Pular para o conteúdo
Engenharia de Software

Code Review em Sistemas Criticos: Checklist

Code review normal pega erro de estilo. Code review de verdade pega o bug que derruba o foguete. Esse checklist de 5 fases e baseado em licoes de desastres reais.

O review que nao foi feito

Code Review em Sistemas Criticos: Checklist. Code review normal pega erro de estilo. Code review de verdade pega o bug que derruba o foguete. Esse checklist de 5 fases e baseado em licoes de desastres reais.

Galera, vou ser direto: 90% dos code reviews que acontecem no mercado sao teatro. O dev abre o PR, olha por 5 minutos, deixa um comentario sobre indentacao, marca approved e vai almocar. Enquanto isso, um overflow silencioso, uma race condition ou uma premissa invalida passam direto pra producao.

Esse artigo e um checklist de 5 fases pra code review que realmente previne desastres. Nao inventei essas fases — elas vem de analise pos-mortem de bugs que custaram vidas e bilhoes de dolares.

Por que review normal nao basta

O review tipico foca no que e facil de ver: nomenclatura, formatacao, complexidade superficial. Isso e util, mas nao pega os bugs que realmente importam. Os bugs que derrubam sistemas estao em premissas invalidas, condicoes de borda nao testadas e interacoes entre modulos que ninguem olhou.

Estudo da Microsoft Research: reviews eficazes demoram entre 60 e 90 minutos por 200-400 linhas. Se voce revisa 2000 linhas em 15 minutos, nao ta revisando — ta dando carimbo.

O outro problema: revisores olham pro codigo e perguntam 'isso parece certo?'. Codigo que ja funcionou antes parece muito certo. O codigo do Ariane 4 que derrubou o Ariane 5 'parecia certo' porque tinha funcionado por anos. Review precisa perguntar 'isso funciona nesse contexto?' — pergunta completamente diferente.

Fase 1: Tipos e Boundaries

A primeira fase checa se os dados cabem nos recipientes. E aqui que voce pega overflow, truncation e erros de conversao.

Checklist de Tipos e Boundaries

  • Todos os inteiros tem tipo adequado ao range do dominio? (int16 vs int32 vs int64)
  • Existem conversoes de tipo (cast) no codigo? Cada uma foi validada com os valores extremos?
  • Operacoes aritmeticas podem estourar? Soma, multiplicacao e subtracao foram testadas com valores de borda?
  • Comparacoes entre signed e unsigned estao corretas?
  • Strings tem limite de tamanho definido? O que acontece se receber uma string de 10MB?
  • Arrays e buffers tem bounds checking? Indices sao validados antes do acesso?
  • Divisao por zero esta tratada em todos os caminhos?

Teria pego o Ariane 5? Sim. A conversao de float64 pra int16 sem checagem de range seria flagrada no item 2.

Fase 2: Concorrencia e Timing

Race conditions sao os bugs mais dificeis de encontrar em review porque o codigo 'parece funcionar' — e funciona, 99.9% das vezes. O 0.1% e quando mata gente (Therac-25).

Checklist de Concorrencia

  • Variaveis compartilhadas entre threads/processos sao protegidas por lock ou atomic?
  • A ordem de operacao importa? O que acontece se duas requisicoes chegarem ao mesmo tempo?
  • Existe TOCTOU (time-of-check-time-of-use)? O recurso verificado pode mudar entre a checagem e o uso?
  • Deadlock e possivel? Existe aquisicao de locks em ordem diferente?
  • Timeouts estao definidos? O que acontece se um recurso externo nao responder nunca?
  • Operacoes que precisam ser atomicas sao de fato atomicas? (ex: ler-modificar-escrever)

Teria pego o Therac-25? Sim. A race condition entre o teclado do operador e a atualizacao do colimador seria flagrada nos itens 1 e 2.

Fase 3: Tratamento de Erros

A pergunta aqui nao e 'o happy path funciona?' — e 'o que acontece quando da errado?'. Porque vai dar errado.

Checklist de Tratamento de Erros

  • Toda excecao e tratada ou propagada de forma explicita? Existe catch generico que engole erros?
  • Falha parcial ta tratada? Se 3 de 5 operacoes funcionam e a 4a falha, o sistema fica em estado consistente?
  • O sistema falha de forma segura (fail-safe)? Um crash deixa o sistema em estado perigoso?
  • Erros sao logados com contexto suficiente pra diagnostico? (nao so 'erro desconhecido')
  • Recursos (file handles, connections, locks) sao liberados em todos os caminhos de erro?
  • Retry logic tem limite e backoff? Retry infinito pode causar cascade failure?

Teria pego o Ariane 5 de novo? Sim. A excecao de overflow nao era tratada — o sistema simplesmente desligava em vez de entrar em modo seguro.

