Ir para o conteúdo principal
Parceiros

Habilitando o Desenvolvimento Evolutivo de Banco de Dados: ramificação de banco de dados com Lakebase

Uma série (quase) em três partes

por Pramod Sadalage e Kevin Hartman

Por que esta série existe

A metodologia descrita em Evolutionary Database Design e operacionalizada em Refactoring Databases: Evolutionary Database Design é clara há vinte anos. As sete práticas, o catálogo de mais de 70 refatorações nomeadas, a mecânica de transição – tudo documentado, revisado por pares, ensinado.

Essa metodologia chegou ao CI/CD em 2010 com Continuous Delivery (Capítulo 12: Gerenciando Dados). Migrações se tornaram artefatos de primeira classe no pipeline de implantação. A disciplina de mudanças de banco de dados como código atingiu o movimento CI/CD mais amplo. O que o CD não resolveu foi o isolamento por pipeline: pipelines podiam executar migrações, mas ainda precisavam de um banco de dados de destino, e esse destino era compartilhado. A Prática nº 4 – Cada um tem sua própria instância de banco de dados – permaneceu aspiracional na maioria das equipes, pois bancos de dados reais moldados pela produção para cada desenvolvedor custam tempo, dinheiro e ciclos de DBA. A camada compensatória que surgiu para contornar a lacuna (objetos mock, ambientes de staging compartilhados, substitutos de banco de dados em memória, filas de tickets de DBA) tornou-se metodologia fundamental por padrão, não por design.

Em 2026, o branching de banco de dados copy-on-write chegará ao Databricks Lakebase. Um branch de banco de dados de produção de terabytes, com duração de um segundo e zero armazenamento na criação, agora é uma operação O(1). A restrição que manteve a Prática nº 4 aspiracional foi removida.

Esta série descreve o que muda quando a restrição é removida: não a metodologia – essa se mantém –, mas as práticas que emergem pela primeira vez, a governança em escala de equipe que se torna automática, a evolução do papel do DBA e o novo substrato que os agentes compartilham com seus colegas humanos.

Conheça Jen

Jen é a personagem desenvolvedora de Evolutionary Database Design. Nesse ensaio, ela implementou uma refatoração de banco de dados – dividindo um inventory_code campo em location_code, batch_number e serial_number – como uma user story rotineira, ilustrando que DBAs e desenvolvedores podem colaborar, esquemas podem evoluir em pequenos incrementos e migrações carregam a mudança adiante com segurança.

A série continua com Jen vinte anos depois. A metodologia que ela segue é a mesma que ela seguia em 2003. O que há de novo é o substrato sob seu fluxo de trabalho: branching de banco de dados copy-on-write, que torna as práticas sobre as quais ela tem lido operacionalmente reais em escala de produção. Nas três partes desta série, ela é a mesma Jen em três escopos – seu dia (Parte 1), seu novo playbook (Parte 2) e sua equipe (Parte 3).

Parte 1: A história de Jen: um recurso, uma mudança no banco de dados

Para entender como isso funciona, vamos percorrer a jornada de como uma desenvolvedora chamada Jen implementa uma tarefa que afirma que o usuário deve ser capaz de ver, pesquisar e atualizar a localização, o lote e o número de série de um item em estoque e usá-lo posteriormente no fluxo do aplicativo.

O que se segue descreve as várias etapas que Jen precisa realizar para concluir esta tarefa, enquanto descrevemos as etapas, tentaremos comparar como o fluxo de trabalho de Jen muda ao trabalhar com bancos de dados tradicionais e usando Lakebase, que permite o branching de banco de dados a um custo mínimo.

Jen começa a trabalhar na tarefa de seu recurso

Jen pega o que parece ser um recurso direto. A equipe de produto quer permitir que os usuários capturem localização, lote e número de série de um item durante a adição ao estoque e o usem posteriormente no fluxo do aplicativo. Do lado de fora, a mudança parece pequena: adicionar um campo à tela, salvar o valor, mostrá-lo na tela de Inventário de um item e talvez usá-lo em uma decisão downstream mais tarde.

