Friday, April 25, 2025

Fluent Migrator vs DBUp

DBUp vs FluentMigrator for .NET + PostgreSQL

Why We Landed on FluentMigrator

Database migrations are one of those invisible chores that can make—or break—release day. We recently put DBUp and FluentMigrator head-to-head for a C# / PostgreSQL project. Spoiler: FluentMigrator won. Here’s the quick-and-dirty comparison, a peek at sample code, and why we think the fluent approach pays dividends for most .NET teams.


TL;DR

DBUpFluentMigrator
Migration languageRaw SQL scriptsC# fluent API
Dev-friendlinessGreat if you dream in SQLGreat if you live in C#
PostgreSQL loveWorks via extra providerNative dialect + extensions
Down migrationsManual SQL (often skipped)Built into each class
Community~1.5 k ★ GitHub3 k+ ★ GitHub, more plugs & docs
Unit-testableNeeds a DB spin-upTest Up()/Down() like any C# method
CI/CD setupSimpleSimple + first-party PM Console cmds
Best fitSQL-centric teams.NET-centric teams, multi-DB futures

The Contenders, in 60 Seconds

DBUp

  • Executes ordered SQL scripts.

  • Tracks what ran via a Versions table.

  • Philosophy: keep everything SQL-first and source-controlled.

FluentMigrator

  • Write migrations as C# classes with a fluent DSL.

  • Database-agnostic runner (Postgres, SQL Server, MySQL, …).

  • Extras: tags for env-specific scripts, rollback helpers, index-type tweaks, third-party tooling.


Head-to-Head

1. Migration Syntax

DBUp (SQL):

sql
-- 202504211029_CreateClientsTable.sql CREATE TABLE Clients ( Id SERIAL PRIMARY KEY, Name VARCHAR(255) NOT NULL );

