Engineering

Como impedi que o Claude Code partisse a minha arquitetura com 18 testes em 0,4 segundos

Publicado em 21 de mai. de 2026·8 min de leitura
Como impedi que o Claude Code partisse a minha arquitetura com 18 testes em 0,4 segundos

Como impedi que o Claude Code partisse a minha arquitetura com 18 testes que correm em 0,4 segundos

Passei as últimas semanas a construir um boilerplate de produção para sistemas de AI Agent e IoT. FastAPI, asyncpg, LangGraph, MQTT, pgvector — uma stack complexa com fronteiras arquiteturais muito específicas que não podem ser violadas.

O problema: estava a usar Claude Code e Cursor para acelerar o desenvolvimento. E são brilhantes. Mas também são completamente agnósticos à arquitetura que tens na cabeça.

Deixa-me mostrar-te o tipo de coisa que acontece sem proteção.

O Problema

O meu sistema tem uma regra crítica: o IngestionService nunca pode importar SQLModel ou SQLAlchemy. É o hot path para ingestão de telemetria IoT — apenas SQL puro com asyncpg, zero overhead de ORM. Esta separação é intencional e está documentada em 20 páginas de ARCHITECTURE.md.

Numa sessão típica, pedi ao Claude Code para "refatorar o IngestionService para ser mais consistente com o resto do código base".

Resultado gerado:

# services/ingestion.py — gerado pelo Claude Code
from sqlmodel import Session  # ← VIOLAÇÃO CRÍTICA
from core.database import get_session

class IngestionService:
    async def ingest(self, payload, trace_id):
        async with get_session() as session:  # ← destrói o hot path
            session.add(SensorReading(**payload.dict()))
            await session.commit()

Tecnicamente correto. Arquiteturalmente catastrófico. A latência de escrita salta de 0,8ms para 4–6ms. A 5.000 mensagens por segundo, é a diferença entre um sistema que aguenta a carga e outro que colapsa.

O Claude Code não sabe disto. Não consegue saber. A arquitetura vive na minha cabeça e num documento de texto que pode ou não ter lido antes de gerar o código.

A Solução: Architecture Fitness Tests

A ideia não é nova — Martin Fowler escreve sobre "fitness functions" em Building Evolutionary Architectures. Mas a aplicação ao desenvolvimento assistido por IA é muito concreta: se o modelo vai ter permissão total de refactor, precisas de testes que falham imediatamente quando uma fronteira arquitetural é cruzada.

Não testes em runtime. Testes estruturais estáticos — análise AST do código Python, zero dependências externas, zero servidor a correr.

A suite completa corre em 0,4 segundos. Pre-commit, não post-deploy.

Um Exemplo Concreto

O teste mais importante no meu sistema:

# tests/test_architecture_fitness.py

def test_ingestion_service_never_imports_sqlmodel(self):
    """
    O IngestionService é o Hot Path. SQLModel (SQLAlchemy) nunca
    pode aparecer aqui. É a fronteira mais crítica do sistema.
    """
    if not INGESTION_SERVICE.exists():
        pytest.skip(f"IngestionService not yet created at {INGESTION_SERVICE}")

    violations = []
    forbidden = {"sqlmodel", "sqlalchemy", "SQLModel", "AsyncSession"}

    for imp in _get_imports(INGESTION_SERVICE):
        module = imp["module"]
        names = imp["names"]
        if any(module.startswith(f) for f in forbidden):
            violations.append(imp)
        if any(name in forbidden for name in names):
            violations.append(imp)

    assert not violations, _format_violation(
        violations,
        "IngestionService imported SQLModel/SQLAlchemy.\n"
        "  Fix: Use asyncpg raw SQL only in services/ingestion.py.\n"
        "  This is the Dual-Path contract. The hot path has zero ORM overhead."
    )

A função _get_imports usa o módulo ast da stdlib do Python para parsear o ficheiro sem o executar:

def _get_imports(filepath: Path) -> list[dict]:
    tree = ast.parse(filepath.read_text(encoding="utf-8"))
    imports = []
    for node in ast.walk(tree):
        if isinstance(node, ast.ImportFrom):
            module = node.module or ""
            imports.append({
                "module": module,
                "names": [alias.name for alias in node.names],
                "line": node.lineno,
                "file": str(filepath),
            })
    return imports

