Protegendo Branches e Revisando Código com Pull Requests

[65] Protegendo Branches e Revisando Código com Pull Requests

Pull Requests e proteção de branch: as regras que tornam a revisão obrigatória, anatomia de um bom PR e template padrão, boas práticas de revisão de código, as três estratégias de merge do GitHub e a integração com o GitHub Actions como revisor automático.
DevOps

11 min de leitura

Em equipes sem processo de revisão, é comum encontrar código que foi direto para produção sem que nenhum outro par de olhos o tivesse lido. Bugs que poderiam ter sido detectados em segundos chegam a clientes. Vulnerabilidades de segurança passam despercebidas. Decisões de arquitetura questionáveis se acumulam silenciosamente.

O Pull Request — ou PR — é o mecanismo pelo qual uma mudança proposta é revisada, discutida e aprovada antes de ser integrada. Combinado com regras de proteção de branches, ele cria uma barreira de qualidade que não pode ser contornada — nem acidentalmente, nem intencionalmente.

Configurando Proteção de Branch no GitHub

As regras de proteção são configuradas em Settings → Branches → Add branch protection rule no repositório do GitHub.

As configurações mais importantes:

Require a pull request before merging — impede que qualquer pessoa faça push direto na branch protegida. Todo código precisa chegar via PR.

Require approvals — define quantas aprovações são necessárias antes que o PR possa ser mergeado. Para equipes pequenas, uma aprovação é suficiente. Para sistemas críticos, duas ou mais.

Require status checks to pass before merging — impede o merge se qualquer check de CI estiver falhando. Quando combinado com o GitHub Actions do artigo GitHub Actions: Sua Primeira Automação de CI/CD, o pipeline de testes se torna uma porta obrigatória.

Require branches to be up to date before merging — garante que a branch do PR está atualizada com a branch destino antes do merge, evitando que código desatualizado passe nos testes mas quebre após o merge.

Include administrators — aplica as regras também aos administradores do repositório. Sem essa opção, administradores podem contornar todas as proteções.

Criando um Pull Request

O fluxo completo, do início ao merge:

# Cria e muda para uma nova branch
git checkout -b feature/adicionar-healthcheck

# Faz as alterações necessárias
cat > healthcheck.sh << 'EOF'
#!/bin/bash
curl -f http://localhost:3000/health || exit 1
EOF

chmod +x healthcheck.sh
git add healthcheck.sh
git commit -m "feat: adiciona script de healthcheck da aplicação"

# Envia a branch para o GitHub
git push origin feature/adicionar-healthcheck

Após o push, o GitHub exibe automaticamente um botão para criar o Pull Request. Alternativamente, pela linha de comando com o GitHub CLI:

# Instala o GitHub CLI se necessário
# sudo apt install gh

gh pr create \
  --title "feat: adiciona script de healthcheck" \
  --body "Adiciona script que verifica se a aplicação está respondendo na porta 3000." \
  --base main

Anatomia de um Bom Pull Request

Um PR bem escrito economiza tempo de todos. Os elementos essenciais:

Título — claro, conciso, seguindo a convenção de commits do projeto. feat: adiciona healthcheck é melhor que atualizações diversas.

Descrição — responde três perguntas: o que foi feito, por que foi feito, e como testar. Um template pode padronizar isso para toda a equipe.

Tamanho — PRs menores são revisados mais rapidamente e com mais qualidade. Um PR com 50 linhas recebe revisão cuidadosa. Um PR com 800 linhas recebe um clique em "Approve" e uma oração.

Para criar um template padrão de PR, cria-se o arquivo .github/pull_request_template.md no repositório:

## O que foi feito
<!-- Descreva brevemente as mudanças -->

## Por que foi feito
<!-- Contexto e motivação -->

## Como testar
<!-- Passos para validar as mudanças -->

## Checklist
- [ ] Testes adicionados ou atualizados
- [ ] Documentação atualizada se necessário
- [ ] Sem secrets ou informações sensíveis no código

Realizando uma Revisão de Código

Ao revisar um PR, o GitHub oferece três opções: Comment (comentário neutro), Approve (aprovação) e Request changes (solicitação de mudanças).

Boas práticas para quem revisa:

Comentários devem ser específicos e acionáveis. "Isso está errado" não ajuda. "Esse loop pode ser substituído por Array.map para maior legibilidade — veja linha 23" ajuda.

Distingue-se entre bloqueadores reais e sugestões. Um comentário que começa com nit: sinaliza que é uma observação menor, não um bloqueador para o merge.

Aprova-se o que está bom, não apenas o que é perfeito. Perfeccionismo em code review cria gargalos e desmotiva equipes.

Merge Strategies: Qual Escolher

O GitHub oferece três estratégias de merge, cada uma com implicações diferentes para o histórico:

Merge commit — preserva todos os commits da branch e adiciona um commit de merge. O histórico fica completo, mas pode ficar verboso.

main: A - B - C - M
               \  /
feature:        D - E

Squash and merge — comprime todos os commits da branch em um único commit na branch destino. O histórico fica limpo, mas perde-se a granularidade dos commits intermediários.

main: A - B - C - DE

Rebase and merge — reaplica os commits da branch sobre a ponta da branch destino, sem criar um commit de merge. Histórico linear, mas reescreve os hashes dos commits.

main: A - B - C - D' - E'

Para projetos que seguem Git Flow, o merge commit com --no-ff é o mais adequado — preserva a evidência de que aquele conjunto de commits veio de uma feature branch. Para projetos com deploy contínuo e PRs pequenos, squash and merge produz um histórico mais legível.

Automatizando Revisões com GitHub Actions

O pipeline do artigo GitHub Actions: Sua Primeira Automação de CI/CD pode ser conectado diretamente ao processo de PR. Com a proteção de branch configurada para exigir que os checks passem, o GitHub Actions age como um revisor automático que valida antes de qualquer humano:

name: Validação de Pull Request

on:
  pull_request:
    branches: [ main ]

jobs:
  validar:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Verifica tamanho do PR
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          LINHAS=$(git diff origin/main...HEAD --stat | tail -1 | grep -o '[0-9]* insertion' | grep -o '[0-9]*')
          echo "Linhas adicionadas: $LINHAS"
          if [ "${LINHAS:-0}" -gt 500 ]; then
            echo "AVISO: PR muito grande. Considere dividir em partes menores."
          fi

      - uses: actions/setup-node@v4
        with:
          node-version: '20.x'
          cache: 'npm'

      - run: npm ci
      - run: npm run lint
      - run: npm test

Referências para Aprofundamento

Documentação oficial

Leitura técnica

  • Google Engineering Practices — Code Review — O guia interno do Google para revisão de código, aberto ao público. Cobre tanto a perspectiva de quem revisa quanto de quem tem o código revisado.
  • Conventional Commits — Especificação para mensagens de commit estruturadas, em português. Base para automação de changelogs e versionamento semântico — tema do próximo artigo.

Prática

Exercícios

Exercício 1

Por que a opção Include administrators existe, e o que ela protege? Qual o custo de ativá-la?

Ver resposta

✓ Resposta: Sem ela, as regras de proteção valem para todos menos para os administradores — que continuam podendo dar push direto na branch protegida e mergear com o CI vermelho. A proteção fica esvaziada exatamente para quem tem mais poder de causar dano, e nasce a exceção informal do "eu passo direto porque é urgente", que com o tempo deixa de ser exceção. Ativá-la torna a barreira real: não existe caminho que a contorne, nem por acidente nem por decisão. O custo é aceitar que, mesmo numa emergência, o administrador também abre PR — e é justamente esse o ponto, já que é sob pressão que os erros mais caros acontecem.

Exercício 2

Que problema específico a regra Require branches to be up to date before merging resolve? Descreva a situação em que sua ausência causa uma quebra.

Ver resposta

✓ Resposta: Ela resolve o descompasso entre o que o CI validou e o que realmente existirá depois do merge. A situação é esta: seu PR passou em todos os testes; enquanto ele aguardava revisão, a main recebeu outros merges; o resultado é que o CI aprovou seu código contra um estado do repositório que já não existe. Duas mudanças individualmente corretas podem ser incompatíveis entre si — alguém renomeou uma função que o seu código passou a chamar, por exemplo. É o caso clássico em que ninguém quebrou nada sozinho e ainda assim a main quebrou. Exigir a atualização força o CI a rodar contra o estado real de destino, antes de liberar o merge.

