O Custo Oculto do Código que "Funciona"

Workflow Sistemático de Revisão de Código com IA: Planejar, Gerar, Validar

Guia prática para manter a qualidade do código na velocidade da IA


Introdução: O Custo Oculto do Código que “Funciona”

O código a seguir gera um endpoint de registro de usuários. Compila corretamente, passa nos testes básicos e funciona em staging:

app.post('/api/register', async (req, res) => {
  const { email, password, username } = req.body;
  
  const user = await db.query(
    `INSERT INTO users (email, password, username) 
     VALUES ('${email}', '${password}', '${username}') RETURNING *`
  );
  
  res.json({ success: true, user });
});

:warning: Problemas críticos:

  • Vulnerabilidade de injeção SQL
  • Senha armazenada em texto plano
  • Sem validação de entrada
  • Sem tratamento de erros
  • Sem proteção contra duplicatas

Este é o resultado típico quando se gera código com IA sem um processo de validação. A IA otimiza para código que funciona, não para código correto.

A solução é implementar um workflow sistemático que separe a geração da validação.


O Workflow de Três Fases

workflow-principal

Princípio fundamental: Cada fase tem um propósito específico e utiliza ferramentas diferentes. A IA de geração (Cursor, Copilot, Claude) otimiza velocidade. A IA de revisão (Claude Code, CodeRabbit) otimiza segurança e qualidade.


Fase 1: Planejamento Estruturado

Um planejamento claro produz melhores resultados da IA generativa. Antes de escrever código, complete o seguinte template:

Template de Planejamento

Feature: [Nome do feature]
Propósito: [Uma sentença descrevendo o objetivo]

Entradas:
  - campo: tipo, regras de validação
  - campo: tipo, regras de validação

Saídas:
  Sucesso:
    - código HTTP, estrutura de resposta
  Erro:
    - código HTTP, estrutura de erro

Casos Edge:
  - O que acontece se...?
  - O que acontece se...?

Segurança:
  - Consideração 1
  - Consideração 2

Dependências:
  - Biblioteca/serviço requerido

Exemplo Aplicado: Registro de Usuário

Feature: User Registration Endpoint
Propósito: Criar novos usuários com validação completa e armazenamento seguro

Entradas:
  - email: string, formato RFC 5322, único em BD
  - password: string, mínimo 8 caracteres, pelo menos 1 número e 1 maiúscula
  - username: string, 3-20 caracteres alfanuméricos

Saídas:
  Sucesso:
    - 201 Created, { user: { id, email, username, createdAt } }
  Erro:
    - 400 Bad Request, { errors: [{ field, message }] }
    - 409 Conflict, { error: "O email já está registrado" }
    - 500 Internal Error, { error: "Erro interno do servidor" }

Casos Edge:
  - Email com formato válido mas domínio inexistente
  - Senha que atende requisitos mínimos mas é comum (123456Aa)
  - Username com caracteres Unicode que parecem alfanuméricos
  - Solicitações duplicadas simultâneas (race condition)

Segurança:
  - Hash de senha com bcrypt (cost factor 12)
  - Queries parametrizadas (prevenir injeção SQL)
  - Rate limiting no endpoint
  - Não revelar se email existe em mensagem de erro genérica
  - Sanitização de inputs antes de logging

Dependências:
  - bcrypt para hashing
  - zod para validação
  - express-rate-limit para throttling

Fase 2: Geração com Contexto

Com o template completo, constrói-se um prompt estruturado que inclui todos os requisitos.

Melhores Práticas de Geração

Prática Descrição
Uma tarefa por prompt Gerar um endpoint, componente ou função por vez
Contexto limpo Novo chat para cada tarefa, evitar contextos contaminados
Stack explícito Especificar versões, convenções e estrutura do projeto
Iteração deliberada 2-3 iterações refinando, não esperar perfeição inicial

Prompt Estruturado

Criar um endpoint Express + TypeScript para registro de usuários.

REQUISITOS TÉCNICOS:
- POST /api/v1/auth/register
- Validação com Zod
- Hash com bcrypt (12 rounds)
- Queries parametrizadas com pg (node-postgres)
- TypeScript strict mode

VALIDAÇÕES:
- email: formato RFC 5322
- password: mínimo 8 chars, 1 número, 1 maiúscula
- username: 3-20 chars alfanuméricos

RESPOSTAS:
- 201: { user: { id, email, username, createdAt } }
- 400: { errors: [{ field: string, message: string }] }
- 409: { error: string } para email duplicado
- 500: { error: string } genérico

SEGURANÇA:
- Não incluir senha em nenhuma resposta
- Mensagem genérica para email duplicado
- Logging sem dados sensíveis

ESTRUTURA DO PROJETO:
src/
  controllers/
  middleware/
  validators/
  types/

