Code First vs Database First: a decisão não é técnica

Escolher entre code first ou database first é uma das dúvidas mais comuns em discussões sobre arquitetura de software, e a resposta honesta costuma frustrar: depende. Porém, essa resposta não depende da linguagem, do framework ou da sua preferência pessoal, mas sim do contexto em que o sistema vive.

Essa decisão costuma ser mal compreendida porque muita gente a trata como uma escolha técnica. Na prática, ela é uma escolha estratégica, ligada ao valor dos dados, ao tamanho do time e à maturidade do produto.

O que significa code first ou database first

Antes de comparar, vale alinhar os termos. As duas expressões descrevem qual artefato funciona como fonte da verdade do modelo de dados.

No Database First, o banco nasce primeiro, com tabelas, relacionamentos, índices e regras de integridade. Depois, a aplicação reflete essa estrutura, geralmente por meio de engenharia reversa.

No Code First, o fluxo se inverte. O modelo nasce no código, como entidades e regras de negócio, e o banco passa a ser uma consequência desse modelo.

A documentação oficial do Entity Framework Core resume bem essa ideia. Segundo a Microsoft, você precisa decidir se o modelo do EF Core ou o schema do banco ocupa o papel de fonte da verdade.

Quando o modelo manda, você usa migrations para aplicar mudanças incrementais ao banco. Quando o banco manda, você usa engenharia reversa (scaffolding) para gerar o DbContext e as classes de entidade a partir do schema.

Portanto, a pergunta sobre code first ou database first é, no fundo, uma pergunta sobre quem manda no modelo.

Code first ou database first: o erro começa na pergunta

A própria pergunta "o que é melhor?" já nasce torta. Afinal, as duas abordagens não competem entre si como soluções rivais para o mesmo problema.

Elas atendem contextos diferentes. Por isso, tratar a escolha como disputa leva a debates longos e pouco produtivos.

Pense na comparação entre um carro esportivo e um veículo off-road: qual deles é melhor?

A resposta sempre depende do terreno.

Com code first ou database first acontece a mesma coisa. O terreno, nesse caso, envolve o estágio do projeto, o número de pessoas no time e o quanto os dados importam para o negócio.

Software não é código, é produto

Antes de entrar nos detalhes técnicos, precisamos alinhar um conceito mais fundamental. Software é um produto.

Produto não significa, necessariamente, algo comercial. Um sistema interno, um projeto pessoal ou até uma lista de tarefas feita para uso próprio também entram nessa definição.

Ainda assim, todos continuam sendo produtos, porque representam o resultado de um trabalho e carregam valor. E, quando falamos de valor em software, existe uma hierarquia que muda a forma de tomar decisões:

  • Código pode ser refeito.
  • Dados podem se perder para sempre.
  • Marca leva anos para ser construída.

Essa diferença é essencial. Enquanto o código pode ser reconstruído a partir do conhecimento do time, os dados guardam histórico, contexto e memória do sistema.

Por isso, decisões que envolvem o banco de dados merecem muito mais cuidado do que normalmente recebem. Assim, escolher code first ou database first sem pensar nos dados significa ignorar o ativo mais difícil de recuperar.

Perguntas que definem code first ou database first

A escolha só faz sentido quando você analisa o cenário do projeto. Então, antes de decidir qualquer coisa, responda algumas perguntas com sinceridade.

  • Qual é o objetivo principal do sistema?
  • Em que estágio ele está: ideia, protótipo ou produção?
  • Quantas pessoas trabalham nele?
  • Existe alguém responsável pelos dados, como um DBA?
  • O banco atende a vários sistemas ou fica isolado em uma única aplicação?

Essas perguntas parecem simples, mas elas direcionam a decisão de verdade. Além disso, cada resposta muda o peso de fatores como velocidade de entrega, previsibilidade e controle.

Ignorar esse contexto leva a escolhas que funcionam no curto prazo. Contudo, no longo prazo, essas mesmas escolhas viram problemas difíceis de resolver, como schemas inconsistentes e migrations em conflito.

