MÓDULO 1.5

🏗️ Arquitetura Saudável

Sair da bola de lama sem refatorar tudo. Identifique pontos de melhoria, ataque incrementalmente, mantenha decisões vivas.

8
Seções
35
Minutos
Inter
Nível
Prática
Tipo
1

🪣 Sintomas de ball of mud

"Big ball of mud" é o termo arquitetural para código onde nada tem lugar: responsabilidades misturadas, dependências circulares, módulos que crescem por acreção. Você reconhece pelo cheiro: cada mudança simples cascateia em três arquivos não relacionados.

✓ Código saudável

  • Cada módulo tem uma responsabilidade clara e nomeável
  • Dependências fluem em uma direção (alto → baixo nível)
  • Vocabulário consistente entre código, testes e docs
  • Duplicação é exceção e justificada
  • Você prevê onde uma feature nova vai morar
  • Trocar uma implementação não vaza para 10 lugares

✗ Ball of mud

  • "Helper" / "utils" / "core" gigantes sem foco
  • A importa B que importa A (ciclos)
  • O mesmo conceito tem 3 nomes (user / customer / account)
  • Copy-paste com pequenas mutações em vários lugares
  • Toda feature nova exige "tour" pra saber onde por
  • Mudar uma regra requer caçar 8 arquivos

Cheiros típicos (code smells arquiteturais)

  • God modules: um arquivo com 2.000 linhas que "todo mundo importa"
  • Shotgun surgery: uma mudança simples toca 15 arquivos
  • Feature envy: métodos que mexem mais nos dados de outra classe do que nos próprios
  • Magic strings / numbers espalhados sem constante central
  • Conditionals por tipo: if/elif gigante checando "type === 'X'"
  • Wrappers que só repassam: camadas que não decidem nada
Responsabilidade
Cada módulo faz uma coisa, bem nomeada.
Direção
Dependências sem ciclos.
Vocabulário
Um conceito = um nome.
Previsibilidade
Onde mora uma feature nova? Você sabe.
2

🔍 /improve-codebase-architecture

O comando /improve-codebase-architecture não pede pra "refatorar tudo". Ele faz a coisa mais útil: lista deepening opportunities — pontos onde o código pode ficar significativamente mais saudável, ordenados por impacto. A análise é informada pelo CONTEXT.md e pelos ADRs do projeto, então as sugestões respeitam o que já foi decidido.

⚙️ O que o comando faz

  • Lê o CONTEXT.md para entender o domínio e o vocabulário do projeto
  • Lê os ADRs para saber quais decisões arquiteturais estão vigentes
  • Varre a estrutura do código procurando cheiros que contradizem o domínio
  • Retorna uma lista priorizada de oportunidades, não um plano de rewrite
  • Cada item tem: descrição, impacto estimado, esforço, e ponto de partida

Saída de exemplo

terminal
$ /improve-codebase-architecture
# 5 deepening opportunities identified (sorted by impact)
[1] HIGH  src/billing/ — "materialization cascade" lives in 4 modules
       Impact:  unifica lógica fragmentada, fecha conceito do CONTEXT.md
       Effort:  2-3 dias  |  Risk: medium
       Start:   src/billing/cascade.py (extract; ADR-009 references this)
[2] HIGH  src/utils/helpers.py — 1,847 lines, 23 unrelated functions
       Impact:  quebra god module; melhora descoberta
       Effort:  1-2 dias  |  Risk: low (mostly mechanical)
       Start:   agrupar por consumer; mover; testar
[3] MED   "customer" vs "user" vs "account" usados intercambiavelmente
       Impact:  alinha código ao glossário do CONTEXT.md (Sec. 2.3)
       Effort:  1 dia    |  Risk: low
       Start:   renomear customer.* (canonical) e atualizar callsites
[4] MED   ciclo: api/orders → services/inventory → api/orders
       Impact:  remove ciclo; clarifica direção de dependência
       Effort:  meio dia |  Risk: low
       Start:   extract InventoryPort interface
[5] LOW   tests/ duplica fixtures de billing em 6 arquivos
       Impact:  reduz manutenção de testes
       Effort:  2h       |  Risk: very low
       Start:   conftest.py compartilhado por subdir

💡 Dica

Rode esse comando antes de planejar o próximo ciclo. Ele te dá uma lista honesta de débito arquitetural pra escolher 1 item por sprint — não pra atacar tudo de uma vez.