FluentMigrator (C#):

csharp
[Migration(202504211029)] public class CreateClientsTable : Migration { public override void Up() => Create.Table("Clients") .WithColumn("Id").AsInt32().PrimaryKey().Identity() .WithColumn("Name").AsString().NotNullable(); public override void Down() => Delete.Table("Clients"); }

Why we prefer the C# route → zero context-switching, IntelliSense, unit tests, and the same review process as the rest of the codebase.


2. PostgreSQL Superpowers

  • DBUp: You get full SQL control—but that means you hand-roll every Postgres-specific tweak.

  • FluentMigrator: Pick GIN vs. GiST indexes, conditional expressions, JSON operators, etc., straight from helpers—no string-concatenated SQL required.


3. Down Migrations & Rollbacks

Downs are optional, but if you need them:

  • DBUp → write separate SQL scripts and keep them in sync.

  • FluentMigrator → just implement Down() next to Up(). Same compile-time safety, same PR.


4. Dev Experience & Learning Curve

If you’re a…You’ll like…
DBA / hardcore SQL fanDBUp (pure SQL, no abstraction)
Full-stack C# devFluentMigrator (stays in the language you already type 90 % of the day)

5. Community & Ecosystem

  • DBUp – battle-tested, but fewer modern extensions, ~1.5k GitHub ⭐.

  • FluentMigrator – bigger crowd, more blog posts, Google group support, NuGet extras, >3k ⭐.

More eyeballs = more bug fixes, Stack Overflow answers, and plug-ins when you need them.


Real-World Example: FluentMigrator in CI

yaml
# buildspec.yml – CodeBuild snippet version: 0.2 phases: install: commands: - dotnet tool install -g FluentMigrator.DotNet.Cli build: commands: - dotnet restore - dotnet build Database.Migrations.csproj - export PG_CONN=$(aws secretsmanager get-secret-value --secret-id prod-pg --query SecretString --output text) - dotnet fm migrate -p Database.Migrations.dll --connection "$PG_CONN"

One step in the pipeline keeps schema and app deploys decoupled—perfect for blue-green or canary releases.


When DBUp Still Makes Sense

  • Your team lives and breathes raw SQL.

  • You need micro-optimised hand-crafted scripts and don’t mind maintaining them.

  • The project targets a single DB engine with minimal future change.

If that checklist fits, DBUp is lean, predictable, and plenty solid.


Verdict

We chose FluentMigrator because:

  • Our developers think in C#, not DDL.

  • Native Postgres helpers reduce hand-written SQL.

  • Bigger community and ecosystem.

  • Rollbacks, tags, multi-DB, and CI tooling come for free.

Bottom line: if your shop is C#-heavy and you want migrations to feel like just more code, FluentMigrator is the smoother ride. Give it a whirl on your next feature branch and see if you miss SQL files—odds are, you won’t.

The Case Against a Mono-Deployment Model for Software Projects

Executive Summary

Consider a development team that maintains a large monolithic repository with several projects. One service, Jobs.Api, functions almost entirely autonomously except for a single stored-procedure call to the MainWeb database. Adopting a mono-deployment model—where every project ships together—introduces elevated failure risk, lengthier release cycles, and scaling inefficiencies. A modular deployment strategy, ideally by separating Jobs.Api into its own repository, improves reliability, speed, and clarity of ownership while leveraging AWS CodeDeploy’s strengths.


Introduction

In this example scenario, a single repository contains console utilities, web applications, and an EF-Core migration project under the MainWeb banner. Jobs.Api relies on the rest of the codebase only through one stored procedure, yet deploying the entire codebase together can allow unrelated changes to disrupt otherwise stable services. The following sections outline the drawbacks of mono-deployment and present modular alternatives suited to a small, distributed team using AWS CodeDeploy.


Problems with the Mono-Deployment Model

IssueExplanation
Higher failure riskBundling unrelated changes allows a harmless update (e.g., a console tool) to break Jobs.Api. Martin Fowler refers to this as “accidental coupling,” which expands the blast radius of any defect.
Slower release cyclesEven trivial fixes force full builds and tests across every project, delaying delivery for an otherwise independent service.
Inefficient scalingDistinct projects have different resource profiles; one-size-fits-all infrastructure wastes capacity and money.
Coordination frictionIn a monorepo without formal code reviews, ownership boundaries blur, leading to merge conflicts and late-night emergencies across time zones.

Benefits of Modular Deployment

  • Isolated releases – Updating Jobs.Api runs only its pipeline, making rollbacks surgical.

  • Faster CI/CD – Smaller codebase equals shorter test and build times.

  • Right-sized infrastructure – Each service scales independently via AWS CodeDeploy deployment groups.

  • Clear ownership – Separate repositories or bounded contexts make responsibility unmistakable, reducing errors in teams without mandatory reviews.


Industry Guidance

  • Monorepo vs. Polyrepo – ThoughtWorks, GitHub, and Martin Fowler note that tightly coupled monorepos can slow delivery, whereas polyrepos simplify independent deployments.

  • Selective builds – Tools such as Nx can restrict builds to the code that changed, but extracting an autonomous service is simpler and less brittle.

  • AWS CodeDeploy – Deployment groups, blue/green, and canary strategies are geared toward modular rollouts—not all-at-once releases.


Practical Recommendations

  1. Move Jobs.Api to its own repository.

    • Independent CI/CD pipeline using AWS CodeBuild ➜ S3 ➜ CodeDeploy.

    • Blue/green deployments limit downtime and risk.

  2. Maintain a separate pipeline for MainWeb.

    • Database migrations run first; web apps follow.

  3. Sequence database changes.

    • CodePipeline stages enforce “migrations → API” order so stored-procedure compatibility is preserved.

  4. Fallback: selective deployment inside a monorepo.

    • Configure Nx (or a comparable tool) to build only the changed project and provide a project-specific AppSpec file for CodeDeploy.


Addressing Typical Objections

ConcernResponse
“Multiple repos increase overhead.”Modern tooling (GitHub Actions, Dependabot, branch protection) automates much of the extra work.
“Cross-repo database dependency is fragile.”Pipeline stages can enforce migration order; stored procedures can be versioned.
“The team is small; one repo feels simpler.”Independent deployments actually reduce firefighting effort—crucial when headcount and review bandwidth are limited.

Conclusion

Mono-deployment amplifies risk, slows iteration, and impedes efficient scaling. Splitting Jobs.Api into its own repository—or, at minimum, deploying it independently—follows proven best practices, leverages AWS CodeDeploy’s capabilities, and enables faster, safer delivery with unmistakable ownership, benefiting any small, distributed team.


Key References

  • Martin Fowler – Monorepo vs. Polyrepo

  • GitHub Docs – Repository Best Practices

  • AWS CodeDeploy – Deployment Groups

  • CircleCI – Monorepo Benefits & Challenges

  • Semaphore – Smart Build Systems for Monorepos

  • ThoughtWorks Technology Radar – Modular vs. Monolithic Architecture

  • StackOverflow – Deploying Subdirectories with AWS CodeDeploy

  • Nx .NET – Monorepo Management for .NET

Proposal for Mapping Work Items to JIRA requests


Introduction

This document outlines the proposed guidelines for our team to effectively map work items to JIRA requests. It includes detailed instructions on tracking individual tasks using hours or story points, integrating Agile best practices into our workflow, and standardizing the use of JIRA ticket numbers in all commits and pull requests (PRs). By adhering to these guidelines, we aim to enhance collaboration, transparency, and efficiency within the team.

 

Team Guidelines for Mapping Work Items to JIRA Requests

Introduction

This document outlines the proposed guidelines for our team to effectively map work items to JIRA requests. It includes detailed instructions on tracking individual tasks using hours or story points, integrating Agile best practices into our workflow, and standardizing the use of JIRA ticket numbers in all commits and pull requests (PRs). By adhering to these guidelines, we aim to enhance collaboration, transparency, and efficiency within the team.



1. Purpose and Objectives

Purpose

  • Standardization: Establish a consistent method for mapping work items to JIRA requests.

  • Tracking: Improve the tracking of individual tasks using hours or story points.

  • Best Practices: Integrate Agile best practices into our workflow.

  • Traceability: Ensure all code changes are traceable back to their corresponding JIRA tickets.

Objectives

  • Enhance team collaboration and communication.

  • Increase transparency in task progress and workload distribution.

  • Streamline the development process through standardized practices.


2. Work Item Mapping to JIRA Requests

2.1 Work Item Types

Understanding the different work item types in JIRA is crucial for accurate mapping and tracking. This section provides detailed guidelines on when to use an Epic, Story, Task, or Bug.

2.1.1 When to Use an Epic

  • Definition: An Epic is a large body of work that encompasses multiple Stories or Tasks.

  • When to Use:

    • Large Features or Projects: When implementing a significant feature that cannot be completed in a single sprint.

    • Cross-Team Initiatives: When work spans multiple teams or departments.

    • Long-Term Goals: For objectives that require a roadmap over several months.

  • Examples:

    • Developing a new payment processing system.

    • Overhauling the user authentication mechanism.

    • Implementing multi-language support across the platform.

2.1.2 When to Use a Story

  • Definition: A Story represents a user-focused requirement that delivers value.

  • When to Use:

    • User-Centric Features: When adding functionality that directly affects the user experience.

    • Breakdown of Epics: When decomposing an Epic into manageable, incremental pieces.

    • Specific Use Cases: When addressing a particular user need or scenario.

  • Format: Written as "As a [user role], I want [feature], so that [benefit]."

  • Examples:

    • As a shopper, I want to save items to a wishlist so that I can purchase them later.

    • As an admin, I want to generate user activity reports so that I can monitor platform usage.

2.1.3 When to Use a Task

  • Definition: A Task is a specific piece of work that may or may not be tied to a Story.

  • When to Use:

    • Technical Work: When performing technical tasks that support Stories or Epics but do not directly deliver user value.

    • Infrastructure Changes: Updates to systems, environments, or deployment processes.

    • Maintenance Activities: Code refactoring, updating documentation, or improving performance.

  • Examples:

    • Set up a new development environment.

    • Refactor codebase to improve maintainability.

    • Update API documentation for third-party developers.

2.1.4 When to Use a Bug

  • Definition: A Bug is a defect or issue in the product where expected functionality is not met.

  • When to Use:

    • Defects: When functionality is broken or not working as intended.

    • Regression Issues: When new code changes have broken existing features.

    • User-Reported Issues: When users report problems or unexpected behavior.

  • Examples:

    • Application crashes when submitting a form.

    • Incorrect calculation of totals in the shopping cart.

    • UI elements not displaying correctly on mobile devices.

2.2 Creating JIRA Tickets

Steps to Create a Ticket

  1. Select Project: Ensure you are in the correct project space.

  2. Choose Issue Type: Epic, Story, Task, or Bug based on the guidelines above.

  3. Fill in Summary: Provide a concise and descriptive title.

  4. Detail Description: Include all relevant details, acceptance criteria, and context.

  5. Assign: Allocate to the appropriate team member or leave unassigned for team lead assignment.

  6. Set Priority: Indicate urgency (e.g., High, Medium, Low, Critical).

  7. Add Labels: For easier filtering and organization (e.g., "frontend", "backend", "urgent").

  8. Attach Files: Include any supporting documents, screenshots, or error logs.

2.3 Necessary Information for Tickets

  • Acceptance Criteria: Clear conditions that must be met for the ticket to be considered complete.

  • Dependencies: Other tickets that are related or blocking this work item.

  • Estimates: Time or story points required to complete the work.

  • Environment: Specify if the issue is environment-specific (e.g., "only on staging server").

  • Reproduction Steps (for Bugs): Detailed steps to replicate the issue.

2.4 Linking and Managing Tickets

Linking Issues

  • Blocks/Is Blocked By: Indicates a dependency where one ticket cannot proceed until another is completed.

  • Relates To: Shows a non-critical association with other tickets.

  • Duplicates: Identifies redundant tickets to avoid duplicated efforts.

Sub-Tasks

  • Usage: Break down complex Stories or Tasks into smaller, actionable items.

  • Management: Sub-tasks should be completed before the parent ticket is considered done.

  • Assignment: Can be assigned to different team members to parallelize work.

Updating Status

  • Responsibility: The assigned team member must update the ticket status promptly.

  • Workflow States:

    • To Do: Work has not yet started.

    • In Progress: Work is currently being done.

    • Code Review: Work is completed and awaiting peer review.

    • Testing: Undergoing testing and validation.

    • Done: Work is completed and meets acceptance criteria.


3. Estimating Tasks with Hours and Story Points

3.1 Understanding Story Points

  • Definition: A relative unit of measure for the effort required to implement a Story or Task, considering complexity, risk, and uncertainty.

  • Purpose: Facilitates estimation and planning without the precision (and potential inaccuracies) of hour-based estimates.

3.2 Estimation Techniques

Planning Poker

  • Process:

    • Each team member reviews the Story or Task.

    • Independently select a card representing their estimate.

    • Reveal estimates simultaneously.

    • Discuss discrepancies and reach a consensus.

T-Shirt Sizing

  • Categories: XS, S, M, L, XL, representing increasing levels of effort.

  • Usage: For high-level estimation when detailed information is not yet available.

Bucket System

  • Process:

    • Create "buckets" labeled with different story point values.

    • Place each Story or Task into a bucket based on relative size and complexity.

3.3 Best Practices for Estimation

  • Consistency: Use the same scale and reference tasks across the team to maintain consistent estimates.

  • Collaboration: Involve the entire development team in estimation sessions to leverage diverse perspectives.

  • Re-estimation: Reassess estimates if the scope changes or new information emerges that affects the original estimate.

  • Avoid Overcommitment: Be realistic about the team's capacity to prevent burnout and ensure sustainable pace.


4. Agile Best Practices

4.1 Sprint Planning

  • Objective: Define what will be delivered in the next sprint and how the team will achieve it.

  • Activities:

    • Review the prioritized backlog items.

    • Discuss and clarify each item's details and acceptance criteria.

    • Assign estimates to new or updated items.

    • Select and commit to the backlog items that fit within the team's capacity.

    • Define the sprint goal to align the team on the primary objective.

4.2 Daily Stand-ups

  • Duration: Time-boxed to 15 minutes.

  • Agenda:

    • What did you accomplish yesterday?

    • What will you do today?

    • Are there any blockers or impediments?

  • Purpose: Promote transparency, synchronize team efforts, and identify obstacles early.

4.3 Sprint Reviews and Retrospectives

Sprint Review

  • Purpose: Demonstrate completed work to stakeholders and gather feedback.

  • Activities:

    • Present the work completed during the sprint.

    • Collect feedback and discuss any changes in requirements or priorities.

    • Update the product backlog based on insights gained.

Sprint Retrospective

  • Purpose: Reflect on the sprint to identify successes and areas for improvement.

  • Activities:

    • Discuss what went well during the sprint.

    • Identify challenges and their root causes.

    • Develop actionable plans to enhance processes and team dynamics.

4.4 Backlog Grooming

  • Frequency: Regularly, typically once per sprint.

  • Activities:

    • Review and prioritize backlog items.

    • Refine or split large Stories or Epics into smaller, actionable items.

    • Ensure backlog items are well-defined and ready for upcoming sprints.

  • Participants: Product Owner, Scrum Master, and development team members.


5. Using JIRA Ticket Numbers in Commits and PRs

5.1 Commit Message Guidelines

  • Format: [JIRA-ISSUE] Brief description of the change

  • Best Practices:

    • Begin with the JIRA ticket number in brackets.

    • Use imperative mood (e.g., "Add", "Fix", "Update").

    • Keep the message concise but informative.

  • Example:

    [PROJ-123] Add validation for user input in registration form

5.2 Pull Request Guidelines

  • Title: Include the JIRA ticket number and a concise description.

    • Example: PROJ-123: Implement user registration validation

  • Description:

    • Summary: Provide a high-level overview of the changes.

    • JIRA Link: Include a direct link to the JIRA ticket.

    • Testing Details: Explain how the changes were tested and how reviewers can verify them.

    • Additional Notes: Mention any relevant details, such as dependencies or migration steps.

    • Screenshots: Add images or gifs if the changes affect the UI.

5.3 Examples

Commit Message

[PROJ-456] Fix data serialization issue in the API response

Pull Request Title

PROJ-456: Correct data serialization in API responses

Pull Request Description

**Summary:** This PR fixes the data serialization issue where null values were being returned instead of empty strings. **JIRA Ticket:** [PROJ-456](https://jira.company.com/browse/PROJ-456) **Testing Details:** - Added unit tests to cover serialization cases. - Manually tested API responses in Postman. **Additional Notes:** - No database migrations required.


6. Using JIRA for Reporting and Metrics

6.1 Generating Reports on Task Durations and Remaining Work

JIRA provides robust tools to help teams track progress and generate reports on various metrics. These reports can be used to create weekly or bi-weekly updates on task durations and remaining work.

6.1.1 Built-in Reports

JIRA offers a variety of built-in reports accessible from the project sidebar under the "Reports" section. Some useful reports include:

  • Burndown Chart:

    • Purpose: Displays the actual and estimated amount of work remaining in a sprint.

    • Usage: Helps teams monitor sprint progress and predict if sprint goals will be met.

  • Sprint Report:

    • Purpose: Provides information about the completed and remaining work in a sprint.

    • Usage: Reviews sprint performance and identifies incomplete tasks.

  • Velocity Chart:

    • Purpose: Shows the amount of work completed in each sprint.

    • Usage: Helps predict the team's capacity for future sprints.

  • Control Chart:

    • Purpose: Visualizes the cycle time for issues, from start to completion.

    • Usage: Identifies bottlenecks and improves process efficiency.

6.1.2 Dashboards and Gadgets

Custom dashboards can display real-time metrics using various gadgets.

  • Creating a Dashboard:

    1. Navigate to "Dashboards" > "Manage Dashboards" > "Create new dashboard".

    2. Set the name, description, and sharing preferences.

  • Adding Gadgets:

    1. Click "Add gadget" on the dashboard.

    2. Search and add relevant gadgets:

      • Issue Statistics: Displays statistics based on a specified field.

      • Workload Pie Chart: Shows workload distribution among team members.

      • Created vs. Resolved Chart: Compares the number of issues created versus resolved over time.

      • Time Tracking Report: Summarizes time spent on issues.

6.1.3 Filters and JQL (JIRA Query Language)

  • Creating Filters:

    • Use JIRA's advanced search to create filters based on specific criteria.

    • Save these filters for reuse in reports and gadgets.

  • Using JQL:

    • JQL allows for complex queries to retrieve specific sets of issues.

    • Example:

      project = PROJ AND status != Done AND updated >= -2w ORDER BY updated DESC

      • Retrieves all issues in project "PROJ" not marked as Done and updated in the last two weeks.

6.1.4 Exporting Data

  • Export to Excel or CSV:

    • Filters can be exported for further analysis.

    • Navigate to the filter results and select "Export" > "Excel (All fields)".

  • Third-Party Reporting Tools:

    • Integrate JIRA with tools like Power BI or Tableau for advanced reporting.

    • Use JIRA's REST API to pull data into external systems.

6.2 Tracking Task Durations

  • Time Tracking Fields:

    • Original Estimate: The initial time estimate for completing an issue.

    • Remaining Estimate: The current remaining time estimate.

    • Time Spent: The actual time logged on the issue.

  • Logging Work:

    • Team members should regularly log time spent on tasks.

    • Update the "Remaining Estimate" as work progresses to reflect accurate remaining time.

  • Analyzing Time Spent:

    • Use the "Time Tracking Report" to compare estimated vs. actual time.

    • Identify tasks that are taking longer than expected and investigate reasons.

6.3 Monitoring Remaining Work

  • Backlog Overview:

    • The backlog displays all pending issues, their priorities, and statuses.

    • Utilize backlog views to assess remaining work and plan future sprints.

  • Epic Burndown Report:

    • Visualizes progress towards completing an Epic.

    • Helps understand remaining work at the Epic level and adjust plans accordingly.

  • Version Report:

    • Tracks the projected release date based on work completed and remaining.

    • Useful for monitoring progress towards a specific version or release.

6.4 Creating Weekly/Bi-Weekly Reports

  • Automated Reports:

    • Set up subscriptions for dashboards or filters to receive regular email updates.

    • Navigate to the desired filter or dashboard and select "Subscribe".

  • Custom Reports:

    • Use JIRA's reporting tools to generate tailored reports.

    • Include key metrics such as:

      • Number of tasks completed and pending.

      • Average time taken to complete tasks.

      • Identification of blockers or high-priority issues.

      • Workload distribution among team members.

  • Sharing Reports:

    • Export reports to PDF or Excel for distribution.

    • Present findings in team meetings or stakeholder updates.

6.5 Best Practices for Reporting

  • Data Accuracy:

    • Ensure team members keep JIRA updated with the latest information.

    • Regularly review and close completed issues to maintain data integrity.

  • Consistency:

    • Use standardized filters, dashboards, and reporting periods.

    • Consistent reporting enables trend analysis over time.

  • Actionable Insights:

    • Focus on metrics that provide value and inform decision-making.

    • Avoid overwhelming stakeholders with unnecessary data.

  • Feedback Loop:

    • Encourage feedback on reports to improve their usefulness.

    • Adjust reporting content and frequency based on team and stakeholder needs.


7. Conclusion

By implementing these guidelines, we aim to improve our workflow's efficiency, transparency, and consistency. Standardizing the mapping of work items to JIRA requests and incorporating Agile best practices will enhance our team's productivity and product quality. Clear definitions on when to use Epics, Stories, Tasks, and Bugs ensure that everyone on the team understands how to categorize and manage work effectively. Utilizing JIRA's reporting capabilities allows us to monitor progress, make informed decisions, and keep stakeholders informed through regular metrics and updates.

References

New Features in .Net 10

🚀 Runtime Enhancements Stack Allocation for Small Arrays The Just-In-Time (JIT) compiler now optimizes memory usage by stack-allocating s...