I’ve been there. You join a project, and the first big argument is about the database. One camp wants to stick with the existing, massive SQL Server database and generate code from it (Database-First). The other wants to write C# classes and have Entity Framework Core create the database for them (Code-First).

Picking the wrong one isn’t just a technical mistake; it’s a commitment that can cost your team months of painful refactoring. This happened to me on an e-commerce platform where a premature switch to Code-First crippled our ability to integrate with the company’s legacy inventory system.

Let’s cut through the theory and talk about which approach to choose and when, based on real-world projects.

What’s the Real Difference?

It’s all about the source of truth.

  • Database-First: The database schema is the source of truth. You start with an existing database and use a tool to generate your EF Core models (DbContext and entity classes). You never touch the generated model files directly.
  • Code-First: Your C# classes are the source of truth. You write your domain models, and EF Core migrations generate and update the database schema to match your code.

Here’s how they stack up in practice.

AspectDatabase-FirstCode-First
Starting PointAn existing, live database schema.A set of C# classes.
Schema ControlUsually a DBA team using SQL scripts.Developers, through EF Core migrations.
Typical Workflow1. DBA changes the DB. <br> 2. You re-run a command to update your code.1. You change a C# class. <br> 2. You run Add-Migration.
Core ToolScaffold-DbContextAdd-Migration, Update-Database
Best FitBrownfield projects, legacy systems, strict DBA governance.Greenfield apps, agile teams, prototypes.
Biggest RiskYour code gets out of sync with the database, causing runtime errors.Developers with little SQL knowledge create a slow, unoptimized database.

Here’s When to Go Database-First

Stick with Database-First when the database already exists and you don’t have full control over it. Simple as that.

I’d use it if:

  • You’re integrating with a huge, pre-existing database (like an ERP or a decade-old system).
  • A dedicated DBA team manages the schema and they don’t want your app making changes.
  • Multiple applications share the same database, and your app is just one consumer.
  • The database is full of critical stored procedures, views, and functions you have to use.

One of the most common scenarios is connecting to a vendor’s database. You can’t change their schema, so your only option is to map your code to what they give you.

Here’s the command you’ll get to know very well. It reads the database schema and spits out the C# models.

# This command connects to a SQL Server DB and creates models in the 'Models' folder
dotnet ef dbcontext scaffold "Server=prod-sql;Database=ERP;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -o Models --force

That --force flag is your best friend and worst enemy. It overwrites everything, so any manual changes you made to the model classes are gone. This is by design: the database is the source of truth, not your code.

And When to Pick Code-First

Go with Code-First for new projects where developers own the entire stack. You get to define your data structures in C# and let the tooling handle the SQL.

This is my default choice for:

  • New web APIs, microservices, or SaaS applications.
  • Projects where the data model will change a lot, especially early on.
  • Teams where developers are comfortable with basic database design.
  • When you want your database schema changes to be part of your pull requests and code reviews.

The beauty of Code-First is that your schema is version-controlled right alongside your application code. A new feature might involve a code change and a migration in the same PR.

You define your entities and their configuration in code, giving you fine-grained control.

// Example of configuring an entity using Fluent API
public class ProductConfiguration : IEntityTypeConfiguration<Product>
{
    public void Configure(EntityTypeBuilder<Product> builder)
    {
        builder.HasKey(p => p.Id);
        
        // This is a classic performance trick: create a non-clustered index.
        builder.HasIndex(p => p.Sku).IsUnique();

        builder.Property(p => p.Name).IsRequired().HasMaxLength(200);

        builder.Property(p => p.IsActive).HasDefaultValue(true);
    }
}

Then you create and apply the migration from your terminal.

# Create a migration file based on your code changes
dotnet ef migrations add AddProductSkuIndex

# Apply the migration to the database
dotnet ef database update

The Performance Gotcha

Let’s be honest: Database-First often leads to better performance out of the box. Why? Because a DBA has likely already optimized the indexes, keys, and data types. The schema was designed for performance first.

Code-First performance depends entirely on the developers. I’ve seen a Code-First app grind to a halt because the team forgot to add a simple index to a foreign key column. The N+1 query problem is also way easier to create by accident. This stuff usually gets exposed once a table hits around 100,000 rows. Suddenly, a page that loaded in 50ms takes 5 seconds.

If you go Code-First, make sure someone on your team actually understands SQL query plans and indexing. Don’t just trust the generated SQL.

Common Traps to Avoid

Both approaches have sneaky ways of burning you in production.

Database-First Gotchas:

  • Model Drift: A DBA adds a column, nobody tells you, and your app breaks at runtime because your scaffolded model is stale. This is the #1 issue. You have to have a process for re-scaffolding regularly.
  • Losing Customizations: You add a [NotMapped] attribute or some validation logic to a generated class. The next time you run scaffold, it’s gone. Use partial classes to separate your custom code from the generated code.

Code-First Traps:

  • Messy Migration History: After a year, you can end up with 200 migration files. It can make database deployments slow and new developer onboarding a pain. Learn how to squash migrations.
  • Risky Data Migrations: A migration that just adds a column is safe. A migration that has to update a million rows of existing data is risky. These can lock tables in production and cause outages if you’re not careful. Always test data migrations against a production-sized database copy.

My Final Take: Here’s How I Choose

There’s no “better” approach, only the one that fits your project and team.

I choose Database-First when: I’m a guest in someone else’s house. The database is the authority, and my job is to work with it, not change it. Think enterprise integrations, legacy modernizations, or systems with heavy DBA governance. It’s slower and more rigid, but it’s safe and predictable.

I choose Code-First when: I’m building a new house from the ground up. My team and I own the whole stack, and we need to move fast. It gives developers incredible speed and control, putting schema evolution right into the development workflow. This is the go-to for most modern greenfield applications.

The most expensive mistake is picking one based on preference instead of reality. Analyze your constraints: Who controls the schema? How often will it change? What’s your team’s SQL skill level? The answer to those questions will point you to the right choice.

References

FAQ

Does EF Core use Database-First or Code-First by default?

EF Core supports both, but Code-First is the more common starting point for new projects. Database-First is used when you scaffold models from an existing schema.

When should I choose Database-First in EF Core?

Use Database-First when integrating with an existing or vendor-managed database, especially if DBAs control schema changes or strict change management processes exist.

When should I choose Code-First in EF Core?

Use Code-First for new applications where developers own the schema, migrations are tracked in source control, and rapid iteration is important.

Can I switch from Code-First to Database-First later?

Yes, but switching approaches mid-project can require significant refactoring. A hybrid approach, starting Code-First and switching to Database-First once the schema stabilizes, can work well.

Which approach is better for performance?

Performance depends more on schema design and query optimization than the approach itself. Database-First may have an edge in complex, DBA-optimized databases, while Code-First relies on developers to apply best practices.

About the Author

@CodeCrafter is a software engineer who builds real-world systems , from resilient ASP.NET Core backends to clean, maintainable Angular frontend. With 11+ years in production development, he shares what actually works when shipping software that has to last.