Em outras palavras, a decisão entre code first ou database first começa em uma conversa sobre o produto, e não em uma discussão sobre ferramenta.

Quando o banco vem primeiro

No Database First, a estrutura de dados nasce antes da aplicação. O time desenha tabelas, relacionamentos, índices e regras de integridade, e o código vem depois para refletir essa estrutura.

Esse modelo faz muito sentido onde os dados são críticos e precisam de controle rigoroso. Sistemas corporativos com várias equipes acessando o mesmo banco costumam se beneficiar dele.

Da mesma forma, cenários com um DBA responsável ganham previsibilidade e segurança. Toda alteração passa por alguém que conhece o impacto no banco como um todo.

Por outro lado, quem compara code first ou database first percebe que essa rigidez pode atrapalhar quando o sistema precisa evoluir rapidamente. Mudanças passam a depender de processos formais, refatorações ficam mais complexas e o código tende a se acoplar ao schema.

Como funciona o scaffolding no EF Core

No ecossistema .NET, o EF Core oferece o comando de engenharia reversa para esse fluxo. Ele lê o schema do banco, incluindo tabelas, colunas, restrições e índices, e gera o código correspondente.

# Gera o DbContext e as entidades a partir de um banco existente
dotnet ef dbcontext scaffold "Server=localhost;Database=Store;Trusted_Connection=True;" \
    Microsoft.EntityFrameworkCore.SqlServer \
    --output-dir Models \
    --context StoreContext

Segundo a documentação, tabelas viram tipos de entidade, colunas viram propriedades e chaves estrangeiras viram relacionamentos. Além disso, o EF Core gera o DbContext e as entidades como classes partial.

Esse detalhe importa bastante. Quando o banco muda, o fluxo típico consiste em rodar o scaffolding de novo, e o comando sobrescreve o código gerado anteriormente quando você usa --force.

Por isso, a recomendação oficial é não editar os arquivos gerados. Em vez disso, você estende as classes em arquivos separados e usa o método parcial OnModelCreatingPartial para ajustar a configuração.

// Arquivo separado: não é sobrescrito pelo scaffolding
public partial class StoreContext
{
    partial void OnModelCreatingPartial(ModelBuilder modelBuilder)
    {
        // Ajustes que sobrevivem a um novo scaffolding
        modelBuilder.Entity<Product>()
            .Property(p => p.Name)
            .HasMaxLength(120);
    }
}

A partir do EF7, também é possível customizar os templates T4 usados na geração. Com isso, o time adapta o código gerado ao padrão do projeto sem perder a capacidade de regenerar tudo.

Quando o código vem primeiro

No Code First, o modelo nasce no código. Entidades e regras de negócio vêm primeiro, e o banco surge como consequência, gerado por meio de migrations.

public class Order
{
    public Guid Id { get; set; }
    public string CustomerName { get; set; } = string.Empty;
    public decimal TotalAmount { get; set; }
    public DateTime CreatedAt { get; set; }
}

Entre code first ou database first, essa abordagem brilha quando o sistema ainda está evoluindo. Projetos novos, MVPs e aplicações com times pequenos ganham muito com essa flexibilidade.

Na prática, o desenvolvimento acelera, as mudanças fluem com naturalidade e o domínio evolui junto com o código. A própria Microsoft destaca que o code first tende a acelerar o desenvolvimento, porque as ferramentas do EF Core cuidam da maior parte da criação e da manutenção do banco.

# Cria uma migration a partir das mudanças no modelo
dotnet ef migrations add AddOrders

# Aplica as migrations pendentes no banco local
dotnet ef database update

O problema começa quando essa liberdade não vem acompanhada de disciplina. Em times maiores ou em ambientes compartilhados, migrations mal controladas geram conflitos, inconsistências e até perda de dados.

Se você quiser ver esse fluxo completo em um projeto real, o artigo sobre migrations com EF Core e PostgreSQL mostra os comandos na prática.

Como aplicar migrations em produção

A disciplina aparece principalmente no momento de levar as mudanças para produção. Rodar dotnet ef database update direto no servidor funciona bem em desenvolvimento, mas não é o ideal para bancos de produção.