Informado
Lê CONTEXT.md e ADRs.
Priorizado
Impacto × esforço.
Acionável
Cada item tem ponto de partida.
Não-destrutivo
Sugere deepening, não rewrite.
3

🔭 /zoom-out — perspectiva ampla

Existe um padrão de falha comum: o agente entra em um arquivo, começa a otimizar uma função, e depois de 20 minutos está re-escrevendo lógica isolada — sem perceber que aquela função nem deveria existir nesse módulo. /zoom-out serve pra isso: forçar o agente a parar e pedir contexto mais alto antes de continuar.

1

Zoom-in inicial (detalhe)

O agente entra no arquivo X e começa a editar

Foco estreito é necessário pra fazer a mudança imediata, mas vira armadilha quando a função, o arquivo, ou até o módulo inteiro estão no lugar errado. O agente "afunda" sem perguntar se o problema é maior.

2

Zoom-out (panorama)

/zoom-out — pede contexto arquitetural

O comando sobe um nível: qual é a responsabilidade desse módulo? O que o CONTEXT.md diz? Onde essa lógica deveria morar? A pergunta certa muda de "como melhorar essa função" para "essa função pertence aqui?". Frequentemente a melhor edição é mover ou remover, não otimizar.

3

Zoom-in informado (detalhe com mapa)

Volta ao código, mas com a foto grande

Agora a edição local é guiada pela arquitetura. Você sabe o que NÃO fazer (criar acoplamento novo, duplicar lógica de outro módulo) e o que SIM fazer (extrair, mover, alinhar nomes ao glossário). O detalhe importa, mas serve à estrutura.

💡 Use /zoom-out quando…

  • • Você está editando o mesmo arquivo há >15 minutos sem progresso claro
  • • A mudança simples está virando 4 arquivos diferentes
  • • O agente está "consertando" algo que, na verdade, é sintoma de outro problema
  • • Antes de aceitar uma sugestão grande que toca múltiplos módulos
Para o foco
Quebra túnel de detalhe.
Reposiciona
Pergunta certa, não resposta rápida.
Usa CONTEXT.md
Domínio como bússola.
Volta melhor
Zoom-in 2 ≠ zoom-in 1.
4

🌱 Deepening opportunities, não rewrites

Deepening é um termo emprestado de Eric Evans (DDD): aprofundar a modelagem onde o domínio é mais importante, sem jogar fora o que já funciona. É o oposto do impulso "vamos re-escrever do zero" — que custa caro, raramente entrega o prometido, e descarta todo o aprendizado embutido no código atual.

✗ Rewrite destrutivo

  • "Vamos jogar fora e refazer com a arquitetura nova"
  • Branch paralelo de 3-6 meses sem deliver
  • Perde edge cases que o código atual já trata
  • Big bang merge → regressões silenciosas em massa
  • Time fica preso no novo, suporte do velho atrasa
  • Lições aprendidas (CONTEXT.md, ADRs) viram lixo

✓ Deepening incremental

  • "Vamos melhorar 1 oportunidade por sprint, sem parar deliver"
  • Cada PR é pequeno, revisável, mergeable
  • Mantém edge cases enquanto refina a forma
  • Regressões aparecem cedo, em mudanças pequenas
  • Time continua entregando feature ao lado
  • CONTEXT.md e ADRs evoluem junto, viram referência viva

📖 Termo: deepening

Deepening = aprofundar a modelagem do domínio incrementalmente. Em vez de re-escrever, você identifica conceitos que estão "tortos" no código (escondidos em utils, espalhados, mal nomeados) e os eleva a primeiro-classe — uma extração por vez.

Origem: Domain-Driven Design (Eric Evans, 2003) — "deepening the model".

Incremental
1 melhoria por sprint.
Sem branch longo
Merge contínuo.
Preserva
Edge cases já dominados.
Cumulativo
Arquitetura melhora visivelmente.
5

🧭 CONTEXT.md como guia

O CONTEXT.md não é só onboarding — ele é a régua arquitetural do projeto. A linguagem do domínio que ele descreve é o que orienta onde refatorar: módulos que implementam conceitos centrais merecem mais atenção arquitetural do que infraestrutura genérica.

🎯 Princípio: conceito de domínio = atenção arquitetural