Para Jen, a mudança no aplicativo é fácil de visualizar. Ela sabe onde o formulário está. Ela sabe qual serviço lida com a solicitação. Ela pode ver o objeto modelo que precisa de mais atributos. Mas no momento em que ela rastreia a mudança completamente, ela vê a dependência real, o banco de dados também tem que mudar.

Algumas novas colunas são necessárias, os dados existentes no ambiente de produção precisam ser preservados e devem ser semanticamente corretos. O aplicativo deve lidar com dados antigos e novos com segurança e ela precisa adicionar testes para provar que os novos campos são armazenados, lidos e exibidos corretamente. O que parecia um recurso simples é agora uma mudança coordenada de aplicativo e banco de dados, com a responsabilidade adicional de garantir que o esquema e os dados de produção existentes sejam migrados para o novo esquema.

Banco de dados compartilhado

Jen cria um branch de código para o trabalho que está prestes a iniciar e, como eles estão usando um banco de dados compartilhado e o resto da equipe está usando o mesmo banco de dados para desenvolvimento, ela imediatamente começa a pensar em todas as mudanças que vai introduzir na camada de banco de dados que podem afetar outros usuários do banco de dados compartilhado e começa a planejar como torná-lo seguro para os outros. Ela poderia executar a mudança do aplicativo localmente e ser capaz de executar seus testes unitários e de integração? Cada opção tem custos. Ela pode esperar. Ela pode pedir à equipe para coordenar. Ela pode configurar seu próprio Postgres local no Docker, popular com dados desatualizados pg_dump de uma semana atrás e esperar que as diferenças não importem. Ela pode recorrer à execução de um banco de dados local em um contêiner ou a um banco de dados em memória H2 ou SQLite que roda rápido, mas usa o dialeto errado, então seus testes passam localmente e exibem falhas desconhecidas no Postgres real. Ela pode até testar seus scripts de migração de esquema e dados? Esse medo de quebrar os outros a atrasa e, ao mesmo tempo, não permite que ela experimente várias opções de construção do recurso.

Fig 1: Mostrando um banco de dados compartilhado com todos os tipos de usuários acessando o banco de dados de desenvolvimento.

Como em um banco de dados compartilhado, um desenvolvedor pode estar testando uma mudança na lógica de negócios, outro está depurando uma migração de dados, alguém criou dados de teste que Jen não entende. Se Jen aplicar sua mudança de esquema ao banco de dados compartilhado, ela pode quebrar o trabalho de outra pessoa. Se outra pessoa mudar o esquema enquanto ela estiver testando, seus resultados podem não ser mais confiáveis. Se ela adicionar dados de teste, isso pode interferir nas suposições de outro desenvolvedor.

Jen pode esperar até que o banco de dados compartilhado esteja livre, o que protege a equipe de colisões, mas transforma um recurso pequeno em um problema de agendamento e perda de produtividade. Ela pode coordenar manualmente com os outros desenvolvedores: “Você está usando o dev agora?” “Posso executar uma migração?” “Por favor, não reinicie os dados na próxima hora.” algo como um bastão em uma corrida de revezamento. Isso funciona por um tempo, mas não escala, especialmente com uma equipe remota ou em fusos horários diferentes.

Jen pensa em outra opção, usando um banco de dados local em memória, ela sabe que essa configuração não corresponde ao estado do banco de dados usado pelo resto da equipe, o que significa que ela não terá confiança em sua solução, pois a mudança pode funcionar localmente e ainda falhar mais tarde quando encontrar os dados e o esquema reais em ambientes superiores, como staging e produção.

O verdadeiro problema que Jen está enfrentando é de feedback mais lento ela pode fazer a mudança, mas descobrir se a mudança funciona, mas feedback rápido e realista e sem esse feedback a mudança no banco de dados se torna algo que a equipe trata com cuidado e acaba escolhendo a primeira solução que funciona e nunca experimenta ou tenta múltiplas soluções, levando assim a soluções subótimas, produtividade reduzida e desenvolvedores insatisfeitos.

