# Git Flow: o que aprendi quando os branches dos times se misturaram

> Um caso real de branches misturados, os problemas ao subir para a release e um exemplo de Git Flow, com seus benefícios e limites.

- URL: https://iagofrota.com.br/blog/git-flow-o-dia-em-que-os-branches-dos-times-se-misturaram/
- Data: 2026-10-21
- Tags: git, git-flow, branches, versionamento, engenharia

Há um tempo, na empresa onde eu trabalho, vi na prática o que acontece quando um time decide que o fluxo de branches é só uma sugestão.

Eu já tinha lido sobre Git Flow. Mas ler é uma coisa. Ter que desatar o nó de quem não seguiu é outra.

Bora contar o que aconteceu.

## O que aconteceu

Existia uma ferramenta interna de automação, usada por vários times no mesmo repositório. Cada time tinha o seu branch, e existia um branch de **release** comum a todos. O combinado era simples:

1. Você sobe o seu trabalho para o **branch do seu time**.
2. Quando está pronto, o time **promove** esse branch para a release.
3. Os outros times puxam a release para os seus próprios branches.

A release era o ponto de encontro. Tudo passava por ela.

Só que, no dia a dia, alguns times começaram a fazer outra coisa: **mergear direto de um branch de time para o outro**. Parecia prático. Eu preciso do que o outro time fez, então puxo daqui e pronto.

Funcionou. Por um tempo.

## Quando a conta chegou

Veio um comunicado da liderança pedindo para mostrar os números de uso da ferramenta. Era hora de subir para a release.

E foi aí que apareceram vários problemas ao mesmo tempo. Inclusive com uma pessoa do meu time, e sobrou para mim resolver.

Ao investigar, encontrei o padrão: **os merges entre times tinham pulado a promoção combinada**, e isso contribuiu para a confusão. A release já não refletia com clareza o que cada time tinha de fato entregue.

## Por que misturar branches de times dá errado

O mecanismo é sempre o mesmo.

Quando o time A faz merge no branch do time B, o código do A entra no B **do jeito que estava**. Mesmo que o A ainda não tenha promovido nada para a release. Mesmo que ninguém tenha revisado.

Depois o time B promove o seu branch. Junto vai o código do A, por um caminho que ninguém previu.

Complica, né?

O Git preserva os commits nesse caminho. Mas três riscos aparecem ao mesmo tempo:

1. **Rastreabilidade.** Fica difícil responder de qual time veio cada mudança e quando ela foi promovida.
2. **Conflitos.** Branches que evoluíram separadamente podem ter alterações incompatíveis, e o merge exige resolução na hora errada.
3. **Reversão.** Se uma mudança precisa voltar, ela já está misturada com o resto.

No fim do dia, a release deixou de ser o ponto de encontro. Virou o lugar onde a bagunça aparecia.

## Como o Git Flow resolve isso