Fase 3: Validação Automatizada

O código gerado requer validação com uma segunda perspectiva especializada em segurança.

Arquitetura de Validação

arquitetura-validacao

Opção 1: Claude Code como Revisor de Segurança

Claude Code permite executar análises de segurança diretamente do terminal. Configuração recomendada:

Instalação e Setup

# Instalar Claude Code
npm install -g @anthropic-ai/claude-code

# Instalar ferramentas de análise
npm install -D eslint @eslint/js eslint-plugin-security
npm install -D @typescript-eslint/parser @typescript-eslint/eslint-plugin

Configuração ESLint para Segurança

// eslint.config.js
import security from 'eslint-plugin-security';
import tseslint from '@typescript-eslint/eslint-plugin';

export default [
  {
    plugins: {
      security,
      '@typescript-eslint': tseslint
    },
    rules: {
      'security/detect-object-injection': 'error',
      'security/detect-non-literal-regexp': 'error',
      'security/detect-unsafe-regex': 'error',
      'security/detect-buffer-noassert': 'error',
      'security/detect-child-process': 'warn',
      'security/detect-disable-mustache-escape': 'error',
      'security/detect-eval-with-expression': 'error',
      'security/detect-no-csrf-before-method-override': 'error',
      'security/detect-non-literal-fs-filename': 'warn',
      'security/detect-non-literal-require': 'warn',
      'security/detect-possible-timing-attacks': 'error',
      'security/detect-pseudoRandomBytes': 'error',
      'security/detect-sql-injection': 'error'
    }
  }
];

Prompt de Revisão para Claude Code

Criar um arquivo CLAUDE.md na raiz do projeto com instruções de revisão:

# Instruções de Revisão de Segurança

Ao revisar código, analisar sistematicamente:

## 1. Injeção
- [ ] SQL Injection: Se usam queries parametrizadas?
- [ ] NoSQL Injection: Se validam operadores do MongoDB?
- [ ] Command Injection: Se sanitizam inputs para exec/spawn?
- [ ] XSS: Se escapam outputs em templates?

## 2. Autenticação
- [ ] Senhas hasheadas com bcrypt/argon2 (cost >= 10)?
- [ ] Tokens com expiração apropriada?
- [ ] Rate limiting em endpoints de auth?

## 3. Autorização
- [ ] Verificação de ownership em recursos?
- [ ] RBAC/ABAC implementado corretamente?

## 4. Dados Sensíveis
- [ ] Secrets em variáveis de ambiente (não hardcoded)?
- [ ] Logging sem dados sensíveis?
- [ ] Respostas sem informação interna?

## 5. Dependências
- [ ] Executar: npm audit
- [ ] Verificar: nenhuma dependência deprecated

## Comandos de Análise
npx eslint --ext .ts,.js src/
npm audit --audit-level=moderate

Executar Revisão com Claude Code

# Navegar ao projeto
cd meu-projeto

# Iniciar revisão de segurança
claude-code

# Dentro de Claude Code, executar:
> Revise o arquivo src/controllers/auth.controller.ts 
> seguindo as instruções de CLAUDE.md. 
> Execute os comandos de análise e reporte vulnerabilidades.
```### Opção 2: CodeRabbit (Integração CI/CD)

CodeRabbit se integra diretamente com GitHub/GitLab para revisão automática em cada PR.

```yaml
# .github/workflows/code-review.yml
name: Automated Code Review

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  security-review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          
      - name: Install Dependencies
        run: npm ci
        
      - name: Security Audit
        run: npm audit --audit-level=moderate
        
      - name: ESLint Security
        run: npx eslint --ext .ts,.js src/
        
      # CodeRabbit se ativa automaticamente em PRs
      # após instalá-lo no repositório

Fluxo de Decisão de Segurança

checklist-segurança


Exemplo Completo: De Vulnerável a Seguro

:cross_mark: Código Vulnerável (Geração sem validação)

// ⚠️ NÃO USAR EM PRODUÇÃO - Apenas para demonstração
import express from 'express';
import { Pool } from 'pg';

const app = express();
const pool = new Pool();

app.post('/api/register', async (req, res) => {
  const { email, password, username } = req.body;
  
  // 🔴 SQL Injection
  const result = await pool.query(
    `INSERT INTO users (email, password, username) 
     VALUES ('${email}', '${password}', '${username}') 
     RETURNING *`
  );
  
  // 🔴 Senha na resposta
  res.json({ success: true, user: result.rows[0] });
});

Vulnerabilidades detectadas:

  1. Injeção SQL por concatenação de strings
  2. Senha armazenada em texto plano
  3. Senha retornada na resposta
  4. Sem validação de entrada
  5. Sem tratamento de erros
  6. Sem rate limiting