Primeiro, porque a ferramenta aplica o SQL sem dar chance de revisão. Além disso, ela exige o SDK do .NET e o código-fonte no ambiente de execução.

A documentação recomenda duas estratégias principais. Para implantação automatizada, use um migration bundle; para fluxos com revisão de um DBA, gere um script SQL.

# Script idempotente: aplica apenas as migrations que faltam
dotnet ef migrations script --idempotent --output artifacts/migrations.sql

# Bundle executável que aplica as migrations sem o SDK
dotnet ef migrations bundle

O script idempotente consulta a tabela de histórico de migrations e aplica somente o que falta. Dessa forma, ele atende bancos que estão em estados diferentes.

Outra boa prática envolve permissões. A identidade usada no deploy pode alterar o schema, enquanto a identidade da aplicação deve ter apenas as permissões de leitura e escrita de que precisa.

Repare como essa recomendação aproxima os dois mundos. Mesmo em um projeto code first, o script SQL revisado por alguém responsável pelos dados traz para o processo a governança típica do database first.

Code first ou database first com vários sistemas

Existe um cenário específico em que a decisão deixa de ser opcional. Quando vários sistemas compartilham o mesmo banco de dados, o Code First se torna um risco real.

Imagine diversas aplicações tentando evoluir a mesma estrutura de forma independente. O resultado é previsível: conflitos, migrations fora de ordem e perda de controle.

Isso acontece porque cada aplicação mantém seu próprio snapshot do modelo. Assim, nenhuma delas enxerga as mudanças que as outras fazem no schema.

Nesse cenário, o Database First deixa de ser uma escolha e passa a ser uma necessidade. Alguém precisa ser dono da estrutura, e esse dono não pode ser uma das aplicações isoladamente.

Agora, quando cada sistema possui seu próprio banco, a situação muda completamente. Cada aplicação responde pelo seu próprio contexto, e o Code First volta a fazer sentido, já que não existe mais concorrência sobre a estrutura.

Portanto, entre code first ou database first, a topologia do banco pesa mais do que o gosto do time. Banco compartilhado pede governança central; banco isolado permite autonomia.

Domain-driven vs data-driven

Essa discussão também se conecta com a forma de pensar o sistema. Em uma abordagem data-driven, o banco ocupa o centro, e o código reflete a estrutura de dados.

Já em uma abordagem domain-driven, o foco está nas regras de negócio. O sistema nasce a partir do comportamento, e o banco surge como consequência.

Essas duas formas de pensar se alinham com Database First e Code First, respectivamente, e explicam por que o debate sobre code first ou database first vai além da ferramenta. Se quiser aprofundar essa diferença, o artigo data-driven vs domain-driven explora o tema em detalhes.

Mas, na prática, sistemas maduros costumam misturar os dois mundos. O domínio guia o código, enquanto a governança protege os dados.

Projetos que seguem uma arquitetura em camadas mostram bem essa mistura. No artigo sobre EF Core em Clean Architecture, por exemplo, o domínio fica isolado e a persistência se adapta a ele.

O problema que ninguém quer assumir

Existe um ponto ignorado em praticamente todos os projetos: responsabilidade. Afinal, quem responde pelo banco de dados?

Quando essa resposta não fica clara, o problema começa. Sem governança, qualquer desenvolvedor pode alterar a estrutura, aplicar migrations ou executar mudanças sem entender o impacto completo.

Sem um dono, a discussão sobre code first ou database first perde o sentido.

Isso até funciona em projetos pequenos. Mas, em sistemas maiores, é questão de tempo até algo quebrar.

Definir responsabilidade, controle de acesso e políticas de alteração não é burocracia. É proteção.

Na prática, governança pode ser algo simples. Um processo de revisão de migrations no pull request, um script gerado no CI e uma pessoa que aprova a mudança já resolvem boa parte dos riscos.

O que está em risco entre code first ou database first

Uma pergunta simples ajuda a colocar tudo em perspectiva: o que é pior perder?