Branches de banco de dados individuais

Usando Lakebase, Jen tem a capacidade de criar um branch de um banco de dados para seu uso individual e essa capacidade muda completamente a forma como ela trabalha.

Em vez de esperar que o banco de dados de desenvolvimento compartilhado fique disponível, Jen cria um branch de banco de dados databricks postgres create-branch para sua funcionalidade ou usando uma Extensão VS Code / Cursor. Isso muda a forma do trabalho imediatamente. Ela não está mais pedindo à equipe uma janela de tempo. Ela não está mais negociando com outros desenvolvedores sobre quem pode executar qual migração e quando. Ela não está mais tentando proteger sua alteração semi-concluída das alterações semi-concluídas de todos os outros. Ela tem seu próprio espaço de banco de dados isolado, criado a partir do mesmo tipo de ambiente de banco de dados que o aplicativo eventualmente usará em produção.

Fig 2: Todos na equipe recebem seu próprio banco de dados e podem obter mais de um banco de dados, se necessário.

O branch dá a Jen uma cópia rápida do estado do banco de dados com o qual ela precisa trabalhar. Ela agora tem o mesmo motor Postgres, o mesmo esquema, as mesmas políticas de governança e os mesmos dados com formato de produção que veria se consultasse a produção diretamente. A única diferença: este branch pode ser modificado, descartado ou recriado sem afetar nenhuma outra carga de trabalho. Ela não está testando contra um banco de dados local simplificado que se comporta de maneira diferente da produção. Ela está trabalhando com o mesmo tipo de banco de dados que a equipe usa em produção, com os mesmos tipos de regras de esquema, restrições, índices, dados de referência e histórico de migração que fazem com que as alterações no banco de dados sejam bem-sucedidas ou falhem no mundo real. Esse realismo é importante porque muitos problemas de banco de dados não aparecem em testes unitários isolados. Eles aparecem quando uma nova migração encontra a estrutura existente, os dados existentes, as suposições existentes e o comportamento do aplicativo existente.

Agora Jen pode tratar a alteração do banco de dados como parte do design, não apenas como uma etapa de implantação. Ela pode tentar a versão óbvia primeiro: adicionar as novas colunas, definir uma lógica padrão para dividir a coluna existente, criar um script de migração de banco de dados, atualizar o aplicativo e executar os testes. Então ela pode fazer perguntas melhores. Este script de migração deve funcionar para volumes de dados de produção, a qualidade dos dados em produção é como o script dela espera que sejam? Um script de migração de dados está escondendo informações de negócios ausentes? A preferência deve ser modelada como colunas simples, uma tabela de consulta ou uma tabela separada de item_information porque mais informações provavelmente virão depois? O padrão de consulta precisará de um índice? Este design tornará a geração de relatórios downstream mais fácil ou mais difícil? No fluxo de trabalho antigo, essas perguntas muitas vezes são comprimidas porque alterar o banco de dados é caro.

Fig 3: Fluxo de trabalho de Jen ao trabalhar em tarefas, com a capacidade de criar branches de bancos de dados

No fluxo de trabalho com branch, Jen pode explorá-las enquanto a funcionalidade ainda está sendo moldada. O DBA pode se juntar a ela para orientá-la sobre nuances de produção e volumes de dados, fornecendo assim uma entrada valiosa no design da solução, em vez de ser um revisor posterior.

Fazendo a alteração do aplicativo e do banco de dados juntas

Jen escreve o script de migração. O que quer que sua equipe use – Flyway, Liquibase, Alembic, Knex, Prisma – o script fica no repositório de código, ao lado das alterações do aplicativo. O esquema e a migração de dados viajam com o código.

(Esta é a refatoração Split Column – um dos ~70 padrões catalogados em Refactoring Databases, o livro que operacionalizou as sete práticas.)