Zero imports externos. Zero servidor. Zero base de dados. Apenas Python.

As 8 Fronteiras que Testo

O meu sistema tem um contrato arquitetural de 7 camadas. Os testes cobrem as violações mais comuns que a IA gera:

1. Isolamento de Camadas — Import Guards

  • Workers MQTT nunca importam LangGraph
  • IngestionService nunca importa SQLModel
  • Agentes nunca escrevem diretamente via ORM
  • Routers nunca chamam o IngestionService diretamente

2. Integridade do Hot Path

  • IngestionService usa apenas strings SQL puras, nunca query builders
  • Tabelas do hot path nunca são definidas como modelos SQLModel

3. Contratos de Eventos

  • Publicações em Redis Stream usam o envelope canónico DomainEvent
  • Workers MQTT validam com Pydantic antes de publicar

4. Async Enforcement

  • Sem clientes HTTP síncronos (requests) em routers
  • Sem time.sleep() no IngestionService
  • Handlers de workers são async def

5. Contrato de Trace ID

  • IngestionService.ingest() aceita trace_id como parâmetro
  • Todos os eventos de domínio incluem trace_id

6. Convenção do Keyspace do Redis

  • Sem chaves Redis sem prefixo de namespace
  • checkpoint:{id}, stream:{name}, cache:{type}:{id}

7. Integridade de Migrations

  • Zero instruções DDL em código aplicacional

8. Topologia de Dependências Completa

  • Valida a matriz completa de imports proibidos numa única passagem

O Resultado em Produção

Quando dou ao Claude Code permissão total para refatorar qualquer ficheiro, o ciclo é:

Claude Code gera código
        ↓
pytest tests/test_architecture_fitness.py -v
        ↓ (0,4 segundos)
Vermelho → Claude Code corrige
        ↓
Verde → commit

O modelo nunca pode violar fronteiras arquiteturais sem que eu saiba imediatamente. Não porque seja adversarial — mas porque otimiza para consistência local, não para contratos globais.

Depois de adicionar estes testes, todas as violações arquiteturais desapareceram das code reviews. O Claude Code começou a gerar código compliant automaticamente porque o ficheiro de contexto CLAUDE.md descreve explicitamente o que os testes verificam.

CLAUDE.md — O Segundo Mecanismo

Os testes são enforcement. O CLAUDE.md é prevenção.

É um ficheiro na raiz do repositório que o Cursor e o Claude Code leem antes de gerar código. Contém os contratos em linguagem explícita com exemplos ✅ corretos vs ❌ errados:

## Hard Boundaries (Enforced by tests/test_architecture_fitness.py)

### 1. The Dual-Path Contract

services/ingestion.py → asyncpg ONLY
  ✅ await pool.acquire() → await conn.execute("INSERT INTO ...", ...)
  ❌ from sqlmodel import Session
  ❌ session.add() / session.commit()
  ❌ time.sleep() → use await asyncio.sleep()

A combinação de testes que falham + documentação explícita cria um ambiente onde a IA pode refatorar livremente com garantias estruturais.

Números Finais

Suite completa:        18 testes
Tempo de execução:     0,42 segundos
Dependências:          0 (Python stdlib + pytest)
Violações apanhadas
antes dos testes:      12+
Violações depois:      0

Conclusão

O desenvolvimento assistido por IA é genuinamente transformador para a produtividade. Mas cria uma nova categoria de risco: deriva arquitetural silenciosa. O modelo otimiza para o que vê, não para o que a arquitetura global exige.

Os Architecture Fitness Tests são a resposta. Não são difíceis de escrever — o módulo ast do Python faz todo o trabalho pesado. E o retorno é imediato: liberdade total para usar IA em tarefas de refactor sem ansiedade pelo que pode ter sido partido.

Se estás a construir um sistema distribuído com desenvolvimento assistido por IA, estes testes são a primeira coisa que devias escrever — não a última.


O boilerplate completo (FastAPI + asyncpg + LangGraph + MQTT + pgvector + Prometheus + Grafana) com os 18 testes, o CLAUDE.md e um ARCHITECTURE.md de 20 secções está disponível em murinelo.gumroad.com/l/pdmfvr