Exercício 3

Compare as três estratégias de merge do GitHub. O que cada uma faz com o histórico, e em que tipo de projeto cada uma se encaixa melhor?

Ver resposta

✓ Resposta: O merge commit preserva todos os commits da branch e acrescenta o commit de merge: o histórico fica completo, porém verboso. É o mais adequado a projetos em Git Flow, onde a evidência de que aquele conjunto veio de uma feature branch tem valor. O squash and merge comprime tudo em um único commit no destino: o histórico fica limpo, mas a granularidade intermediária se perde — combina com PRs pequenos e deploy contínuo, onde cada PR já é uma unidade coerente. O rebase and merge reaplica os commits sobre a ponta do destino sem commit de merge: o histórico fica linear, mas os hashes são reescritos, de modo que os commits resultantes não são mais os mesmos objetos que existiam na branch original.

Exercício 4

Por que o tamanho do PR afeta diretamente a qualidade da revisão? E o que o prefixo nit: em um comentário sinaliza?

Ver resposta

✓ Resposta: Porque a atenção do revisor não escala junto com o diff. Um PR de 50 linhas é lido linha a linha; um de 800 recebe, como diz o artigo, um clique em "Approve" e uma oração. O efeito é pior do que não revisar: o processo continua existindo no papel, dando a sensação de segurança, sem filtrar nada de verdade. Já o prefixo nit:, de nitpick, marca o comentário como observação menor e não bloqueadora. Ele separa explicitamente "isto precisa mudar antes do merge" de "se quiser, fica melhor assim" — e é o que impede que uma preferência de estilo segure uma entrega pronta.

Exercício 5

O que a combinação de Require status checks to pass com um workflow disparado por on: pull_request produz na prática? Qual o ganho além de detectar erros?

Ver resposta

✓ Resposta: Produz um revisor automático que age antes de qualquer humano: o workflow dispara quando o PR é aberto ou atualizado, roda lint e testes, e a proteção de branch mantém o botão de merge bloqueado enquanto os checks não passarem. O ganho maior não é a detecção de erros em si, e sim a realocação da atenção humana. Sem isso, boa parte da revisão se gasta apontando indentação, teste quebrado e erro de lint — coisas que a máquina vê melhor e mais rápido. Com o CI como porta obrigatória, o revisor chega ao PR já validado e pode se concentrar no que nenhuma ferramenta avalia: adequação da solução ao problema, clareza, implicações de segurança e decisões de arquitetura.

Exercício 6

Quais três perguntas a descrição de um PR deve responder? E por que feat: adiciona healthcheck é um título melhor que atualizações diversas?

Ver resposta

✓ Resposta: A descrição responde o que foi feito, por que foi feito e como testar. O "por quê" é o único dos três que o diff jamais revela — e é exatamente o que o revisor precisa para julgar se a solução escolhida serve ao problema, em vez de apenas verificar se o código funciona. O "como testar" entrega o caminho de validação pronto, em lugar de deixar o revisor adivinhar como exercitar a mudança. Quanto ao título, feat: adiciona healthcheck segue a convenção de commits e diz o que mudou; atualizações diversas obriga qualquer pessoa a abrir o diff só para descobrir se aquele PR lhe interessa. E quando o título alimenta changelog e versionamento automático, o genérico deixa de ser só inconveniente e vira ruído permanente no histórico do produto.

Comentários

Mais em DevOps

Kubernetes em Produção: Segurança, GitOps e Deploys Avançados
Kubernetes em Produção: Segurança, GitOps e Deploys Avançados

A distância entre funcionar e estar pronto para produção: os perfis do Pod…

O Que é um Container e Por Que Isso Mudou Tudo
O Que é um Container e Por Que Isso Mudou Tudo

O que é um container e por que ele mudou a entrega de software: o problema…

Introdução ao Terraform: Infraestrutura que Você Pode Versionar
Introdução ao Terraform: Infraestrutura que Você Pode Versionar

O problema dos snowflake servers e a infraestrutura que se pode versionar: o…