Ela aplica a migração ao seu branch usando flyway migrate. A ferramenta é executada em menos de um segundo contra dados com formato real. Ela atualiza o código do seu repositório para ler e escrever as três novas colunas. Ela executa sua suíte de testes. Os testes passam contra Postgres real, sem mocks, sem substitutos em memória.

Se ela quiser uma lousa limpa para tentar uma abordagem diferente, ela descarta o branch e cria um novo a partir da produção. Mais um segundo. Sem tickets de limpeza. Sem DBA envolvido.

Mesma Jen. Mesma refatoração. O que mudou foi a capacidade.

Espaço para falhar mais rápido

A capacidade de experimentar é importante. O design e o desenvolvimento evolutivos não se tratam apenas de se mover rapidamente por uma lista de verificação predefinida. Trata-se também de aprender à medida que o trabalho se torna mais concreto. Jen pode descobrir que o primeiro design de esquema funciona, mas cria uma lógica de aplicativo estranha. Ela pode descobrir que o segundo design é mais limpo, mas complica a migração de registros existentes. Ela pode descobrir que uma pequena decisão de normalização agora facilitaria futuras alterações. O primeiro script de migração que ela escreveu os índices SUBSTRING estão com um caractere a mais. O destrutivo DROP COLUMN foi executado antes que ela pudesse verificar se as novas colunas foram preenchidas corretamente. Como ela tem seu próprio branch, essas descobertas são baratas. Ela pode aplicar uma migração, executar o aplicativo, inspecionar os dados, avançar com outra migração ou redefinir e tentar um caminho diferente.

O branch também muda a postura emocional do trabalho. Jen não precisa ser excessivamente cautelosa porque outra pessoa pode estar dependendo do banco de dados de desenvolvimento compartilhado. Ela não precisa anunciar cada experimento para a equipe. Ela não precisa limpar os dados de teste imediatamente porque outro desenvolvedor pode tropeçar neles. Seu branch é um lugar seguro para pensamentos inacabados. Ele pode conter tabelas temporárias, tentativas de migração com falha, dados de teste estranhos e designs pela metade sem criar ruído para mais ninguém.

Ao mesmo tempo, o isolamento não significa desapego dos padrões da equipe. Jen ainda escreve scripts de migração. Ela ainda mantém o código do aplicativo e a alteração do banco de dados juntas. Ela ainda executa testes. Ela ainda espera que o design final seja revisado. A diferença é que ela pode fazer a parte bagunçada do trabalho em particular e rapidamente antes de pedir à equipe para raciocinar sobre a versão polida. Quando ela abrir um pull request, a conversa poderá se concentrar se o design está correto, não se ela teve um lugar seguro para testá-lo.

Esta é a mudança chave: o branch do banco de dados dá a Jen um feedback rápido, realista e isolado que ela também pode ter revisado por seus líderes técnicos ou DBAs, mostrando seu branch do banco de dados. Rápido significa que ela pode criar o ambiente quando precisar, não quando alguém o provisiona para ela. Realista significa que ela está testando contra o mesmo tipo de comportamento de banco de dados que importa em produção. Isolado significa que seus experimentos não interrompem ninguém mais. Juntas, essas três propriedades transformam a alteração do banco de dados de um gargalo em uma parte normal do desenvolvimento de funcionalidades.

Jen agora pode avançar o aplicativo e o banco de dados juntos. Seu branch de código e seu branch de banco de dados se tornam dois lados da mesma tarefa. Um contém as alterações do aplicativo. O outro fornece a essas alterações um banco de dados real para viver. Em vez de esperar, coordenar ou fingir com uma configuração simplificada, Jen pode projetar, testar, revisar e aprender. A funcionalidade ainda é pequena, mas agora o banco de dados não é mais o que a torna lenta.

Abrindo o pull request

Jen confirma tanto o código do aplicativo quanto o script de migração. Ela abre um PR.

