Pular para o conteúdo principal

Desafio 07: Estratégias de branching

Platform: GitHub-first

Habilidades do exame

  • Projetar uma estratégia de branch, incluindo trunk-based development, feature branch e release branch

Cenário

A Contoso Ltd tem três equipes de desenvolvimento trabalhando em uma plataforma compartilhada. A equipe Alpha faz deploy semanalmente (API mobile), a equipe Beta faz deploy quinzenalmente (painel web) e a equipe Gamma faz deploy mensalmente (motor de cobrança). Cada equipe adotou uma abordagem de branching diferente sem coordenação. O resultado: dor de integração a cada release, builds quebrados quando branches divergem por semanas e hotfixes que nunca voltam para as feature branches. O CTO determinou que main deve estar sempre implantável e cada equipe precisa de uma estratégia de branching adequada à sua cadência.

Tarefas

Tarefa 1: Implementar trunk-based development para a equipe Alpha (releases semanais)

Trunk-based development mantém todos os desenvolvedores trabalhando em um único branch com feature branches de curta duração (menos de 24 horas) e feature flags para ocultar trabalho incompleto.

# Clone the repository
git clone https://github.com/contoso/platform-api.git
cd platform-api

# Create a short-lived feature branch (should live < 1 day)
git checkout -b feature/add-rate-limiting
# Make changes...
git add .
git commit -m "feat: add rate limiting middleware"

# Rebase onto main before pushing (keep history linear)
git fetch origin
git rebase origin/main

# Push and create PR (will be merged same day)
git push origin feature/add-rate-limiting
gh pr create --title "Add rate limiting middleware" \
--body "Short-lived branch - feature flagged behind RATE_LIMIT_ENABLED" \
--base main

# After PR approval, merge with squash to keep main clean
gh pr merge --squash --delete-branch

Implemente uma feature flag para ocultar trabalho incompleto:

// config/feature-flags.json
{
"RATE_LIMIT_ENABLED": {
"enabled": false,
"rollout_percentage": 0,
"description": "Enable API rate limiting (in progress)"
}
}

Configure deploy contínuo a partir de main:

# .github/workflows/deploy-on-merge.yml
name: Deploy on merge to main
on:
push:
branches: [main]

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests
run: npm test
- name: Deploy to production
run: |
az webapp deploy --resource-group contoso-prod \
--name contoso-platform-api \
--src-path ./dist

Tarefa 2: Implementar GitHub Flow para a equipe Beta (releases quinzenais)

GitHub Flow usa feature branches com pull requests mesclados em main, seguido de um deploy.

# Start a feature branch from main
git checkout main
git pull origin main
git checkout -b feature/dashboard-dark-mode

# Work on the feature over several days
git add .
git commit -m "feat: add dark mode toggle to header"
git add .
git commit -m "feat: implement dark mode CSS variables"
git add .
git commit -m "test: add dark mode toggle tests"

# Push the branch and open a PR
git push origin feature/dashboard-dark-mode
gh pr create --title "Dashboard dark mode support" \
--body "Implements dark mode toggle. Closes #234" \
--base main \
--reviewer "@contoso/web-team" \
--label "enhancement"

# After review and CI passes, merge
gh pr merge --merge --delete-branch

Configure um deploy agendado para releases quinzenais:

# .github/workflows/biweekly-release.yml
name: Bi-weekly release
on:
schedule:
- cron: '0 10 1,15 * *' # 10 AM on 1st and 15th
workflow_dispatch:

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Create release tag
run: |
VERSION="v$(date +%Y.%m.%d)"
git tag -a "$VERSION" -m "Bi-weekly release $VERSION"
git push origin "$VERSION"
- name: Deploy to staging
run: ./scripts/deploy.sh staging
- name: Run smoke tests
run: npm run test:smoke
- name: Deploy to production
run: ./scripts/deploy.sh production

Tarefa 3: Implementar release branching para a equipe Gamma (releases mensais)

Release branching mantém branches de release de longa duração para suportar versões antigas enquanto main avança.

# Create a release branch when feature-complete
git checkout main
git pull origin main
git checkout -b release/v2.1
git push origin release/v2.1

# Continue development on main for next release
git checkout main
git checkout -b feature/new-billing-engine
# ... work on v2.2 features ...

# Bug fix on the release branch
git checkout release/v2.1
git checkout -b hotfix/fix-invoice-calculation
git add .
git commit -m "fix: correct tax calculation for EU invoices"
git push origin hotfix/fix-invoice-calculation
gh pr create --title "Fix invoice calculation" --base release/v2.1

# After merging to release, cherry-pick to main
git checkout main
git cherry-pick <commit-sha>
git push origin main

# Tag the release when ready to ship
git checkout release/v2.1
git tag -a v2.1.0 -m "Release v2.1.0 - Monthly billing release"
git push origin v2.1.0

# Patch release for hotfix
git tag -a v2.1.1 -m "Release v2.1.1 - Fix EU tax calculation"
git push origin v2.1.1

Tarefa 4: Framework de decisão - quando usar qual estratégia

Crie um documento de matriz de decisão:

Framework de decisão de estratégia de branching

FatorTrunk-basedGitHub FlowRelease branching
Cadência de releaseDiária/semanalQuinzenalMensal/trimestral
Tamanho da equipeQualquerPequena-médiaMédia-grande
Automação de deployObrigatória (CD)RecomendadaOpcional
Feature flags necessáriasSimOpcionalNão
Suportar versões antigasNãoNãoSim
Portão de code reviewOpcional (pair)Obrigatório (PR)Obrigatório (PR)
Estratégia de rollbackFeature flag offRevert commitDeploy versão anterior
Risco de integraçãoBaixo (sempre merged)MédioAlto (longa duração)