O código pode ser refeito. Os dados, não.

Essa diferença muda a forma de tomar decisões. Backup deixa de ser opcional, controle de acesso deixa de ser detalhe e governança deixa de ser exagero.

A documentação do EF Core reforça esse cuidado. Ela alerta que uma migration pode remover uma coluna quando a intenção era renomeá-la, então você deve sempre inspecionar e testar as migrations antes de aplicá-las em produção.

Da mesma forma, scripts de rollback executam as operações Down, mas não conseguem reconstruir dados que a migration original apagou. Por isso, nenhum debate sobre code first ou database first substitui uma boa estratégia de backup.

Code first ou database first no mesmo projeto

A conclusão mais madura não passa por escolher um lado. Ela passa por entender quando usar cada abordagem.

Sistemas reais, em produção, raramente seguem apenas uma delas. É comum ver Code First no desenvolvimento e Database First na governança.

Um fluxo bastante frequente funciona assim: o time modela as entidades no código e gera as migrations. Em seguida, o pipeline produz um script SQL idempotente, e um responsável pelos dados revisa e aplica esse script.

Outro caminho comum começa com um banco legado. Nesse caso, o time faz o scaffolding uma única vez e, a partir daí, passa a usar migrations, tratando o código gerado como ponto de partida.

A documentação do EF Core descreve exatamente essa transição como uma das opções após o scaffolding. Ou seja, a fronteira entre code first ou database first é muito mais flexível do que os debates sugerem.

No fim das contas, a decisão não gira em torno da ferramenta. Ela gira em torno de contexto, maturidade e responsabilidade.

Conclusão

A escolha entre code first ou database first não deveria começar com uma preferência, mas com uma análise. O cenário importa, a equipe importa e, acima de tudo, os dados importam.

Use Code First quando o produto ainda evolui rápido, o time é pequeno e cada aplicação tem seu próprio banco. Prefira Database First quando vários sistemas compartilham a mesma estrutura ou quando existe um responsável formal pelos dados.

Em qualquer caso, trate migrations com disciplina, revise scripts antes de aplicá-los e mantenha backups confiáveis. Assim, a abordagem escolhida trabalha a seu favor.

A forma como você enxerga o software, como código ou como produto, muda completamente a decisão. Porque arquitetura não fala só de tecnologia; arquitetura também é cultura.

FAQ

Qual é a diferença entre code first e database first?

No Code First, o modelo no código define o banco por meio de migrations. No Database First, o banco existente define o modelo, que você gera por engenharia reversa.

O EF Core suporta database first?

Sim, por meio do comando dotnet ef dbcontext scaffold, que gera o DbContext e as entidades a partir do schema. O recurso também aparece como reverse engineering na documentação.

Posso trocar de database first para code first depois?

Sim. A documentação do EF Core sugere fazer o scaffolding uma vez e passar a usar migrations, tratando o código gerado como ponto de partida.

Code first funciona com banco compartilhado entre sistemas?

Funciona mal, porque cada aplicação tenta evoluir o schema de forma independente. Nesse cenário, a governança central do Database First costuma ser a opção mais segura.

Devo aplicar migrations na inicialização da aplicação?

A Microsoft recomenda cautela com essa prática em produção. Prefira migration bundles para automação ou scripts SQL quando houver revisão e aprovação.

Glossário

Termo Significado
Code First Abordagem em que o modelo no código é a fonte da verdade e gera o banco por migrations
Database First Abordagem em que o banco existente é a fonte da verdade e o código é gerado a partir dele
Migration Arquivo que descreve uma mudança incremental no schema do banco
Scaffolding Engenharia reversa que gera o DbContext e as entidades a partir de um banco existente
Script idempotente Script SQL que verifica o histórico e aplica apenas as migrations que faltam
Migration bundle Executável que aplica migrations sem exigir o SDK do .NET nem o código-fonte
DBA Profissional responsável pela administração e governança do banco de dados
Governança de dados Conjunto de regras, papéis e processos que controlam alterações e acesso aos dados

Fontes