CI faz o que Jen acabou de fazer, mas para a equipe: ele cria seu próprio branch Lakebase temporário, aplica a migração, executa a suíte de testes do aplicativo, executa testes de banco de dados contra o esquema migrado, valida a própria migração (aplica-se de forma limpa, idempotente, reversível) e publica um comentário no PR mostrando exatamente quais objetos de banco de dados foram alterados.

O revisor agora pode ver o que a alteração do esquema faz em linha com o código que a utiliza, mudando sua compreensão contextual de abstrata para concreta.

Captura de tela da visualização Resumo de Diferença de Branch da Extensão Lakebase SCM

Revisando a alteração

No fluxo de trabalho antigo, a pergunta sobre a revisão do banco de dados era "isso vai quebrar o banco de dados?" – controlada por um DBA que precisava analisar cada alteração isoladamente, pois cada alteração tinha consequências em escala de produção se fosse liberada. As revisões eram síncronas. Os agendamentos colidiam. A agenda do DBA se tornou uma fila e, às vezes, o DBA era pulado por motivos de "Tempo de Lançamento no Mercado".

No novo fluxo de trabalho, a pergunta é "este é o design correto?" O DBA já viu o diff do esquema postado pelo CI. Ele já viu a migração ser executada com sucesso em um branch de dados real. Jen também pode chamar o DBA para uma discussão, para mostrar o que ela está pensando e todas as outras opções que ela tentou. O DBA pode revisar em seu próprio cronograma, não no de Jen. Eles podem fornecer a revisão muito mais cedo no ciclo de desenvolvimento da solução e melhorar a solução em torno da integridade dos dados, estratégia de indexação, extensibilidade futura ou manutenibilidade de longo prazo, não no controle de proteção que costumava consumir todo o tempo deles.

A equipe revisa o código e o banco de dados juntos. Um PR. Uma conversa. Mesma janela.

Mesclando com confiança

A migração já foi testada em um branch de dados real. A aplicação já foi executada com o esquema alterado. A migração do esquema foi revisada. O build do CI executou exatamente os mesmos passos e está verde há uma hora.

Quando Jen mescla, a migração é aplicada ao próximo ambiente, os branches de banco de dados e código para o ambiente de CI e Jen são limpos. Assim, garantindo que a alteração do banco de dados não seja mais uma surpresa na noite de lançamento.

O que Jen acabou de fazer é a quinta prática do ensaio de 2003: integração contínua de alterações de banco de dados.

O que a jornada de Jen mostra

A alteração do banco de dados se torna parte do desenvolvimento normal. O branching reduz a espera, o risco e a sobrecarga de coordenação. O loop diário de Jen agora oferece feedback rápido e isolado na camada de banco de dados.

Na Parte 2 – O Novo Playbook de Jen, explicamos o que foi levantado e por que a camada compensatória com a qual Jen trabalhou a carreira inteira pode ser removida: branching copy-on-write, a arquitetura que a faz funcionar e as otimizações de metodologia que se seguem.

Na Parte 3 – A Equipe de Jen em Escala, analisamos como é a história de Jen quando ela é uma de cinquenta desenvolvedores, ou talvez ela esteja trabalhando em um produto white-labeled, ou ela esteja trabalhando em um monólito modular com muitos domínios dentro dele – governança na criação de branches, o reframe do DBA, o agente em loop e o trabalho de design de plataforma que se abre quando a agenda do DBA não é uma fila de tickets.

Para leitores que desejam um tour pelas ferramentas IDE que Jen usou neste post, há o Complemento: Walkthrough do Plugin – a Extensão Lakebase SCM para VS Code / Cursor, de ponta a ponta.

Finalmente, um Lakebase App Dev Kit para agentes usarem, acompanhado por um ebook para humanos seguirem, será lançado em breve.

(Esta publicação no blog foi traduzida utilizando ferramentas baseadas em inteligência artificial) Publicação original

Receba os posts mais recentes na sua caixa de entrada

Assine nosso blog e receba os posts mais recentes diretamente na sua caixa de entrada.