Exemplo: se o CONTEXT.md descreve "materialization cascade" como conceito chave do sistema de billing — a cadeia pela qual mudanças de preço se propagam para contratos ativos — então o módulo que implementa essa cascade não pode estar escondido em utils/helpers.py dividido em 4 funções soltas.

O deepening óbvio aqui é extrair billing/cascade.py com a cascade como entidade nomeada, alinhada ao vocabulário do CONTEXT.md. O comando /improve-codebase-architecture vê esse desalinhamento porque ele lê o CONTEXT.md.

Perguntas que CONTEXT.md responde

  • Quais são os conceitos centrais? → eles merecem módulos próprios e nomes consistentes
  • Qual é o glossário canônico? → "customer" vs "user" se resolve aqui, não em PR review
  • Quais são os boundaries? → onde uma feature pertence é função do bounded context
  • Quais invariantes existem? → regras que NÃO podem ser violadas em refator

💡 Dica

Se o CONTEXT.md está vago ou desatualizado, essa é a primeira deepening opportunity. Sem ele, qualquer melhoria arquitetural vai ser local e perderá coerência.

Régua
Bate código contra domínio.
Glossário
Resolve disputa de nomes.
Boundaries
Onde a feature mora.
Invariantes
Regras intocáveis em refator.
6

📜 ADRs como memória arquitetural

ADR = Architecture Decision Record. Um documento curto por decisão arquitetural relevante. Sem ADRs, o próximo refator desfaz silenciosamente uma escolha consciente — porque ninguém lembra por que X foi separado de Y.

ADR-009 — exemplo

docs/adr/0009-separate-cascade.md
# ADR-009: Separar materialization cascade de pricing
## Status
Accepted — 2025-08-14
## Contexto
A "materialization cascade" propaga mudanças de preço para contratos
ativos. Originalmente vivia dentro de pricing/calculator.py porque
parecia "cálculo de preço". Na prática, são duas responsabilidades:
  - pricing/    → calcula o preço de UM item, sem estado
  - cascade/    → reage a mudanças, atualiza N contratos, com estado
Misturadas, qualquer mudança em pricing arriscava efeito colateral
em milhares de contratos ativos.
## Decisão
Extrair cascade para billing/cascade.py como entidade nomeada e
independente. pricing/ vira pure function. cascade/ orquestra.
## Consequências
+ pricing testável sem mock de DB
+ cascade tem ownership claro de invariantes (idempotência, ordem)
+ vocabulário do CONTEXT.md (Sec. 4) finalmente reflete o código
- duas pastas onde antes havia uma (custo de descoberta)
- imports adicionais entre os módulos (acoplamento explícito)
## Alternativas consideradas
- Manter junto: rejeitado, acopla cálculo a propagação
- Event bus: prematuro, sem necessidade de async hoje
- Inline em cada caller: rejeitado, duplicação garantida

🧠 Por que ADRs importam em deepening

  • • Sem ADR, daqui 6 meses alguém vai "consolidar" cascade de volta em pricing por parecer redundante
  • • O ADR é prova de que a separação já foi pensada e tem motivo
  • /improve-codebase-architecture lê ADRs antes de sugerir — não vai propor algo já rejeitado
  • • ADRs descontinuados também valem: registram aprendizado ("tentamos X, não funcionou porque Y")

💡 Dica: ADR curto > ADR perfeito

ADR de 1 página vence ADR de 10 páginas que ninguém escreve. Status, Contexto, Decisão, Consequências — quatro headings, parágrafos curtos. Numere sequencialmente (0001, 0002…) e não delete: marque como Superseded by ADR-XXX quando substituído.

Memória
Por que X ≠ Y, registrado.
Anti-regressão
Próximo refator não desfaz.
Curto
1 página > 10.
Imortal
Substitua, não delete.
7

📈 Exemplo de melhoria progressiva

Como uma equipe sai de ball of mud sem parar o produto. Narrativa de 3 sprints, baseada em padrão real: uma oportunidade por sprint, CONTEXT.md e ADRs atualizados a cada ciclo.

S0

Sprint 0 — Diagnóstico

Rodamos /improve-codebase-architecture pela primeira vez

Saída lista 5 oportunidades. Time discute em 30 min, escolhe atacar 3 nas próximas 3 sprints, na ordem de impacto. As outras 2 ficam no backlog explicitamente — não esquecidas, não urgentes.

S1

Sprint 1 — Extrair cascade

Oportunidade #1: materialization cascade espalhada

