Sua Estratégia de Branch Está Ajudando ou Travando Seu Time?
Talvez o problema não seja o deploy… seja o tempo que o código ficou parado esperando merge.
Se você já trabalhou em um time grande, com várias squads mexendo em um mesmo monólito, sabe como é: o código é compartilhado, mas a dor também. Features grandes, regras de negócio emaranhadas e um sistema onde qualquer mudança pode ter efeito dominó. O resultado? Conflito atrás de conflito.
Não importa se você é júnior, pleno ou sênior, mais cedo ou mais tarde, você vai encarar:
Branches que duram semanas e dão conflito com tudo.
Merge que vira job full time.
Features que estavam prontas e “sumiram” depois de um rebase mal feito.
Código represado por medo de quebrar algo que outra squad acabou de mexer.
War Room…
Deploys bloqueados por dependências invisíveis entre módulos.
Testes quebrando em lugares que você nem tocou.
E aí, a frustração bate: "por que algo que deveria ser só versionamento acaba atrapalhando tanto o fluxo de entrega?"
A verdade é que branchs mal organizadas ou sem estratégia nenhuma viram verdadeiras armadilhas para times grandes. Mas isso não precisa ser assim.
Neste artigo, a gente vai explorar estratégias práticas para evitar:
Conflitos de merge constantes;
Represamento de features por medo de integração;
Perca de código por sobreposição entre squads;
Acúmulo de débitos técnicos invisíveis causados por isolamento excessivo.
Você vai ver como é possível trabalhar em um monólito de forma fluida e colaborativa mesmo com múltiplas squads, usando práticas como feature toggles, integração contínua real, trunk-based development adaptado à realidade, e outras formas de organizar a colaboração sem travar entregas.
O veneno das branches longas (e o antídoto que começa com pessoas)
A verdade é que, quando falamos de conflitos de merge, código perdido ou features represadas, o problema quase nunca é só a branch. Na maioria das vezes, é a falta de conversa entre as pessoas que estão usando ela.
Branchs são ferramentas. Elas não travam sozinhas. O que trava é quando cada squad trabalha como se estivesse isolada, como se aquele pedaço do monólito fosse só dela até o momento em que tudo precisa se juntar. E aí, vem o caos: conflitos, testes quebrados, medo de mergear e horas preciosas tentando entender o que mudou na base.
A mágica de uma estratégia de branches que realmente funciona começa fora do código: com colaboração, comunicação e organização. Não adianta ter uma estratégia bonita no papel se as squads não estão alinhadas entre si. Não adianta seguir um fluxo se a outra squad está reinventando o dela do outro lado do repositório.
Branchs longas, que ficam dias (ou semanas) sem serem atualizadas, criam um efeito curioso: o código “esfria”. Ele envelhece. O que você escreveu há cinco dias já não encaixa mais direito com o que foi feito na main
desde então. E o pior: quanto mais tempo passa, maior o medo de integrar. Parece que o risco cresce exponencialmente. E cresce mesmo.
Mas olha só: isso não quer dizer que está todo mundo trabalhando errado. Só quer dizer que trabalhar bem com branches depende de enxergar o código como algo coletivo, vivo, em constante mudança e não como algo que você pode guardar no bolso e devolver quando quiser.
O primeiro passo para resolver isso não está na ferramenta, está no time. Está em criar uma cultura onde:
Squads compartilham contexto antes de sair codando.
Pull requests são revisados rapidamente porque todo mundo sabe que quanto mais tempo parado, mais difícil fica.
Merges acontecem com frequência, não só quando a feature está “pronta”.
E, acima de tudo, ninguém trabalha com medo de causar conflito, porque há confiança, alinhamento e canais abertos para resolver qualquer atrito com leveza.
Quando esse ambiente existe, a estratégia de branches deixa de ser um problema técnico. Ela vira uma ferramenta a serviço da colaboração.
E é aí que a gente começa a ver a diferença entre um time que “só empilha código” e um time que constrói software junto.
O papel dos tech leads e gestores: ou você ajuda a fluir ou vira o gargalo
Em times grandes, o fluxo de trabalho não para. Branches novas surgem todos os dias, PRs se acumulam, ajustes em código compartilhado aparecem o tempo todo. É como uma cidade movimentada com gente indo e vindo por todos os lados.
E nessa cidade, os tech leads e gestores não podem ser os semáforos que travam tudo. Pelo contrário, precisam ser quem ajusta o trânsito para que tudo flua com segurança e agilidade.
Um erro comum em times assim é achar que revisão de código é algo “que dá pra fazer quando sobra tempo”. E aí os PRs se acumulam. Começa a fila. Depois vem a pressa. E logo estão sendo mergeadas branches sem revisão adequada, com bugs escondidos que vão estourar no time seguinte. O ciclo de frustração se instala rapidinho.
Tech leads e gestores precisam entender que não se trata apenas de revisar código trata-se de garantir que a colaboração entre squads funcione sem fricção. E para isso, algumas atitudes fazem toda a diferença:
Delegar revisores obrigatórios conforme a especialidade é uma excelente prática: backend revisa backend, frontend revisa frontend, e alterações que impactam regras de negócio devem passar por alguém que domina o domínio (preferencialmente um sênior, mas na ausência dele, alinhe com um PO ou outro desenvolvedor experiente para um double check). Assim, evitamos revisões superficiais e elevamos a qualidade dos merges. Sempre que possível, automatize essa delegação utilizando ferramentas como CODEOWNERS, garantindo que cada mudança passe por olhos realmente atentos ao que importa.
Rotacionar responsabilidades de revisão dentro da squad. Não é papel só do tech lead revisar tudo. É papel do time. O lead orquestra, mas não centraliza. Ele pode delegar isso para outros, achar que o Tech Lead é apenas o responsável tira a colaboração entre pessoas do mesmo time e centraliza tudo em uma pessoa só, o conhecimento fica represado!
Acompanhar o tempo médio de aprovação de PRs. Se está demorando demais, algo precisa ser ajustado. O lead tem que agir antes que vire gargalo.
Fomentar a cultura de revisão leve, frequente e orientada a contexto. Não é só "olhar o código", é entender o impacto daquilo no resto do sistema especialmente num monólito.
E o mais importante: ser exemplo de disponibilidade e agilidade. Quando o lead some por 2 dias e depois aparece cobrando agilidade dos outros, o time trava de vez.
O ponto é: o merge é o fim da linha de produção, mas ele só funciona se todo o resto estiver alinhado. Sem revisores bem definidos, com autonomia e clareza de papéis, o sistema emperra. A culpa recai em quem desenvolveu, mas a falha começou antes na falta de estrutura e confiança para garantir que todo mundo está revisando o que realmente entende.
Então, se você lidera ou gerencia um time que produz código o tempo todo, pare por um momento e reflita: você está ajudando o time a ganhar velocidade com segurança, ou está virando um checkpoint desnecessário na estrada?
Porque, num ambiente de colaboração intensa, o melhor lead não é o que aprova tudo, mas o que libera os caminhos para que o time aprove junto.
Qual estratégia escolher? Depende. Mas ignorar o contexto nunca dá certo.
É tentador procurar a “melhor estratégia de branch. uma solução clara, simples e que funcione sempre. Mas com branchs, a coisa não é tão binária assim. O que funciona perfeitamente para um time pode virar um desastre em outro. E o segredo está no contexto.
Vamos imaginar um cenário que, convenhamos, é mais comum do que gostaríamos:
um time grande, com muitas features sendo desenvolvidas ao mesmo tempo, mas com poucos desenvolvedores sêniors para revisar as PRs.
Você pode até adotar o Git Flow com aquela separação bonita entre develop
, feature
, release
, hotfix
e main
. Só que… quem vai garantir que as PRs estão sendo bem revisadas antes de ir pro develop
?
Quem vai coordenar o fluxo entre release
e main
com segurança?
E mais importante: quantas pessoas vão ficar esperando “alguém com permissão” aprovar algo enquanto o código esfria?
Em times assim, Git Flow tende a ser mais burocracia do que proteção. Ele pressupõe um nível de maturidade, revisão e sincronização que, se não for real, acaba só escondendo os problemas atrás de branches “bonitas”.
Então… é tudo culpa da estratégia? Não. Mas também não adianta usar uma estratégia complexa num time que ainda não tem estrutura para sustentá-la.
Talvez o caminho mais seguro nesse contexto seja apostar em algo mais fluido e contínuo, como o Trunk-Based Development, combinado com feature toggles (vamos falar mais pra frente)
Isso não exige que você tenha 10 pessoas experientes revisando cada PR. Exige que o time tenha disciplina para integrar com frequência, uma boa base de testes e uma estrutura mínima para toggles (que pode ser até um arquivo de config simples no começo).
Claro que toggles não são mágicos: se forem mal usados, viram bagunça. Mas com atenção e limpeza recorrente, eles podem destravar o fluxo de entrega sem precisar que tudo esteja 100% pronto ou 100% revisado por alguém experiente.
E é aí que está a virada de chave:
A melhor estratégia de branch é aquela que protege seu fluxo, sem criar barreiras que seu time ainda não consegue pular.
Não adianta parecer sofisticado se ninguém consegue acompanhar. E também não adianta simplificar tanto a ponto de perder controle.
Antes de copiar o que o Spotify ou a Netflix faz, olhe para o seu time.
Quantas features estão em desenvolvimento ao mesmo tempo?
Quantas pessoas têm contexto suficiente para revisar código com segurança?
Qual o nível de autonomia e confiança entre squads?
Tem automação suficiente para confiar em merges contínuos?
A resposta para qual estratégia seguir não vem do Git, vem do chão da sua operação.
E ela pode mudar com o tempo e tudo bem.
Feature Toggles na prática: o botão invisível que salva seu fluxo
Imagine que você está terminando uma parte importante de uma feature, mas ainda falta um ajuste visual, um teste de ponta a ponta ou aquele último detalhe que depende de outro time. Ainda assim, você gostaria de mergear seu código na main
hoje, porque quanto mais tempo você espera, mais coisas mudam — e mais difícil fica.
É aí que entra o Feature Toggle.
Um toggle é, basicamente, um interruptor no seu código. Ele permite que você suba uma feature incompleta ou desativada para produção, sem que isso afete o usuário final. A funcionalidade está lá, no repositório, nos testes, no deploy… mas só será visível ou executada se o toggle estiver ligado.
E o que isso resolve na prática?
Permite merges frequentes, mesmo que a feature esteja em progresso.
Evita represamento de código. Nada de “esperar mais um dia” pra fazer o merge.
Reduz conflitos. Menos código isolado, menos rebase sofrido depois.
Desacopla desenvolvimento de release. Você pode subir hoje e liberar quando quiser.
Tipos de toggles mais comuns:
if (toggleService.isEnabled("nova_tela_de_pagamento")) {...}
Flag por ambiente (
staging
,prod
)Config por banco ou cache (ex: Redis)
Ferramentas prontas como LaunchDarkly, Unleash, ConfigCat, ou soluções caseiras com banco/config
Toggle mal usado vira armadilha
Toggles são ótimos aliados, mas precisam de disciplina. Do contrário, eles viram verdadeiros fantasmas no seu código escondendo lógicas antigas, acumulando branches zumbis e atrapalhando quem está chegando no projeto.
Alguns cuidados essenciais:
Todo toggle deve ter um plano de expiração. Se está ligado ou desligado, por quê? E até quando?
Não use toggle pra esconder código quebrado. Ele não é desculpa pra pular testes.
Evite condicionalizar cada parte da feature separadamente. Um bom toggle protege o comportamento inteiro, não uma linha solta.
Limpeza periódica. O toggle foi lançado e validado? Remova! Código morto não contribui.
Um exemplo simples:
Você está construindo uma nova tela de checkout. Backend e frontend estão em paralelo, mas o design ainda não foi validado com o time de produto.
No backend:
if (toggleService.isEnabled("checkout_v2")) {
return novaVersaoDoCheckout();
} else {
return checkoutAtual();
}
No frontend:
{ toggles.checkout_v2 ? <NewCheckout /> : <OldCheckout /> }
Você pode mergear ambos na main
hoje, subir para staging, testar... e só ativar no momento certo com segurança e controle.
Em resumo: Feature Toggles não são enfeite. São estratégia de sobrevivência em times que querem escalar sem perder fluidez.
Eles ajudam seu time a respirar, seu repositório a ficar saudável, e sua entrega a seguir mesmo quando nem tudo está 100% pronto.
Se usados com responsabilidade, eles viram a ponte entre o que está sendo feito e o que está pronto para ser usado.
Trunk-Based Development: integrando cedo, errando rápido, corrigindo junto!
Imagine um fluxo onde, ao invés de trabalhar dias em uma branch esperando "terminar tudo", você faz pequenos avanços, integra com o time todos os dias (ou até mais de uma vez ao dia), e vê sua entrega se construindo passo a passo, sem represamento. Esse é o espírito do Trunk-Based Development.
No lugar de manter dezenas de branches isoladas, o time se organiza para manter uma única linha principal ativa a famosa main
ou trunk
. Todo mundo trabalha ali por perto. As branches existem, claro, mas são curtas, rápidas e com vida útil mínima. O foco está na integração contínua, e não no isolamento prolongado.
Só que vamos falar a verdade… isso exige mais do que mudar o nome da branch principal no repositório. TBD não é só um fluxo de Git, é uma forma de trabalhar.
O que muda na prática?
As tarefas precisam ser quebradas em entregas menores. Pequenos passos, que cabem em 1 ou 2 dias no máximo.
O código é integrado com frequência real, não só “quando der”.
Automação se torna essencial. Testes precisam rodar com confiança para que o time integre sem medo.
Erros aparecem cedo, e isso é bom. O problema salta na hora certa, quando ainda está fresco não dias depois, quando já virou confusão.
Planejar tarefa pequena
↓
Criar branch curta (1-2 dias)
↓
Desenvolver com foco em integração
↓
Rodar testes locais e automatizados
↓
Abrir PR (pequena e objetiva)
↓
Revisão rápida por pares
↓
Merge na trunk (`main`)
↓
Pipeline CI valida e faz deploy contínuo
↓
Feature controlada por toggle (se necessário)
↓
Próxima tarefa incremental
Ou para melhorar isso visualmente…
Mas isso não é arriscado?
Pode parecer. Especialmente quando se está acostumado a só integrar depois de “testar tudo mil vezes”. Mas aqui está a virada de chave: o risco maior está em esperar demais.
Quanto mais tempo você espera pra integrar, mais chances de conflito. Mais chances de estar mexendo em algo que já mudou. No TBD, o risco é diluído. Ele é menor, mas mais frequente o que torna muito mais fácil de identificar e corrigir.
É o clássico: errar rápido e pequeno, ao invés de errar grande e atrasado.
Onde TBD funciona bem (e onde exige atenção)
Se você tem:
Um time com bons testes automatizados (ou pelo menos em evolução nesse sentido),
Um mínimo de disciplina para revisar código com agilidade,
E espaço para refinar melhor as tarefas antes de codar…
Então o TBD pode ser adotado sem dor, inclusive em times grandes.
Agora, se o time está acostumado com:
PRs que demoram dias pra serem lidos,
Features mal quebradas (“criar sistema de cobrança” como única tarefa da sprint),
Testes frágeis ou inexistentes,
… aplicar o TBD vai ser mais difícil.
Mas não significa que é impossível. Você pode começar aos poucos, com:
Branches de 2 dias no máximo, só pra exercitar o hábito de integrar mais cedo.
Automatizar os testes mais críticos primeiro.
Criar combinados internos de revisão rápida.
E, claro, complementar com feature toggles para proteger o que ainda está em progresso.
Mas e se a minha feature leva mais de dois dias?
Uma dúvida super válida. Quando a gente fala em trabalhar com branches curtas, de no máximo dois dias, é natural alguém pensar: “Mas a minha feature é muito grande… não tem como entregar isso nesse tempo.”
E sim, isso acontece. Nem toda tarefa cabe em dois dias especialmente em sistemas complexos, com múltiplas integrações, testes, validações manuais ou dependências externas.
Então o que fazer? A resposta mais comum (e mais tentadora) é:
"Ah, vamos deixar a branch durar mais um pouquinho. Três, quatro dias... uma semana.” E pronto: é aí que o ciclo de acúmulo, conflito e represamento recomeça.
A saída não está em esticar o tempo da branch, e sim em olhar para o que está sendo planejado.
Se uma feature não cabe em dois dias, talvez ela esteja grande demais mesmo. E isso não é um problema de complexidade é um problema de refino.
Aqui vai uma provocação importante:
Se você precisa de uma semana inteira para entregar algo, será que isso foi quebrado da forma certa?
A gente tende a pensar em features como “entregas inteiras”, mas num fluxo saudável de desenvolvimento, o ideal é pensar em incrementos seguros e integráveis. Cada desenvolvedor precisa conseguir avançar uma parte concreta da solução, algo que possa ser testado, versionado e, se possível, já integrado em até 1 ou 2 dias úteis.
Lembra que um dia tem cerca de 8-9 horas úteis de jornada de trabalho. Se mesmo nesse tempo você não consegue evoluir um trecho claro de valor, talvez seja o momento de revisar o planejamento da feature:
Ela pode ser quebrada em passos menores, com merges intermediários?
Há trechos que podem ser codificados sem ativar ainda a experiência completa, usando toggles?
Dá pra aplicar estrutura antes do comportamento (por exemplo, criar o endpoint sem ainda ativar a lógica)?
Ou você está tentando resolver tudo de uma vez porque o escopo está mal definido?
Muitas vezes, o problema não está no código, está na forma como a tarefa chegou até ali. Um refinamento fraco gera atividades grandes demais, o que gera branches longas, que geram conflitos, que geram retrabalho… e lá vamos nós.
Então, sim: às vezes a feature realmente é longa. Mas a pergunta mais honesta é: ela precisa ser?
Quanto mais o time aprende a quebrar suas entregas em passos pequenos e integráveis, mais fluido e seguro fica o processo. Não é sobre apressar ninguém. É sobre permitir que o código entre no sistema aos poucos, com visibilidade e controle, em vez de ser despejado todo de uma vez como uma caixa-surpresa que só vai abrir no último dia da sprint.
Afinal, a main
é produção?
Depende do seu fluxo. Em muitos times que adotam Trunk-Based Development, sim a main
é a branch que representa o código que pode ir para produção a qualquer momento.
Mas isso não significa que tudo que está na main
já está visível para o usuário.
E aqui entra o truque que faz o TBD funcionar: o uso de feature toggles, deploys controlados, ou até pipelines com aprovação manual para liberar aquilo que realmente está pronto.
Então sim, você pode (e deve) mergear na main
mesmo com a feature incompleta desde que ela esteja protegida. Incompleta no sentido de “ainda não liberada para uso”, mas não no sentido de “quebrando tudo”. Essa diferença é fundamental.
Por que isso não vira bagunça?
A sensação de “desorganização” geralmente vem de dois medos:
Medo de subir código quebrado, porque associamos
main
a produção imediata.Medo de misturar código inacabado com o que está pronto.
Mas veja bem: No TBD, a main
não é “tudo vai pro ar na hora”, e sim “tudo aqui precisa estar testado, integrável, e confiável mesmo que esteja escondido”.
Se o seu time tem:
Pipelines de CI/CD confiáveis
Testes automatizados confiáveis
Toggles ou estratégias para não ativar comportamentos incompletos
… então mergear todo dia na main
não só é possível como desejável. É assim que você mantém o código vivo, próximo da base real, e evita aquele cenário clássico:
“Subi uma feature gigante, mexi em 80 arquivos, e agora não sei mais o que está conflitando com quem.”
Alternativas seguras: a main
como pré-produção
Agora, se seu time ainda não está nesse nível de maturidade, dá pra adaptar.
Você pode pensar na main
como uma cópia segura e contínua do código “pronto para ser validado”, enquanto mantém uma branch separada (por exemplo, production/master
) que representa o código efetivamente em produção.
Nesse caso, a main
é sempre integrável, testável, limpa mas só vai pra production
com aprovação explícita.
Isso dá um tempo de respiro enquanto o time aprende a confiar no fluxo e automatiza as validações. Então no Trunk-Based Development:
✅ Você mergeia todos os dias na main
✅ Mesmo com a feature incompleta (mas protegida)
✅ Com segurança, visibilidade e controle
❌ E sem esperar aquela aprovação milagrosa “um dia antes do deploy”
A organização não está em isolar as mudanças. Está em integrar bem, com responsabilidade e consistência.
Trunk-Based Development não é sobre velocidade, é sobre visibilidade.
Quando todo mundo trabalha próximo da main
, o código fica mais vivo, mais acessível e mais fácil de acompanhar. Você vê o sistema evoluindo como um organismo em crescimento e não como um conjunto de peças que vão ser encaixadas no fim, torcendo pra caber.
É desafiador? Sim. Mas todo time que passou a trabalhar assim e acertou o ritmo nunca mais quis voltar para branches longas e releases coletivos.
A falsa sensação de segurança da Branch Develop
Muita gente encara a develop
como um ponto de partida “menos arriscado” que a main
, um tipo de zona de amortecimento onde tudo pode acontecer com menos impacto. Mas na prática, isso costuma abrir espaço para o acúmulo de código frágil, decisões inacabadas e mudanças que ninguém garante que funcionam juntas. A sensação de estabilidade nasce da ausência de verificação imediata, não da real confiança no que está ali. E isso é perigoso.
A herança do caos
Quando alguém cria uma nova branch a partir de uma develop
desatualizada ou pior, bagunçada, carrega para o novo trabalho todo um pacote de problemas invisíveis. São bugs silenciosos, conflitos de dependência, testes inconsistentes e código legado de outras features que ainda nem foram finalizadas. O resultado? Desenvolvedores começam a lutar contra falhas que não têm nada a ver com o que estão construindo, perdendo tempo e foco em algo que nem era problema deles para resolver.
O ciclo de contaminação
Esse comportamento não só afeta quem cria a nova branch. Ele contamina todo o ciclo de desenvolvimento. Cada nova feature que nasce sobre uma base instável se torna, por consequência, mais instável também. As branches deixam de ser ambientes previsíveis e passam a ser mutações genéticas de algo que ninguém mais tem controle. Quando essas mutações voltam pro código principal, o time precisa lidar com mutações acumuladas de várias pessoas ao mesmo tempo e é aí que os conflitos de merge viram um pesadelo.
O monstro dos merges
É nessa hora que a conta chega. O famoso “merge final” se transforma numa operação cirúrgica de dias, envolvendo dezenas às vezes centenas de arquivos. O histórico de mudanças já não ajuda. Os testes quebram sem explicação clara. A build começa a falhar intermitentemente. E o time perde confiança no próprio sistema, adotando posturas defensivas: “vamos esperar o fulano terminar antes de mexer nisso”, “deixa pra depois da homologação”, “não vamos mexer agora que está funcionando”. A entrega paralisa. E a produtividade vai pelo ralo.
Flags mal gerenciadas, builds quebradas
Outro problema comum nesse cenário são as feature flags mal implementadas ou mal gerenciadas. Em vez de ajudar, elas viram armadilhas (comentei isso anteriormente mas quero reforçar). Como as features convivem por muito tempo dentro da develop
, é comum que uma flag ativada para uma feature interfira no comportamento de outra, ainda não concluída. Isso pode causar falhas no ambiente de QA, nos testes automatizados ou até na homologação. E o pior: quando ninguém sabe mais o que pode ou não estar ligado, a confiança na build some. Desenvolvedores começam a ter medo de fazer alterações simples, justamente porque não sabem mais o que está sob controle.
O sistema perde sua forma
O mais perigoso de tudo é o que acontece com o sistema como um todo. Ele perde sua forma, sua coesão. As pessoas deixam de enxergar o software como um organismo funcional e passam a vê-lo como uma colcha de retalhos, onde cada parte obedece uma lógica diferente. Fica difícil saber o que está pronto, o que está em teste, o que está em produção. O backlog começa a inflar com tarefas do tipo “corrigir comportamento estranho na develop” ou “entender por que isso parou de funcionar”. São tarefas que não criam valor — apenas remendam o estrago causado por decisões mal integradas.
Então… o que fazer com a develop?
A resposta mais honesta é: ela precisa ter dono, propósito e limites. O erro não está em ter uma branch chamada develop
, mas sim em tratá-la como uma entidade mágica que por si só resolve os conflitos do time. Se ninguém cuida dela, ela vira um depósito de código quebrado. Mas se for bem gerenciada, pode sim ter um papel útil em cenários específicos especialmente para testes em ambiente de integração ou validação de múltiplas features juntas.
Mas, pra isso funcionar, todo o time precisa estar ciente de algumas coisas fundamentais:
✅ develop
não é um lugar pra “jogar código” esperando que alguém conserte depois.
Se algo quebrou, é responsabilidade de quem quebrou corrigir rapidamente ou reverter. Branch suja é branch que atrasa todo mundo.
✅ Qualquer merge nela precisa passar por um processo de validação nem que seja mínimo.
Se não dá pra rodar todos os testes, rode os principais. Se não dá pra validar tudo, valide o que impacta outras pessoas.
✅ Ela precisa estar sempre atualizada e “estável”.
Não é a main
, mas também não pode ser um terreno minado. Manter a develop
respirando depende de integração frequente, pequenas entregas e correções rápidas.
✅ Branches não devem ser criadas a partir da develop
sem saber exatamente o que está lá.
Se você não sabe quais features estão ativas ou quais flags foram alteradas, sua branch já nasceu contaminada.
✅ Feature flags devem ser rastreáveis e consistentes.
Se você ativou uma flag em develop
só “pra testar rapidinho”, documente ou remova depois. Do contrário, você acaba quebrando ambientes e prejudicando outros devs sem perceber.
A develop
, se for usada, precisa ser tratada com a mesma seriedade da main
. Porque em times ágeis, toda branch ativa é um pedaço do sistema em movimento e não uma lixeira de transição. Não existe ambiente neutro. Ou ele impulsiona o fluxo, ou ele trava o time. O que vai determinar isso é o nível de disciplina, visibilidade e comunicação que o time constrói em torno dela.
E se meu time é júnior, o produto já está no ar, e eu não posso errar?
Vamos ser honestos: essa é, provavelmente, a realidade de muitos tech leads por aí. Você tem um produto em produção, com clientes reais usando. Tem features importantes para entregar e rápido. Mas seu time ainda está ganhando maturidade. Boa parte dos desenvolvedores são júniors e talvez sem tanta vivência em grandes projetos e tantas regras de negócio.
E aí surge o dilema:
“Eu não posso deixar todo mundo integrar o tempo todo, porque uma mudança errada quebra o sistema. Mas se eu continuar desse jeito, meus deploys nunca vão deixar de ser caóticos.”
Esse é o típico impasse entre segurança e fluidez. E aqui vai uma verdade que talvez doa um pouco, mas é libertadora quando aceita:
Você só consegue ter fluidez com segurança quando os fundamentos estão no lugar.
Não tem atalho. Se você quer parar de sentir medo ao mergear uma branch na main
, você vai precisar investir em estrutura. E sim — isso dá trabalho. Mas é o tipo de esforço que paga dividendos todos os dias depois.
O que não pode faltar para dar esse salto?
1. Testes automatizados confiáveis
Chega de confiar só no teste manual "olha aqui no navegador e vê se tá ok".
Testes automatizados são o alicerce da confiança. Mesmo que seu time seja júnior, você pode (e deve) trabalhar junto para:
Escrever bons testes de unidade e integração.
Criar uma base mínima de testes de regressão.
Automatizar cenários críticos do negócio.
Você não precisa cobrir 100% do código — mas precisa cobrir o que não pode quebrar.
2. Uma esteira de CI/CD funcional
Uma pipeline bem configurada é como um guarda de trânsito invisível:
Ela deixa passar quem tá certo e barra o que tá errado.
Configure etapas automáticas para:
Build
Testes
Análise de qualidade
Deploy controlado por ambiente
Isso tira o fardo de ficar "checando tudo na mão" e protege seu time de si mesmo, especialmente nos dias mais corridos.
3. Code Review de verdade (não só um 👍 no PR)
Esse ponto não dá pra negociar.
Code review é a última linha de defesa antes do caos.
Se você é tech lead, sua responsabilidade não é aprovar tudo é estruturar quem revisa o quê.
Defina revisores obrigatórios por contexto (ex: backend, frontend, regras de negócio, QA’s).
Treine o time para fazer revisão com foco em impacto, não só em formatação.
Crie rituais de revisão rápida (não pode levar 3 dias pra alguém olhar um PR).
Mesmo que o time seja júnior, revisores bem definidos criam cultura, confiança e aprendizado coletivo.
4. Uma estratégia de branch que respeite sua realidade
Não adianta forçar Trunk-Based se o time ainda não consegue testar bem o que escreve.
Não adianta criar 5 branches separadas se ninguém entende como fazer merge sem conflito.
Você precisa encontrar o equilíbrio entre controle e integração.
Talvez sua main
ainda não possa ir direto pra produção. Sem problemas crie uma release
intermediária com deploy manual.
Talvez as features precisem ser protegidas por toggles. Ótimo, implemente isso aos poucos.
Talvez você ainda precise de um "pré-merge check" com ajuda dos seniors. Combine isso e torne rotina.
Mas não fique parado no caos só porque “não dá pra mudar tudo agora”.
Comece com o que dá. E cada passo na direção certa já vai diminuir o medo, o risco e o retrabalho.
Antipadrões que travaram seu deploy (e ainda travam o de muita gente)
Agora que a gente falou de estratégias, boas práticas e caminhos possíveis, vamos combinar uma coisa?
Nada disso adianta se você continuar alimentando antipadrões que sabotam o fluxo do seu time.
Sim, eles parecem inofensivos à primeira vista. Alguns até passam a impressão de organização.
Mas por trás, só geram mais fricção, mais retrabalho e mais medo de dar merge.
Vamos aos piores culpados:
❌ Branches de release em ambientes de mudança constante
Sabe aquele fluxo onde, ao invés de subir da main
, você cria uma release/v1.5
, congela tudo e tenta estabilizar ali? Parece profissional, né?
Mas se o sistema ainda está em desenvolvimento ativo, com várias squads mexendo em tudo ao mesmo tempo, isso vira um inferno logístico.
Todo bug fix tem que ser portado de volta. Features ficam fora de sincronia.
E, no fim, ninguém sabe mais onde está a “versão certa”.
Em ambientes dinâmicos, branch de release só serve pra acumular ansiedade.
❌ "Code freeze" no fim da sprint pra tentar juntar tudo
"Vamos congelar o código na quinta-feira pra testar e integrar tudo até sexta."
O plano parece bom no papel… até as primeiras 3 features darem conflito, a branch de 5 dias quebrar a build, e o deploy virar uma corrida desesperada contra o relógio.
Code freeze em times que ainda não integraram as features é como chamar os bombeiros quando a casa já está pegando fogo.
Se o código precisa “parar” pra funcionar, tem algo errado na forma como ele é construído.
❌ Merge coletivo no último dia: o “big bang” da dor
Não tem nada mais perigoso do que deixar para integrar tudo no fim.
Isso transforma o merge que deveria ser só mais uma etapa, em um evento traumático.
Conflitos em todo lado
Testes quebrando sem contexto
A famosa frase: “mas na minha máquina tava funcionando…”
Se você ainda faz isso: pare agora.
Integração contínua existe justamente pra evitar esse acúmulo de tensão.
❌ Cherry-pick em branches paralelas: um caos invisível
"Ah, vamos só pegar esse commit e jogar ali rapidinho…"
E pronto: agora você tem duas branches com código parecido, mas não idêntico.
Dois lugares pra corrigir bug. Duas versões do mesmo sistema em produção.
E ninguém mais sabe onde está o comportamento real.
Cherry-pick pode até salvar numa emergência. Mas como prática regular, é um atalho que sempre cobra um preço alto depois.
❌ Squad “dona da pasta” que não conversa com o resto
"Essa parte do sistema é minha, ninguém mexe."
Esse tipo de mentalidade territorial é o oposto da colaboração.
Num monólito, tudo está conectado. Quando uma squad se isola e trava mudanças, o sistema inteiro perde fluidez.
Mais cedo ou mais tarde, alguém vai precisar mexer onde “não podia”. E aí, ou a pessoa quebra algo sem querer, ou evita mexer — e o débito técnico só cresce.
Código compartilhado exige responsabilidade coletiva, não propriedade exclusiva.
Então… evite:
🚫 Evite acúmulo
🚫 Evite isolamento
🚫 Evite "gambiarras de fluxo"
✅ Prefira integração frequente
✅ Comunicação constante
✅ Revisão de verdade
✅ Automatização que inspira confiança
Se o seu deploy ainda parece uma bomba-relógio, talvez o código não seja o maior problema.
Talvez o problema esteja no fluxo. Ou pior: no hábito.
Mas a boa notícia é que há saída. E ela começa ao evitar tudo isso.
Conclusão
Você chegou até aqui, e talvez esteja pensando:
"Ok, eu entendi as estratégias, os riscos, os antipadrões… mas por onde eu começo?"
A resposta é simples: comece pelo que está ao seu alcance hoje.
Nem todo time vai conseguir adotar Trunk-Based Development amanhã.
Nem todo projeto está pronto para feature toggles agora.
Mas todo time pode revisar seus hábitos e decidir parar de alimentar o caos.
Você pode:
Reduzir o tempo de vida das suas branches.
Refatorar uma tarefa grande para entregas menores.
Criar o primeiro teste automatizado daquilo que mais quebra.
Definir um revisor por contexto técnico.
Evitar aquele merge coletivo de última hora que já deu errado antes.
Você não precisa da solução perfeita. Precisa de movimento na direção certa.
Fluxo saudável, deploy confiável e menos estresse no time não são utopia… são o reflexo de pequenas decisões bem pensadas, repetidas com consistência.
Então leve isso com você:
Você não está aqui só pra entregar código. Você está aqui pra criar um sistema que entrega com confiança.
E isso começa com estratégia, colaboração e a coragem de fazer diferente do que sempre foi feito.
Muito obrigado por ler até o final e até o próximo artigo! ❤️
Post muito legal.
Estou justamente passando por isso.
Meus desafios são gerar entregáveis para um front legado, em uma feature que é até considerada média, no tamanho.
As vezes esbarramos em quebrar a feature em user stories pequenas, mas nem sempre a user story vai gerar um valor para o cliente.
Eu estou duma realidade que o time está saindo do merge c a main p merge c branch feature.
Vamos ver se vai ser problema hehe. Eu particularmente prefiro ir p main direto e usamos tag quando queremos fazer deploy p produção.