Fase 4: Edge Cases e Premissas

Essa e a fase mais importante e a menos feita. Aqui voce questiona as premissas implicitas do codigo — as coisas que o autor assumiu como verdade e nao documentou.

Checklist de Edge Cases e Premissas

  • Quais premissas esse codigo faz sobre os inputs? Estao documentadas? Sao validas nesse contexto?
  • O codigo foi escrito pra outro contexto (reutilizado)? As premissas do contexto original ainda valem?
  • O que acontece com input nulo, vazio, negativo, zero, maximo ou malicioso?
  • Existe dependencia de ordem de chamada que nao esta documentada?
  • O codigo assume que uma API externa retorna dados em formato especifico? E se o formato mudar?
  • Unidades estao consistentes entre modulos? (metros vs feet, celsius vs fahrenheit, newtons vs libras)

Teria pego o Mars Climate Orbiter? Sim. Item 6 — unidades inconsistentes entre o modulo da Lockheed Martin e o da NASA. Teria pego o Ariane 5? Sim. Item 2 — codigo reutilizado sem revisao de premissas.

Fase 5: Compatibilidade e Integracao

A ultima fase olha pra como o codigo interage com o mundo externo: outros modulos, APIs, banco de dados, sistema operacional.

Checklist de Compatibilidade

  • A mudanca e backward-compatible? Consumidores existentes vao quebrar?
  • Contratos de API (request/response) estao documentados e validados?
  • O deploy pode ser feito de forma gradual (canary/staged)? Existe rollback?
  • Feature flags antigas foram removidas? (Knight Capital reutilizou uma flag e perdeu US$ 440 milhoes)
  • Dependencias foram atualizadas? Existe dependencia com vulnerabilidade conhecida?
  • A mudanca funciona em todos os ambientes? (dev, staging, prod podem ter configuracoes diferentes)

Teria pego a Knight Capital? Sim. Item 4 — a feature flag reutilizada sem limpeza do codigo legado seria flagrada.

CI/CD como gate obrigatorio de review

Checklist humano e bom, mas gente esquece. O ideal e automatizar tudo que da pra automatizar no CI/CD e deixar o humano focado no que maquina nao pega: logica de negocio e premissas.

  1. Analise estatica obrigatoria no PR: SonarQube, semgrep ou CodeQL bloqueiam merge se acharem issue critica
  2. Testes unitarios com cobertura minima de branches (nao so linhas). MC/DC pra codigo critico
  3. Testes de integracao entre modulos automatizados
  4. Testes de mutacao periodicos pra medir qualidade dos testes (nao so cobertura)
  5. SAST e DAST pra seguranca: Snyk, Trivy, OWASP ZAP
  6. Linting de types e conversoes: habilite todas as warnings do compilador como erros
  7. Staged deployment automatico: 1% -> 10% -> 50% -> 100% com metricas de erro entre cada passo

Num mundo perfeito, nenhum PR vai pra producao sem passar por analise estatica, testes automatizados e staged rollout. Na pratica, comece com o que der e va adicionando gates com o tempo.

Ferramentas de analise estatica por linguagem

SonarQube / SonarCloud

Analisa 25+ linguagens. Detecta code smells, vulnerabilidades e bugs. Versao gratis pra open-source. Integra com CI/CD.

semgrep

Analise semantica com regras customizaveis. Entende o codigo em vez de so fazer pattern matching. Otimo pra regras de seguranca do time.

CodeQL (GitHub)

Cria banco de dados do codigo e executa queries. Muito poderoso pra vulnerabilidades de seguranca. Gratis pra repos publicos.

Coverity (Synopsys)

Padrao da industria em aeroespacial e automotivo. Taxa de falso-positivo muito baixa. Comercial.

clippy (Rust)

Linter com centenas de verificacoes que vao muito alem de estilo. Detecta overflow, patterns inseguros e anti-patterns.

ESLint + plugins de seguranca

Com eslint-plugin-security e no-secrets, detecta problemas comuns em JS/TS. Combine com TypeScript strict mode.

Cultura de review que funciona

Checklist e ferramenta sao metade da equacao. A outra metade e cultura. Se o time trata review como obrigacao chata que atrasa o sprint, nenhum checklist vai funcionar.

Nas equipes que produzem software confiavel, review e tratado como a atividade de maior valor do dia. Encontrar um bug em review custa 10x menos que encontrar em QA e 100x menos que encontrar em producao. Isso nao e exagero — e dado da IBM System Sciences Institute.

  1. Nunca aprove um PR que voce nao entendeu. Se nao entendeu, peca explicacao ou passe pra quem entende
  2. PRs pequenos: maximo 400 linhas por review. PRs maiores devem ser divididos
  3. Tempo dedicado: bloqueie 1-2 horas por dia no calendario so pra review. Nao e interrupcao, e trabalho de engenharia
  4. Rotacao de revisores: evite que sempre a mesma pessoa revise o mesmo modulo. Diversidade de olhar pega mais bugs
  5. Review adversarial: pense 'como esse codigo pode falhar?' em vez de 'isso parece certo?'
  6. Sem ego: review e sobre o codigo, nao sobre a pessoa. Comentarios devem ser construtivos e especificos

