Codebase Design
Vocabulário compartilhado para desenhar módulos profundos. Use quando o usuário quiser desenhar ou melhorar a interface de um módulo, encontrar oportunidades de aprofundamento, decidir onde fica um seam, tornar o código mais testável ou navegável por IA, ou quando outra skill precisar do vocabulário de módulo profundo.
Codebase Design
Origem: importada de mattpocock/skills (
engineering/codebase-design), adaptada ao vocabulário deste harness.
Desenhe módulos profundos: muito comportamento atrás de uma interface pequena, posicionada num seam limpo, testável através dessa interface. Use esta linguagem e estes princípios sempre que código estiver sendo desenhado ou reestruturado. O objetivo é leverage para quem chama, localidade para quem mantém, e testabilidade para todo mundo.
Quando ler cada arquivo
| Tarefa | Arquivo |
|---|---|
| Aprofundar um cluster de módulos rasos dado suas dependências | DEEPENING.md |
| Explorar interfaces alternativas com subagentes paralelos ("design it twice") | DESIGN-IT-TWICE.md |
Glossário
Use estes termos exatamente: não substitua por "component", "service", "API" ou "boundary". Linguagem consistente é o ponto inteiro.
Módulo: qualquer coisa com uma interface e uma implementação. Deliberadamente agnóstico de escala: uma função, classe, pacote, ou fatia que atravessa camadas. Evite: unit, component, service.
Interface: tudo que quem chama precisa saber para usar o módulo corretamente: a assinatura de tipos, mas também invariantes, restrições de ordem, modos de erro, configuração obrigatória e características de performance. Evite: API, assinatura (estreito demais, referem-se só à superfície no nível de tipos).
Implementação: o que está dentro de um módulo, seu corpo de código. Distinto de Adapter: uma coisa pode ser um adapter pequeno com implementação grande (um repositório Postgres) ou um adapter grande com implementação pequena (um fake em memória). Recorra a "adapter" quando o seam é o assunto; "implementação" nos outros casos.
Profundidade: leverage na interface. A quantidade de comportamento que quem chama (ou um teste) consegue exercitar por unidade de interface que precisa aprender. Um módulo é profundo quando uma grande quantidade de comportamento está atrás de uma interface pequena, raso quando a interface é quase tão complexa quanto a implementação.
Seam (Michael Feathers): um lugar onde você pode alterar comportamento sem editar naquele lugar; a localização onde a interface de um módulo vive. Onde colocar o seam é uma decisão de design própria, distinta do que fica atrás dele. Evite: boundary (sobrecarregado com o bounded context do DDD).
Adapter: uma coisa concreta que satisfaz uma interface num seam. Descreve papel (que encaixe preenche), não substância (o que tem dentro).
Leverage: o que quem chama ganha com a profundidade. Mais capacidade por unidade de interface que precisa aprender. Uma implementação se paga em N pontos de chamada e M testes.
Localidade: o que quem mantém ganha com a profundidade. Mudança, bugs, conhecimento e verificação se concentram num lugar só em vez de se espalhar entre quem chama. Corrige uma vez, corrigido em todo lugar.
Profundo vs. raso
Módulo profundo = interface pequena + muita implementação:
┌─────────────────────┐
│ Interface pequena │ ← Poucos métodos, params simples
├─────────────────────┤
│ │
│ Implementação profunda│ ← Lógica complexa escondida
│ │
└─────────────────────┘
Módulo raso = interface grande + pouca implementação (evite):
┌─────────────────────────────────┐
│ Interface grande │ ← Muitos métodos, params complexos
├─────────────────────────────────┤
│ Implementação fina │ ← Só repassa adiante
└─────────────────────────────────┘
Ao desenhar uma interface, pergunte:
- Dá pra reduzir o número de métodos?
- Dá pra simplificar os parâmetros?
- Dá pra esconder mais complexidade por dentro?
Princípios
- Profundidade é uma propriedade da interface, não da implementação. Um módulo profundo pode ser composto internamente de partes pequenas, mockáveis, trocáveis; elas simplesmente não fazem parte da interface. Um módulo pode ter seams internos (privados à sua implementação, usados pelos próprios testes) além do seam externo na sua interface.
- O teste da deleção. Imagine deletar o módulo. Se a complexidade some, era um pass-through. Se a complexidade reaparece em N chamadores, ela estava merecendo seu lugar.
- A interface é a superfície de teste. Quem chama e os testes cruzam o mesmo seam. Se você quer testar além da interface, o módulo provavelmente tem a forma errada.
- Um adapter é um seam hipotético. Dois adapters é um seam real. Não introduza um seam a menos que algo de fato varie através dele.
Desenhando para testabilidade
Boas interfaces tornam o teste natural:
-
Aceite dependências, não as crie.
// Testável function processOrder(order, paymentGateway) {} // Difícil de testar function processOrder(order) { const gateway = new StripeGateway(); } -
Retorne resultados, não produza efeitos colaterais.
// Testável function calculateDiscount(cart): Discount {} // Difícil de testar function applyDiscount(cart): void { cart.total -= discount; } -
Superfície pequena. Menos métodos = menos testes necessários. Menos parâmetros = setup de teste mais simples.
Relações
- Um Módulo tem exatamente uma Interface (a superfície que apresenta a quem chama e aos testes).
- Profundidade é uma propriedade de um Módulo, medida contra sua Interface.
- Um Seam é onde a Interface de um Módulo vive.
- Um Adapter senta num Seam e satisfaz a Interface.
- Profundidade produz Leverage para quem chama e Localidade para quem mantém.
Enquadramentos rejeitados
- Profundidade como razão linhas-de-implementação/linhas-de-interface (Ousterhout): recompensa inflar a implementação. Usamos profundidade-como-leverage em vez disso.
- "Interface" como a palavra-chave
interfacedo TypeScript ou os métodos públicos de uma classe: estreito demais: interface aqui inclui todo fato que quem chama precisa saber. - "Boundary": sobrecarregado com o bounded context do DDD. Diga seam ou interface.
Skills relacionadas
- Loop TDD que testa através do seam definido aqui:
tdd - Vocabulário de domínio e
CONTEXT.md:domain-modeling - Revisão de dois eixos pós-implementação:
code-review