TL;DR
- Numa equipa de 3 a 5 developers, o default deve ser 1 aprovação obrigatória, CI verde e PRs pequenos. 2 aprovações quase sempre bloqueiam a entrega.
- Um bom code review não tenta encontrar todos os bugs. Tenta proteger arquitectura, legibilidade, segurança, migrações de dados e contratos externos.
- O tamanho do PR é a métrica que mais muda o resultado. Acima de 300 a 500 linhas alteradas, a qualidade da revisão cai muito.
- Define SLA explícito: primeira resposta em menos de 4 horas úteis, revisão completa em menos de 24 horas úteis. Sem isto, reviews viram filas invisíveis.
- Automatiza formatação, lint, testes e checks óbvios. Humanos devem rever decisões, não espaços, nomes de ficheiros ou imports.
O objectivo real do code review numa equipa pequena
Numa equipa de 3 a 5 developers, code review não é uma cerimónia para fingir rigor. É um mecanismo de controlo de risco. O problema é que muitas equipas copiam processos de empresas com 200 engineers e acabam com uma versão lenta, frustrante e pouco útil.
O objectivo deve ser claro: aumentar a qualidade sem travar a cadência.
Para mim, um code review pequeno e bem feito deve responder a cinco perguntas:
- Isto faz o que o ticket diz?
- Isto encaixa na arquitectura existente?
- Isto introduz risco em dados, segurança, performance ou contratos externos?
- Isto vai ser fácil de manter daqui a 6 meses?
- Há testes suficientes para o risco da alteração?
Repara no que não está na lista: preferências pessoais de estilo, discussões infinitas sobre nomes, refactors não relacionados, formatação manual. Isso deve estar automatizado ou decidido antes.
Em equipas pequenas, o custo de contexto é enorme. Se tens 4 developers e 2 ficam bloqueados em reviews durante meio dia, perdeste 25% da capacidade produtiva da equipa nesse período. Isto parece exagero até medires o tempo entre abrir um Pull Request e fazer merge.
A métrica que eu gosto de acompanhar é simples: p95 do tempo até merge. Para uma equipa de produto saudável, alterações normais devem ter p95 abaixo de 48 horas úteis. Bugs críticos e alterações pequenas devem estar abaixo de 24 horas úteis. Se um PR fica 4 dias à espera, não tens um processo de qualidade. Tens inventário parado.
A regra dos 300 LOC e porque PRs grandes são mentira
O maior erro em code reviews é aceitar PRs gigantes como algo normal.
A partir de 300 a 500 linhas alteradas, excluindo lockfiles e snapshots gerados, a revisão começa a degradar. O reviewer deixa de perceber a intenção completa, passa a fazer scanning visual e começa a aprovar por cansaço. Ninguém gosta de admitir isto, mas acontece.
A regra prática que recomendo:
| Tipo de alteração | Tamanho saudável | Aprovação | Tempo esperado |
|---|---|---|---|
| Bug pequeno | até 100 LOC | 1 reviewer | no próprio dia |
| Feature normal | 100 a 300 LOC | 1 reviewer | menos de 24h úteis |
| Migração de dados | qualquer tamanho | 1 reviewer sénior | revisão cuidadosa |
| Refactor transversal | 300 a 800 LOC | partir em fases | não rever tudo de uma vez |
| PR acima de 800 LOC | suspeito | justificar | normalmente dividir |
LOC não é uma medida perfeita. Uma alteração de 40 linhas numa query SQL pode ser mais perigosa do que 400 linhas de UI. Ainda assim, é uma excelente proxy para carga cognitiva.
O ponto não é criar uma regra burocrática. É forçar a pergunta certa: porque é que isto não pode ser dividido?
Um padrão que funciona bem:
- PR 1: preparar estrutura sem mudar comportamento.
- PR 2: adicionar comportamento novo atrás de feature flag ou caminho isolado.
- PR 3: ligar comportamento na UI ou API.
- PR 4: remover código antigo.
Isto torna o review mais fácil e reduz risco. Também facilita rollback. Se tudo entra num único PR com base de dados, API, frontend e limpeza, qualquer problema obriga a reverter um pacote grande.
Há uma excepção: código gerado. Por exemplo, actualizar um SDK, regenerar tipos OpenAPI ou snapshots. Nesse caso, o PR pode ter muitas linhas, mas o reviewer deve focar-se no diff semântico: schema, contrato, endpoints removidos, alterações breaking. Não deve fingir que leu cada linha gerada.
Aprovações: 1 é quase sempre melhor do que 2
Em equipas de 3 a 5 developers, exigir 2 aprovações por defeito é normalmente um erro.
Com 3 developers, 2 aprovações significam que toda a equipa revê quase tudo. Com férias, doença, foco em suporte ou reuniões, o fluxo pára. O ganho de qualidade raramente compensa o custo.
A minha recomendação:
- 1 aprovação obrigatória para alterações normais.
- 2 aprovações apenas para áreas críticas.
- Auto-merge permitido quando CI está verde e a aprovação existe.
- Dono técnico explícito para partes sensíveis do sistema.
Áreas críticas incluem autenticação, permissões, pagamentos, facturação, migrações destrutivas, dados clínicos, integrações com ERP, sincronização de stock, webhooks e alterações de infraestrutura.
Podes configurar isto com CODEOWNERS no GitHub ou GitLab. A documentação oficial vale a pena ler, especialmente as regras de branch protection do GitHub e merge request approvals do GitLab 17.
Exemplo simples de CODEOWNERS:
# Autenticação e permissões
/apps/api/src/auth/ @ana @rui
# Pagamentos e webhooks
/apps/api/src/billing/ @rui
# Infraestrutura
/infra/ @miguel
/.github/workflows/ @miguel
Isto não resolve tudo, mas evita uma armadilha comum: a pessoa que menos conhece a área aprovar a alteração mais perigosa porque foi a primeira a ver o PR.
Comparação prática:
| Modelo | Prós | Contras | Quando usar |
|---|---|---|---|
| 1 aprovação | Rápido, adequado a equipas pequenas, menos filas | Menos redundância humana | Default para produto |
| 2 aprovações | Mais olhos em risco alto, útil para compliance | Lento, cria bloqueios, dilui responsabilidade | Pagamentos, segurança, dados críticos |
| Sem aprovação com pair programming | Fluxo rápido, feedback imediato | Exige disciplina e registo | Spikes, hotfixes, trabalho em pares reais |
Uma opinião forte: se duas pessoas fizeram pair programming de verdade durante a implementação, não faz sentido exigir uma terceira pessoa para rever tudo por rotina. Nesse caso, o PR deve dizer “feito em par com X” e passar se CI estiver verde, excepto em áreas críticas.
O que deve ser automático e o que deve ser humano
Se um reviewer comenta “faltou ponto e vírgula”, “ordena os imports” ou “o Prettier mudou isto”, o processo está mal desenhado.
O mínimo aceitável em 2026 para uma equipa pequena:
- Formatação automática: Prettier 3.x, Black 24.x, gofmt, rustfmt, conforme a stack.
- Lint com regras acordadas: ESLint 9, Ruff, golangci-lint.
- Type checking: TypeScript 5.x com
strict, mypy ou equivalente quando fizer sentido. - Testes unitários e de integração relevantes.
- Build completo.
- Checks de segurança básicos: npm audit com critério claro, OSV Scanner ou Dependabot.
- Protecção de branch para impedir merge com CI vermelho.
Para Node.js 22 LTS e TypeScript, eu não deixaria um PR passar sem typecheck, lint e testes. O mesmo vale para migrations: se usas Prisma, Drizzle, Flyway ou Liquibase, a pipeline deve validar que as migrations aplicam numa base limpa.
O reviewer humano deve concentrar-se nisto:
- A alteração preserva invariantes do domínio?
- Há casos de erro tratados?
- Há idempotência onde é necessária?
- A migração é compatível com deploy sem downtime?
- O contrato da API mudou?
- Há efeitos secundários em filas, webhooks ou jobs?
- Os testes cobrem o risco real?
Um exemplo clássico: webhooks de pagamentos. O linter não vai perceber se processas o mesmo evento duas vezes. Um reviewer experiente deve perguntar: “isto é idempotente por event_id?” Para Stripe, por exemplo, a documentação oficial recomenda verificar assinaturas de webhooks e lidar com eventos repetidos. Isto não é detalhe. É produção.
Template de PR que obriga a pensar antes do review
Um bom template reduz reviews preguiçosos. Não precisa de ser longo. Aliás, se for longo, ninguém preenche bem.
Eu usaria algo deste género:
## O que mudou
Resumo curto da alteração.
## Como testar
Passos concretos ou comando executado.
## Risco
Baixo, médio ou alto. Explicar porquê.
## Dados e migrations
Há alteração de schema ou dados? Sim ou não.
## Contratos externos
Altera API, webhook, evento, fila ou integração? Sim ou não.
## Screenshots ou evidência
Quando aplicável.
Isto muda a conversa. Em vez de o reviewer tentar adivinhar tudo no diff, o autor explicita intenção e risco.
A secção “Como testar” é particularmente importante. “Testado localmente” não chega. Prefiro algo concreto:
- “Corri
npm test -- invoices.service.spec.ts.” - “Validei criação e cancelamento de subscrição em ambiente de staging.”
- “A migration foi aplicada numa cópia anonimizada com 8GB.”
- “Não testei em mobile, só desktop.”
A última frase parece má, mas é boa. Honestidade operacional é melhor do que confiança falsa.
Outra prática útil: labels de risco.
risk:low: UI pequena, copy, alteração isolada.risk:medium: lógica de negócio, endpoints, queries.risk:high: dados, auth, pagamentos, infra, migrations.
Com isto, a equipa deixa de tratar todos os PRs como iguais. Uma alteração de copy no frontend não deve ter o mesmo processo que uma alteração em permissões.
Fluxo diário: SLAs, ownership e bloqueios
Sem SLA, code review vira dívida invisível. Toda a gente “vai ver daqui a pouco” e ninguém vê.
Para uma equipa de 3 a 5 developers, recomendo estas regras:
- PR aberto até às 15h deve receber primeira resposta no próprio dia.
- PR pequeno deve ser revisto em menos de 4 horas úteis.
- PR normal deve ser revisto em menos de 24 horas úteis.
- PR parado há mais de 24 horas aparece no standup.
- PR sem resposta há mais de 48 horas pode ser escalado sem drama.
Isto não significa interromper trabalho profundo a cada 10 minutos. Significa criar janelas. Por exemplo, cada developer revê PRs às 11h30 e às 16h30. Duas janelas por dia chegam para equipas pequenas.
Também gosto de uma regra simples: quem abre PR deve escolher reviewer. Não atirar para “a equipa”. Responsabilidade difusa é uma fábrica de atrasos.
Se houver especialização forte, usa rotação controlada:
| Dia | Reviewer primário |
|---|---|
| Segunda | Developer A |
| Terça | Developer B |
| Quarta | Developer C |
| Quinta | Developer A |
| Sexta | Developer B |
Isto funciona quando há muitos PRs pequenos. Se a equipa tem poucos PRs, escolher reviewer por contexto é melhor.
O standup deve incluir só três perguntas sobre PRs:
- Que PRs estão bloqueados?
- Que PRs precisam de decisão técnica?
- Que PRs estão grandes demais e devem ser partidos?
Não transformes o standup numa leitura de Jira. O objectivo é fluxo.
Armadilhas comuns que aparecem em produção
A primeira armadilha é rever tarde demais. Se a decisão arquitectural está errada, descobrir isso quando o PR tem 900 linhas é caro. Para trabalho incerto, faz review antes do código: uma nota técnica curta, um diagrama, ou um spike de 2 horas.
A segunda é confundir “comentário” com “pedido obrigatório”. Usa linguagem explícita:
- “blocking”: tem de mudar antes do merge.
- “non-blocking”: sugestão, pode ficar para depois.
- “question”: preciso de perceber melhor.
Isto reduz fricção. Sem esta distinção, autores interpretam tudo como crítica obrigatória e reviewers acham que foram ignorados.
A terceira é discutir estilo no PR. Estilo deve estar em guias curtos e ferramentas. Se a equipa ainda discute nomes de pastas em cada PR, falta uma decisão base.
A quarta é aprovar sem correr a aplicação. Nem sempre é preciso testar manualmente, mas para alterações visuais, fluxos de pagamento, permissões ou migrations, só olhar para diff é insuficiente. Um reviewer deve pedir evidência: screenshot, vídeo curto, logs de staging, output de testes, plano de rollback.
A quinta é fazer squash ou rebase sem perceber impacto. Squash merge é bom para histórico limpo, mas pode esconder commits que documentavam fases importantes de uma migração. Merge commits preservam contexto, mas sujam histórico em equipas que não disciplinam branches. Rebase deixa histórico linear, mas pode ser perigoso se a equipa não domina conflitos.
Comparação honesta:
| Estratégia | Prós | Contras |
|---|---|---|
| Squash merge | Histórico limpo, fácil reverter PR inteiro | Perde granularidade dos commits |
| Merge commit | Preserva história completa | Histórico ruidoso |
| Rebase merge | Linear e legível | Exige cuidado com conflitos |
Para equipas pequenas de produto, eu escolheria squash merge por defeito, com títulos de PR bem escritos. Excepção: migrações complexas ou trabalho faseado onde a granularidade dos commits ajuda auditoria.
Checklist final para começares esta semana
Não precisas de um processo pesado. Precisas de regras pequenas, aplicadas sempre.
Começaria por isto:
- Activar branch protection na branch principal.
- Exigir CI verde antes de merge.
- Exigir 1 aprovação por defeito.
- Criar CODEOWNERS para áreas críticas.
- Adoptar template curto de PR.
- Definir SLA de primeira resposta.
- Medir tempo até merge durante 2 semanas.
- Partir PRs acima de 500 LOC, salvo justificação.
- Marcar comentários como blocking ou non-blocking.
- Fazer review antecipado para decisões técnicas grandes.
As ferramentas podem ser GitHub, GitLab ou Bitbucket. O princípio é o mesmo. GitHub Actions, GitLab CI ou CircleCI também servem, desde que a pipeline demore pouco. Se a pipeline demora 35 minutos, developers vão tentar contorná-la. Aponta para menos de 10 minutos nos checks principais. Testes longos podem correr em nightly ou em fases específicas, mas o gate principal tem de dar feedback rápido.
Conclusão: code review numa equipa pequena deve ser leve, explícito e focado no risco. Uma aprovação bem feita, CI decente e PRs pequenos batem processos pesados quase sempre. O pior sistema é aquele que parece rigoroso, mas toda a gente contorna porque atrasa.
Se estás a enfrentar um problema parecido, marca uma conversa em https://impact-origin.com/agendamento.