O Git Flow foi descrito por Vincent Driessen em 2010, no artigo [A successful Git branching model](https://nvie.com/posts/a-successful-git-branching-model/). A ideia central é dar a cada branch uma função.

1. **`main`**: o código pronto para produção, normalmente cada versão publicada. No artigo original, esse branch se chama `master`.
2. **`develop`**: o que está sendo integrado para a próxima versão.
3. **`feature/*`**: uma funcionalidade, criada a partir do `develop`.
4. **`release/*`**: a preparação de uma versão, com correções e ajustes da própria versão.
5. **`hotfix/*`**: correção urgente, criada a partir da `main`.

Repare que **não existe caminho de uma feature para outra feature**. Tudo passa pelo ponto de integração. Era exatamente isso que o pessoal tinha pulado.

O nosso fluxo não era o Git Flow ao pé da letra. O branch do time fazia um papel parecido com o do `develop`. E a `release` do nosso caso era **permanente e compartilhada por todos os times**, bem diferente do `release/*` temporário do Git Flow.

Mas o princípio era o mesmo: um ponto central, e todo mundo passa por ele.

## Exemplo prático: o fluxo do caso

Imagina que você vai criar a funcionalidade de exportar relatório, no `time-a`. É um exemplo inventado, só para mostrar o caminho.

Primeiro, você cria a feature a partir do branch do seu time:

```bash
git checkout time-a
git pull origin time-a
git checkout -b feature/exportar-relatorio
```

Trabalha, commita e abre o merge request para o `time-a`. Depois do review, o merge:

```bash
git checkout time-a
git merge --no-ff feature/exportar-relatorio
git push origin time-a
```

O `--no-ff` mantém o registro de que aquele conjunto de commits era uma feature. Isso ajuda muito na hora de reverter.

Quando o time decide que está pronto, vem a **promoção** para a release. Na prática isso é um merge request, mas o comando equivalente é:

```bash
git checkout release
git pull origin release
git merge --no-ff time-a
git push origin release
```

E se o `time-b` precisa do que o `time-a` fez? Ele puxa **da release**, depois da promoção:

```bash
git checkout time-b
git pull origin time-b
git fetch origin
git merge origin/release
git push origin time-b
```

O `git fetch origin` não é enfeite. O `git pull origin time-b` atualiza o seu branch, mas não garante que o `origin/release` local esteja em dia. Sem o `fetch`, você pode fazer o merge com uma referência antiga.

O que não pode acontecer é este comando, rodando dentro do `time-b`:

```bash
git merge origin/time-a   # não faça isso
```

Foi esse tipo de merge que encontrei ao investigar o problema que contei.

## Quando vale usar o Git Flow

Na minha experiência, ele faz sentido quando:

1. **Existem versões bem definidas**, com número e data de lançamento.
2. **Você precisa manter versões antigas**, aplicando correção em mais de uma.
3. **Há um processo de homologação** entre o código pronto e a produção.
4. **Vários times mexem no mesmo código** e precisam de um ponto de integração controlado.

Repare que o quarto item sozinho não basta. Trunk-based também atende vários times. O que pesa a favor do Git Flow é o ciclo de versões e o suporte em paralelo.

O caso que contei tinha vários times, uma ferramenta compartilhada e uma release como ponto de encontro. Sem disciplina, o fluxo vira decoração.

## Quando é excesso de engenharia

Agora o outro lado, porque Git Flow não é para todo mundo.

Este blog, por exemplo, tem um único autor. Tudo vai direto para a `main`, e a `main` dispara o deploy automático. Não existe `develop`, não existe `release`, e não faria sentido existir.

Se eu criasse todos esses branches aqui, estaria só criando cerimônia.

O próprio Driessen reconheceu isso. Em 2020, ele adicionou uma nota ao artigo original dizendo que, para software com entrega contínua, um fluxo mais simples pode ser melhor. Vale a leitura do [GitHub Flow](https://docs.github.com/en/get-started/using-github/github-flow) e do [Trunk Based Development](https://trunkbaseddevelopment.com/).

Eu diria que é excesso de engenharia quando:

1. **Você entrega várias vezes por dia**, e a "release" é só o próximo deploy.
2. **O time é pequeno** e todo mundo enxerga o que o outro está fazendo.
3. **Só existe uma versão em produção**, sem suporte a versões antigas.

Minha experiência é com os dois extremos: vários times com uma ferramenta compartilhada, e um projeto pessoal. Do meio-termo, ainda estou aprendendo.

## Conclusão

Por fim, o que eu levo desse caso é que **o fluxo não é burocracia, é combinado**. Ele só funciona se todo mundo respeitar, inclusive naquele dia em que pular a etapa parece mais rápido.

Mergear entre branches de times parecia economizar tempo. Custou muito mais na hora da release.

Antes de escolher Git Flow, GitHub Flow ou trunk-based, escolha o que o seu time consegue **seguir de verdade**. E me conta: qual fluxo de branches o seu time usa hoje, e quantos já pularam a etapa?