Template de review pra copiar

Copie e cole esse template no seu PR template do GitHub. Ele forca o revisor a pensar em cada fase antes de aprovar:

markdown
## Review Checklist

### Tipos e Boundaries
- [ ] Tipos adequados ao range do dominio
- [ ] Conversoes de tipo validadas com valores extremos
- [ ] Aritmetica checada contra overflow

### Concorrencia
- [ ] Recursos compartilhados protegidos
- [ ] Sem TOCTOU
- [ ] Timeouts definidos

### Erros
- [ ] Falha segura (fail-safe)
- [ ] Recursos liberados em todos os paths
- [ ] Logs com contexto

### Premissas
- [ ] Premissas do codigo documentadas e validas nesse contexto
- [ ] Edge cases testados (null, vazio, max, negativo)

### Integracao
- [ ] Backward-compatible
- [ ] Feature flags limpas
- [ ] Deploy gradual possivel

Simples assim. Nao precisa de ferramenta cara nem de processo complicado. Precisa de disciplina pra checar cada item em cada PR. Os desastres que mostramos nesse artigo teriam sido evitados com checklists menos completos que esse.

Perguntas frequentes

Esse checklist e so pra sistemas criticos?

As 5 fases se aplicam a qualquer software. Claro, em sistemas criticos o rigor e maior — voce nao pode aprovar sem cobrir todos os itens. Em um SaaS comum, nem todo item se aplica a todo PR. Mas os principios (checar tipos, concorrencia, erros, premissas e integracao) valem pra qualquer projeto.

Analise estatica substitui code review humano?

Nao. Ferramentas encontram bugs mecanicos: null dereference, resource leaks, overflow obvio. Ferramentas nao encontram: logica de negocio incorreta, premissas invalidas sobre o dominio, decisoes de design ruins. Voce precisa dos dois.

Como convencer o time a adotar esse processo?

Mostre o custo de nao fazer. Um bug em producao custa 100x mais que um bug encontrado em review (dado IBM). Comece pequeno: adicione o template de checklist no PR template e peca que cada revisor marque os itens. A mudanca de cultura vem com o tempo.

Perguntas frequentes

Code review realmente previne desastres de software?

Sim, mas so quando feito de forma estruturada. Estudos da IBM mostram que code review encontra entre 60% e 90% dos defeitos. O problema e que a maioria das equipes faz review superficial focado em estilo. Os desastres do Ariane 5 e Boeing 737 MAX passaram por review — mas o review nao checou as coisas certas.

Quanto tempo deve durar um code review?

Pesquisas da SmartBear mostram que revisores perdem eficacia apos revisar mais de 200-400 linhas por hora ou mais de 60-90 minutos seguidos. PRs grandes devem ser divididos. Uma sessao eficaz raramente dura mais de uma hora.

Qual a diferenca entre code review e analise estatica?

Analise estatica e automatizada e encontra bugs mecanicos como null dereference, resource leaks e overflow. Code review humano encontra problemas de logica de negocio, premissas invalidas e design inadequado. Os dois sao complementares — nenhum substitui o outro.

Preciso de checklist diferente pra cada linguagem?

O checklist de logica e premissas e universal. Mas sim, cada linguagem tem armadilhas especificas que merecem itens dedicados. C/C++ precisa checar overflow e gerenciamento de memoria. Java precisa checar null safety. JavaScript precisa checar coercao de tipos. O checklist base serve pra todas, com extensoes por linguagem.

Por que review normal nao basta

O review tipico foca no que e facil de ver: nomenclatura, formatacao, complexidade superficial. Isso e util, mas nao pega os bugs que realmente importam. Os bugs que derrubam sistemas estao em premissas invalidas, condicoes de borda nao testadas e interacoes entre modulos que ninguem olhou. Estudo da Microsoft Research: reviews eficazes demoram entre 60 e 90 minutos por 200-400 linhas. Se voce revisa 2000 linhas em 15 minutos, nao ta revisando — ta dando carimbo. O outro problema: revisores olham pro codigo e perguntam 'isso parece certo?'. Codigo que ja funcionou antes parece muito certo. O codigo do Ariane 4 que derrubou o Ariane 5 'parecia certo' porque tinha funcionado por anos. Review precisa perguntar 'isso funciona nesse contexto?' — pergunta completamente diferente.