pular para o conteúdo
cd/

branches sem mexer na produção: o fluxo feat, dev, main do CMS

eu sabia na teoria como funcionava um fluxo de branches. no CMS eu finalmente fiz na prática, com PR pra dev, release pra main e migration antes do código

3 minver no github

tl;dr#

no CMS do blog em que eu trabalho, o fluxo mudou pra feat/* | fix/* → dev → main. cada coisa nova nasce numa branch própria, entra na dev por PR, e a main (que é a produção) só recebe uma release, que é um PR da dev inteira. eu já sabia como isso funcionava, mas nunca tinha feito de verdade. agora fiz, e o que mais me pegou não foi o git: foi a ordem das coisas. migration antes do código, feature desligada por padrão e merge commit em vez de squash.

antes#

antes eu mandava tudo direto pra main. funcionava, mas qualquer coisa meio pronta já ia pro ar. não tinha um lugar pra juntar várias features e olhar elas juntas antes de soltar.

o fluxo#

feat/assistente ─┐
feat/sininho ────┼──> dev ──(release)──> main
fix/api-404 ─────┘
  • branch de feature: feat/alguma-coisa ou fix/alguma-coisa, sempre saindo da dev.
  • PR pra dev: é aqui que a feature é revisada. o template do PR tem um checkbox logo no topo: "a base é dev". parece bobo, mas o GitHub abre o PR apontando pra main porque ela é a branch padrão, então eu tenho que trocar a base na mão. o checkbox existe pra eu não esquecer.
  • release pra main: quando a dev tá boa, abro um PR da dev pra main listando tudo que entrou (#6, #8, #9, #10). esse é o único PR que aponta pra main.

o que eu não sabia que importava#

1. migration vai antes do código#

o banco de produção é um só, não tem banco de staging. então antes de fazer o merge da release, roda bun run db:migrate em produção. as migrations são só aditivas: tabela nova, coluna nova que aceita nulo ou tem valor padrão, nada apagado ou renomeado. assim o código antigo da main continua funcionando com o banco já migrado, e só depois o código novo chega.

se fosse ao contrário (código primeiro), a página /painel ia quebrar procurando uma tabela que ainda não existe.

2. feature nova nasce desligada#

o assistente de escrita só aparece se a variável GEMINI_API_KEY existir. sem ela, o painel fica igual ao de antes. então dá pra mandar o código pra produção sem ligar a feature pra todo mundo, e ligar só onde faz sentido.

3. merge commit, não squash#

na release eu uso Create a merge commit. se eu fizesse squash, a main ganharia um commit só, diferente de tudo que tá na dev, e na próxima release o git ia achar que as duas branches divergiram. com merge commit, a dev fica sempre só à frente da main, e a release entra sem conflito.

o que eu ainda não sei#

sinceramente, eu ainda não tenho um jeito próprio de decidir o que é certo nisso tudo. muito do que tá aqui eu segui do jeito que o Luan faz, e fui entendendo o porquê enquanto fazia. mas fazer na prática foi o que fez a teoria parar de ser teoria.