Tarefa 5: Lidando com branches de longa duração sem divergência

Evite que branches de release divirjam demais de main:

# Schedule regular syncs from main to release (automate this)
git checkout release/v2.1
git merge main --no-edit
# Resolve any conflicts
git push origin release/v2.1

Automatize a detecção de divergência com uma GitHub Action:

# .github/workflows/branch-drift-check.yml
name: Branch drift detection
on:
schedule:
- cron: '0 9 * * 1' # Every Monday at 9 AM

jobs:
check-drift:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check drift between main and release branches
run: |
for branch in $(git branch -r | grep 'release/'); do
BEHIND=$(git rev-list --count "origin/main..${branch}")
AHEAD=$(git rev-list --count "${branch}..origin/main")
echo "Branch ${branch}: ${BEHIND} commits behind, ${AHEAD} commits ahead"
if [ "$BEHIND" -gt 50 ]; then
echo "::warning::${branch} is ${BEHIND} commits behind main - sync needed"
gh issue create --title "Branch drift: ${branch} is ${BEHIND} commits behind" \
--body "Please merge main into ${branch} to reduce drift." \
--label "maintenance"
fi
done

Tarefa 6: Demonstrar workflows de merge vs rebase

Workflow de merge (preserva histórico, mostra onde o branch divergiu):

# Feature branch with merge
git checkout -b feature/payment-gateway
git commit -m "feat: add Stripe integration"
git commit -m "feat: add payment webhook handler"

# Merge into main (creates merge commit)
git checkout main
git merge feature/payment-gateway --no-ff
# Creates: "Merge branch 'feature/payment-gateway' into main"

Workflow de rebase (histórico linear, log mais limpo):

# Feature branch with rebase
git checkout -b feature/payment-gateway
git commit -m "feat: add Stripe integration"
git commit -m "feat: add payment webhook handler"

# Rebase onto latest main (replays commits on top)
git fetch origin
git rebase origin/main

# Fast-forward merge (no merge commit)
git checkout main
git merge feature/payment-gateway --ff-only

Rebase interativo para limpar antes do PR:

# Squash 5 messy commits into 2 clean ones
git rebase -i HEAD~5

# In the editor:
# pick abc1234 feat: add Stripe integration
# squash def5678 wip: stripe config
# squash ghi9012 fix: typo in stripe key
# pick jkl3456 feat: add payment webhook handler
# squash mno7890 fix: webhook signature validation

Exercícios de quebra e conserto

Cenário 1: Conflito de merge de release branch desatualizado

Um release branch não foi sincronizado com main por 3 semanas. Um hotfix crítico no release branch conflita com código refatorado em main.

# Simulate the problem
git checkout main
echo "class PaymentProcessor:" > billing/processor.py
echo " def calculate(self, amount):" >> billing/processor.py
echo " return amount * 1.2" >> billing/processor.py
git add . && git commit -m "refactor: rename billing processor"

git checkout release/v2.1
echo "class BillingEngine:" > billing/processor.py
echo " def calculate(self, amount):" >> billing/processor.py
echo " return amount * 1.15 # hotfix: correct tax rate" >> billing/processor.py
git add . && git commit -m "fix: correct tax rate"

# Now try to merge main into release
git merge main
# CONFLICT in billing/processor.py
Mostrar solução

Correção: Resolva o conflito mantendo a lógica do hotfix na estrutura refatorada:

# Edit the file to resolve
cat > billing/processor.py << 'EOF'
class PaymentProcessor:
def calculate(self, amount):
return amount * 1.15 # hotfix: correct tax rate
EOF

git add billing/processor.py
git commit -m "merge: resolve conflict keeping hotfix tax rate in refactored class"

Cenário 2: Push acidental em main (burlando a estratégia de branch)

Um desenvolvedor fez push de 3 commits diretamente em main em vez de usar um feature branch.

# Identify the bad commits
git log --oneline -5 main

# Create a branch from the bad commits to preserve work
git checkout main
git branch feature/recover-direct-push

# Reset main to before the direct pushes
git reset --hard HEAD~3
git push origin main --force-with-lease

# The work is preserved on the feature branch for a proper PR
git checkout feature/recover-direct-push
git push origin feature/recover-direct-push
gh pr create --title "Recover: direct push to main" --base main

Verificação de conhecimento

1. : Uma equipe faz releases diários e precisa que main esteja sempre implantável. Eles usam feature flags para ocultar trabalho incompleto. Qual estratégia de branching melhor atende a esse requisito?

2. : Qual é o principal risco de manter branches de release de longa duração?

3. : Ao usar 'git rebase' em vez de 'git merge' para um feature branch, o que acontece com o histórico do branch?

4. : Uma equipe usa GitHub Flow. Um bug crítico é encontrado em produção. Qual é o procedimento correto?

Limpeza

# Delete feature branches created during this challenge
git branch -d feature/add-rate-limiting 2>/dev/null
git branch -d feature/dashboard-dark-mode 2>/dev/null
git branch -d feature/new-billing-engine 2>/dev/null
git branch -d feature/payment-gateway 2>/dev/null
git branch -d feature/recover-direct-push 2>/dev/null
git branch -d hotfix/fix-invoice-calculation 2>/dev/null

# Delete release branches
git branch -d release/v2.1 2>/dev/null

# Remove remote tracking branches
git push origin --delete feature/add-rate-limiting 2>/dev/null
git push origin --delete feature/dashboard-dark-mode 2>/dev/null
git push origin --delete release/v2.1 2>/dev/null

# Prune stale remote references
git remote prune origin

# Verify clean state
git branch -a