Time extrai billing/cascade.py como módulo nomeado. PR pequeno, ~400 linhas movidas, sem mudar comportamento. Escreve ADR-009 documentando a decisão. Atualiza CONTEXT.md (Sec. 4) com o vocabulário canônico. Features novas seguem acontecendo ao lado, sem branch paralelo.

S2

Sprint 2 — Quebrar god module

Oportunidade #2: utils/helpers.py com 1.847 linhas

Maioria mecânico: agrupa funções por consumer, move para módulos próximos. 3 funções "órfãs" levantam perguntas — o que são? Resposta vem do CONTEXT.md (uma é cascade, deveria ter ido na S1; outras duas expõem um conceito novo que vira ADR-010). Bug evitado.

S3

Sprint 3 — Alinhar vocabulário

Oportunidade #3: customer / user / account intercambiáveis

Renomeação guiada pelo glossário do CONTEXT.md. Mecânico, mas mexe em muitos arquivos — feature flag não ajuda aqui, então: PRs pequenos por subdir, com codeowner do subdir aprovando. Ao final, código fala a mesma língua que docs e times de produto.

S4

Sprint 4 — Re-diagnóstico

Roda /improve-codebase-architecture de novo

Lista nova: as 3 oportunidades originais sumiram (foram resolvidas). As 2 que ficaram no backlog continuam. Mas surgiram 2 novas, porque o domínio evoluiu nos 3 sprints. Arquitetura melhorou visivelmente, sem big bang, sem parar deliver.

📊 Resultado em 4 sprints

  • 3 oportunidades de alto impacto resolvidas, 2 ADRs novos
  • CONTEXT.md atualizado 3 vezes, virou referência viva
  • Zero pause em features de produto
  • Bug evitado (cascade órfã em helpers) encontrado por coincidência
  • Time confia no processo: próxima rodada já planejada
1 por sprint
Cadência sustentável.
Docs vivas
CONTEXT.md + ADRs evoluem.
Sem pause
Features continuam.
Cumulativo
Re-diagnóstico mostra ganho.
8

🎯 Exercício prático

Você não aprende deepening lendo. Aprende rodando o comando no seu projeto e defendendo a escolha da primeira oportunidade a atacar.

🛠️ Passos

  1. 1.
    Confirme as bases: seu projeto tem CONTEXT.md? Tem pelo menos 1-2 ADRs? Se não, comece por aí (esse é o pré-deepening).
  2. 2.
    Rode /improve-codebase-architecture no projeto real (não em sandbox).
  3. 3.
    Anote as 3 primeiras sugestões com: descrição, impacto, esforço. Não as 5 — só as 3 primeiras.
  4. 4.
    Qual você ataca primeiro? Pode ser a #1 — mas pode não ser. Defenda a escolha em 3 frases: por que essa, por que agora, qual o risco se deixar.
  5. 5.
    Bonus: rascunhe o ADR antes de mexer no código. Se você não consegue escrever 1 página justificando, talvez a sugestão não esteja madura ainda.

💡 Critério de escolha

Boa primeira escolha: alto impacto, esforço baixo-médio, risco baixo, toca conceito do CONTEXT.md. Má primeira escolha: alto impacto mas alto risco — guarde pra quando o time já confia no processo.

⚠️ Não faça

  • Atacar as 5 oportunidades de uma vez — você volta ao rewrite
  • Pular o ADR — daqui 6 meses ninguém lembra o motivo
  • Refatorar sem teste pré-existente cobrindo o caminho — escreva primeiro
Rode
No projeto real, não exemplo.
3, não 5
Foco na escolha.
Defenda
3 frases: essa, agora, risco.
ADR antes
Se não escreve, não está pronto.

Resumo do Módulo

Ball of mud é diagnosticável — cheiros específicos (god modules, ciclos, vocabulário inconsistente, duplicação).
/improve-codebase-architecture lista oportunidades — informadas por CONTEXT.md e ADRs, priorizadas por impacto.
/zoom-out quebra o túnel do detalhe — devolve a perspectiva arquitetural antes da próxima edição.
Deepening > rewrite — uma melhoria por sprint cumulativa, sem branch longo.
CONTEXT.md é a régua — conceito de domínio merece módulo, nome, e atenção arquitetural.
ADRs preservam decisões — sem eles, o próximo refator desfaz por desconhecimento.

Próximo Módulo:

1.6 — continuando a jornada de skills.