:white_check_mark: Código Seguro (Pós-validação)

// src/controllers/auth.controller.ts
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import bcrypt from 'bcrypt';
import { pool } from '../config/database';
import { AppError } from '../middleware/errorHandler';

// Esquema de validação
const registerSchema = z.object({
  email: z
    .string()
    .email('Formato de email inválido')
    .max(255, 'Email muito longo'),
  password: z
    .string()
    .min(8, 'Mínimo 8 caracteres')
    .regex(/[0-9]/, 'Deve conter pelo menos um número')
    .regex(/[A-Z]/, 'Deve conter pelo menos uma letra maiúscula'),
  username: z
    .string()
    .min(3, 'Mínimo 3 caracteres')
    .max(20, 'Máximo 20 caracteres')
    .regex(/^[a-zA-Z0-9]+$/, 'Apenas caracteres alfanuméricos')
});

// Tipos
interface UserResponse {
  id: string;
  email: string;
  username: string;
  createdAt: Date;
}

interface RegisterBody {
  email: string;
  password: string;
  username: string;
}

export const register = async (
  req: Request<{}, {}, RegisterBody>,
  res: Response,
  next: NextFunction
): Promise<void> => {
  try {
    // 1. Validar entrada
    const validation = registerSchema.safeParse(req.body);
    
    if (!validation.success) {
      const errors = validation.error.errors.map(err => ({
        field: err.path.join('.'),
        message: err.message
      }));
      res.status(400).json({ errors });
      return;
    }
    
    const { email, password, username } = validation.data;
    
    // 2. Hash da senha (fator de custo 12)
    const hashedPassword = await bcrypt.hash(password, 12);
    
    // 3. Query parametrizada (previne injeção SQL)
    const query = `
      INSERT INTO users (email, password_hash, username, created_at)
      VALUES ($1, $2, $3, NOW())
      RETURNING id, email, username, created_at as "createdAt"
    `;
    
    const result = await pool.query<UserResponse>(query, [
      email.toLowerCase(),
      hashedPassword,
      username
    ]);
    
    // 4. Resposta sem senha
    res.status(201).json({
      user: result.rows[0]
    });
    
  } catch (error: unknown) {
    // 5. Tratamento de erros específicos
    if (error instanceof Error && 'code' in error) {
      const pgError = error as { code: string };
      
      // Violação de restrição única (email duplicado)
      if (pgError.code === '23505') {
        // Mensagem genérica (não revelar se email existe)
        res.status(409).json({
          error: 'Não foi possível completar o registro'
        });
        return;
      }
    }
    
    // Log sem dados sensíveis
    console.error('Erro de registro:', {
      timestamp: new Date().toISOString(),
      path: req.path,
      // NÃO incluir: email, password, username
    });
    
    next(new AppError('Erro interno do servidor', 500));
  }
};
// src/routes/auth.routes.ts
import { Router } from 'express';
import rateLimit from 'express-rate-limit';
import { register } from '../controllers/auth.controller';

const router = Router();

// Rate limiting: 5 tentativas por IP a cada 15 minutos
const registerLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 5,
  message: { error: 'Muitas tentativas, tente novamente mais tarde' },
  standardHeaders: true,
  legacyHeaders: false
});

router.post('/register', registerLimiter, register);

export default router;

Checklist de Implementação

Utilize esta lista de verificação antes de cada deploy:

Pré-Geração

  • Template de planejamento preenchido
  • Casos extremos identificados
  • Requisitos de segurança documentados

Pós-Geração

  • Revisão manual da estrutura
  • Convenções do projeto respeitadas
  • Tipos TypeScript corretos

Validação Automatizada

  • npm audit sem vulnerabilidades críticas
  • ESLint security sem erros
  • Claude Code / CodeRabbit sem problemas de segurança

Segurança Específica

  • Queries parametrizadas (sem concatenação)
  • Senhas com hash (bcrypt >= 10 rounds)
  • Entradas validadas com esquema (Zod, Joi)
  • Erros sem informações sensíveis
  • Rate limiting em endpoints críticos
  • CORS configurado corretamente

Conclusão

O fluxo de trabalho de três fases transforma a geração de código com IA de um risco potencial em uma vantagem competitiva:

  1. Planejar antes de gerar reduz iterações e melhora a qualidade do output
  2. Gerar com contexto completo produz código mais próximo da produção
  3. Validar com ferramentas especializadas captura o que a geração omite

A chave está em reconhecer que a IA de geração e a IA de validação têm objetivos diferentes e complementares. Integrar ambas em um fluxo sistemático permite manter a velocidade de desenvolvimento sem sacrificar a segurança.


Recursos Adicionais


Publicado em yoDEV.dev - A comunidade de desenvolvedores da América Latina