programa-de-qualidade-ousterhout
O que ele faz
Use sempre que o código sendo escrito ou revisado cria ou altera uma fronteira — um novo módulo, classe, componente, helper, hook, serviço ou wrapper; qualquer extração ou centralização de código compartilhado; qualquer momento de "vamos tornar isso reutilizável" — e quando estiver explicitamente revisando, refatorando ou projetando um módulo. Avalia se uma abstração justifica sua existência: profundidade do módulo, se deve ocultar uma decisão de design, se código duplicado protege um invariante compartilhado ou apenas rima, se uma interface é estável. Protege contra SOLID/Clean Code mecânico que produz muitas classes superficiais. Também define o teste de custo para o leitor (código que é barato para humanos e agentes lerem e modificarem) e o procedimento para refatorar uma base de código existente para esse padrão.
A instalação abre esta ficha no seu app de desktop AgentsRoom. Se o app ainda não estiver instalado, você será levado à página de download.
SKILL.md
---
name: programa-de-qualidade-ousterhout
description: Use sempre que o código sendo escrito ou revisado cria ou altera uma fronteira — um novo módulo, classe, componente, helper, hook, serviço ou wrapper; qualquer extração ou centralização de código compartilhado; qualquer momento de "vamos tornar isso reutilizável" — e quando estiver explicitamente revisando, refatorando ou projetando um módulo. Avalia se uma abstração justifica sua existência: profundidade do módulo, se deve ocultar uma decisão de design, se código duplicado protege um invariante compartilhado ou apenas rima, se uma interface é estável. Protege contra SOLID/Clean Code mecânico que produz muitas classes superficiais. Também define o teste de custo para o leitor (código que é barato para humanos e agentes lerem e modificarem) e o procedimento para refatorar uma base de código existente para esse padrão.
---
# Programa de Qualidade Ousterhout
## Visão Geral
O trabalho de um módulo é esconder a complexidade por trás de uma interface pequena. A medida central é a **profundidade**: um módulo profundo oferece uma interface simples sobre uma funcionalidade substancial; a interface de um módulo raso é quase tão complexa quanto sua implementação, então ele não compensa. Complexidade é o que você sente quando uma mudança força você a entender ou mexer em código que não esperava — Ousterhout nomeia duas fontes: **dependências** (você não pode mudar A sem mudar B) e **obscuridade** (a informação importante não é óbvia).
Ousterhout sozinho diz como um bom módulo *parece* para você. Ele é mais forte combinado com algumas outras lentes que dizem onde as fronteiras pertencem e como avançar para elas com segurança. Esta skill é essa lente combinada.
## Onde as Revisões Realmente Erram
As duas falhas que esta skill existe para corrigir — observadas repetidamente em código escrito por agentes — estão no **remédio**, não no veredito de dividir/não dividir em si:
1. **A correção rasa.** Dadas seis conversões `as unknown as`, o revisor sem ajuda as centraliza em um helper genérico `castRows<T>()` — mais arrumado, mas a obscuridade permanece. A correção profunda são mapeadores tipados de linha→domínio com testes fixados primeiro (aplicando Parnas: uma conversão é o cheiro de uma fronteira faltando; Beck: prove o mapeamento antes de movê-lo). Arrumar um cheiro não é removê-lo.
2. **A extração reflexiva.** Dada a mesma lógica de atualização repetida em três componentes irmãos, todo revisor sem ajuda disse "extraia um helper compartilhado" — o reflexo DRY. A regra deste programa, estendendo Metz: espere pelo invariante, não pelo terceiro parecido — centralize quando o código protege uma regra compartilhada, não quando rima.
Quando você se pegar recomendando uma correção, passe-a por ambos: ela remove a obscuridade ou só a realoca, e a extração protege um invariante ou só deduplica uma forma?
## Porta de Proporcionalidade
Ignore a lente quando uma mudança não adiciona nenhum nome exportado/importável novo, não cria novo módulo/classe/componente/helper/hook/serviço/wrapper, e não centraliza nada. Renomeações puras, codemods mecânicos, edições de configuração/dados e correções de uma linha estão isentos. Em caso de dúvida, rode apenas os dois testes centrais (profundidade, invariante) e pare aí.
## A Regra Completa
Cada pedaço de código gerado ou revisado passa pela lente Ousterhout antes da tarefa ser considerada concluída — não apenas revisões explícitas de design — exceto mudanças abaixo da porta de proporcionalidade (sem nova fronteira, sem centralização: renomeações, codemods, edições de configuração). Dois testes: (1) **Profundidade** — uma nova interface deve esconder substancialmente mais do que expõe; uma interface tão complexa quanto o que ela envolve não compensa. (2) **Invariante** — extraia código compartilhado somente quando ele protege uma regra compartilhada, nunca porque três locais rimam; e uma correção deve remover obscuridade, não realocá-la (centralizar seis conversões em um helper ainda são seis conversões). Quando uma mudança cria ou remodela uma fronteira, primeiro encontre como um produto estabelecido resolve um problema dessa forma e escala e adote suas convenções a menos que haja uma razão declarada para não fazê-lo (um padrão lembrado do treinamento é uma alegação, não uma fonte), então rode as verificações abaixo.
## Quando Usar
- Decidir se uma nova classe/função/hook vale sua interface, ou é só um repasse raso.
- Um arquivo ultrapassa um limite de tamanho e você está decidindo *como* dividi-lo, não apenas que deve.
- Código repetido te tenta a extrair um helper compartilhado.
- Projetando ou revisando uma fronteira em torno de uma regra de negócio (uma checagem de escopo de autorização, uma regra de dinheiro/arredondamento, um guarda de transição de máquina de estados, uma regra de retenção de dados).
- Uma interface está prestes a crescer um parâmetro ou um caso especial.
- Levando uma base de código existente a este padrão — veja "Refatorando uma Base de Código Existente para Este Padrão" abaixo.
**Não para:** edições mecânicas triviais, ou quando uma convenção do projeto já dita a estrutura — veja a Porta de Proporcionalidade acima. Deferir para `karpathy-guidelines` para disciplina de mudanças cirúrgicas e uma skill de desenvolvimento orientado a testes para a rede de segurança de refatoração, quando disponíveis.
## As Lentes
Cada lente adiciona exatamente uma pergunta. Ousterhout é a espinha; as outras corrigem seus pontos cegos.
| Lente | A única pergunta que ela adiciona | Quando ela prevalece |
|---|---|---|
| **Ousterhout** — módulos profundos | Esta interface esconde mais do que expõe? | Espinha padrão. |
| **Parnas** — ocultação de informação | Qual decisão de design (propensa a mudar) este módulo esconde? | O *motivo* pelo qual um módulo deve ser profundo. Se não esconde nada que mude, a profundidade é cosmética. |
| **Brooks** — essencial vs acidental | Isso remove complexidade acidental, ou apenas realoca complexidade essencial do domínio? | Elimina "refatorações" que movem a bagunça sem reduzi-la. |
| **Evans** — Domain-Driven Design | Esta fronteira está nomeada na linguagem do domínio, não em linguagem genérica de utilitários? | Renomeie `utils`/`helpers` — nomeie a fronteira pelo invariante que este repositório realmente tem. |
| **Fowler** — refatoração / cheiros | Qual é o menor movimento seguro em direção a um design mais profundo? | Transforma "deveria ser mais profundo" em passos concretos por trás de testes aprovados. |
| **Beck** — design simples, teste primeiro | Eu provei o comportamento atual antes de aprofundar a costura? | Um freio para arquitetura prematura. Faça funcionar e teste primeiro, depois aprofunde a costura certa. |
| **Hickey** — simples vs fácil | Isso intercala conceitos não relacionados, ou é genuinamente um conceito só? | Um helper superficial geralmente é *fácil* (próximo, rápido), não *simples* (poucos conceitos intercalados). Prefira simples. |
| **Metz** — duplicação em vez de abstração errada | Este código repetido protege um invariante compartilhado, ou só parece igual (regra deste programa, estendendo Metz)? | Metz: duplicação é mais barata que abstração errada — insira de volta uma abstração errada em vez de forçá-la. Este programa a estende: **não** centralize porque repete; centralize só quando protege um invariante real. Tolere duplicação até o invariante se revelar. |
| **Lei de Hyrum** — comportamento observável | Chamadores dependerão de comportamento além do contrato desta interface? | Defende interfaces pequenas e estáveis: todo comportamento observável eventualmente se torna essencial. |
## A Receita de Combinação
Aplique nesta ordem — lentes posteriores só importam depois que as anteriores passam:
1. **Metz — o portão de admissão.** Esta fronteira/abstração merece existir? Regra deste programa, estendendo Metz: extraia só quando o código protege uma regra compartilhada — três semelhantes não são um invariante revelado. Se não, pare aqui.
2. **Parnas / Ousterhout** — Oculte a decisão volátil (escopo de autorização, regra de arredondamento, guarda de transição, regra de retenção) atrás de um módulo profundo.
3. **Evans** — Nomeie esse módulo na linguagem do domínio, não `utils`.
4. **Beck / Fowler** — Para código existente, fixe o comportamento atual com testes, depois refatore em pequenos movimentos seguros. Para código recém-gerado não há comportamento atual para fixar — escreva o teste que define o comportamento pretendido.
5. **Hickey** — Rejeite interfaces que misturam conceitos não relacionados só porque os fluxos parecem similares.
## O Anti-Padrão Estrutural
**SOLID / Clean Code mecânico produz módulos superficiais.** Uma leitura dogmática —
uma classe por responsabilidade, extraia toda função, mantenha tudo minúsculo —
gera um enxame de classes cujas interfaces são tão complexas quanto seus corpos. Quando
uma regra diz "divida isso", pergunte qual *decisão* a divisão esconde (Parnas) e
se esconde mais do que expõe (Ousterhout). Se não esconde nada que mude, não divida. Esse cuidado é mais importante sob pressão de refatoração ("limpe isso", "este arquivo está grande demais") — em análise calma, revisores já resistem; no meio da refatoração, com mandato para produzir mudança visível, é quando o enxame de arquivos superficiais é escrito.
## Erros Comuns
- **Dividir só pelo tamanho.** Um módulo de consulta de 400 linhas que esconde uma decisão coerente pode ser mais profundo que quatro módulos de 100 linhas que vazam as mesmas junções.
- **Nomear a divisão `helpers`/`utils`.** Se não consegue nomear na linguagem do domínio (Evans), a fronteira provavelmente está errada.
- **Extrair na segunda ocorrência.** Regra deste programa, estendendo Metz: espere pelo invariante, não pelo terceiro semelhante.
- **Aprofundar antes de fixar o comportamento.** Beck: sem teste provando o comportamento atual, uma refatoração de "aprofundamento" é uma reescrita.
- **Contar um repasse como módulo.** Um wrapper que apenas encaminha seus argumentos adiciona uma interface e não esconde nada — superficial por definição.
- **Confundir rima com invariante.** A melhor evidência de um invariante compartilhado é co-mudança: as cópias foram corrigidas ou alteradas juntas na história (o mesmo bug corrigido em dois lugares). Semelhanças que mudam independentemente são rimas; deixe-as duplicadas.
- **Ajustar um cheiro em vez de removê-lo.** Centralizar seis casts em um helper genérico é a versão arrumada da mesma obscuridade. A correção profunda nomeia a fronteira que o cast estava encobrindo.
## Custo para o Leitor: o Terceiro Teste
Profundidade e invariante decidem se uma fronteira deve existir. Custo para o leitor
decide se o código ao redor é barato de mudar. O próximo leitor, humano
ou agente, paga por cada linha que precisa carregar para mudar algo com segurança.
Agentes pagam em tokens e navegam por busca textual, leituras parciais e
loops de verificação de tipo/teste, então os mesmos defeitos custam mais para eles. Pergunte:
- **Encontrável?** Um nome por conceito, escrito da mesma forma em todos os lugares, acessível por busca em texto simples. Defeitos: nomes montados a partir de strings, ligação por efeito colateral de importação, cadeias de reexportação que escondem a definição, dois nomes para um conceito.
- **O leitor pode parar cedo?** O contrato fica no topo do arquivo ou acima da exportação: o que ele promete, o que esconde, o que nunca faz. Defeito: o contrato só pode ser derivado lendo o corpo.
- **Verificável por máquina?** Tipos precisos na entrada e saída de cada limite, para que uma verificação de tipo substitua a leitura dos chamadores. Defeitos: `any`, dicionários simples, flags booleanas cujo significado vive no corpo.
- **O acoplamento é visível?** Lugares que devem mudar juntos são reforçados (um tipo compartilhado, um teste, uma fonte única) ou, na falta disso, marcados em ambos os locais. A evidência de acoplamento oculto é a co-mudança no histórico que nada no código menciona.
- **Livre de ruído?** Sem comentários que repetem o código, sem código comentado, sem ramos mortos, sem comentários de histórico de mudanças, sem caminho obsoleto mantido ao lado de sua substituição.
- **Previsível?** O layout segue o padrão existente do repositório; o teste está onde o leitor vai procurá-lo e roda sozinho.
O tamanho do arquivo está deliberadamente ausente. Um arquivo muito grande é motivo para procurar uma segunda decisão oculta, nunca motivo para cortar: leitores podem buscar e ler um intervalo, e uma divisão que não esconde nada adiciona interfaces sem remover carga.
Para marcadores no código e um mapa do repositório, use `context-audit` onde disponível: sua âncora `AIDEV-NOTE:` (um fato não recuperável mais uma referência de proveniência, no máximo duas linhas, no local) é a convenção para acoplamento que não pode ser reforçado.
## Refatorando uma Base de Código Existente para Este Padrão
Um retrofit é julgado da mesma forma que código novo; o que difere é a ordem e a moderação. A maior parte de uma base de código deve ser deixada intacta.
1. **Censo, somente leitura.** Liste os limites (módulos, serviços, helpers compartilhados). Para cada registro: a decisão que ele esconde, ou "nenhuma"; tamanho da interface contra o corpo; parceiros de co-mudança do histórico; defeitos de custo para o leitor. Ainda não mude nada.
2. **Classifique por churn, não por feiura.** A prioridade é a frequência com que o código muda vezes o custo para lê-lo. Código frio que funciona permanece como está, por mais superficial que seja. A complexidade essencial do domínio permanece onde está (Brooks).
3. **Atribua um remédio por achado:**
- camada de passagem ou wrapper que não esconde nada: delete-o, os chamadores usam o que ele envolvia;
- abstração errada dobrada por flags e casos especiais: insira-a de volta (Metz), depois procure o verdadeiro invariante;
- irmãos superficiais que compartilham uma decisão: una-os atrás de uma interface;
- decisão vazada (chamadores conhecem o formato, a regra, o esquema): puxe-a para o módulo que a possui;
- nome genérico (`utils`, `helpers`, `manager`): renomeie para a decisão que esconde, ou dissolva-o em seus chamadores;
- limite não tipado: tipifique-o, e substitua casts pelo mapeador que eles estavam encobrindo;
- acoplamento oculto: reforce-o, ou marque ambos os locais;
- ruído: delete-o.
Rimas que mudam independentemente não recebem remédio.
4. **Fixe o comportamento primeiro.** Nenhum remédio começa até que um teste prove o comportamento atual do código que toca (Beck). Refatores preservam comportamento; uma mudança de comportamento é um commit separado.
5. **Divida o trabalho em unidades que um agente possa terminar sozinho.** Um limite por unidade. Cada unidade nomeia os arquivos que possui, o contrato que deve preservar e o comando que o prova sozinho. Nenhuma duas unidades concorrentes escrevem o mesmo arquivo; arquivos compartilhados (barrels, registros, tabelas de rotas) têm um único dono ou esperam integração. Mudanças de interface que várias unidades dependem aterrissam primeiro, como sua própria unidade.
6. **Meça o resultado.** Escolha uma mudança representativa antes de começar e conte os arquivos e linhas que um leitor deve carregar para fazê-la; conte novamente depois. Nomes exportados e linhas totais devem cair ou se manter. Um refator que adiciona interfaces deve justificar o motivo.
7. **Pare** quando o que resta for frio, essencial ou uma rima.
Skills relacionadas, onde disponíveis: `repo-review` (tipo de design) produz o censo como artefato apenas para aconselhamento; `design-cleanup` executa o ciclo de corrigir e reanalisar para complexidade acidental; `context-audit` adiciona âncoras e o mapa do código; `ousterhout-build-deep` é a lista de verificação do autor para os agentes que fazem as unidades.
## Onde isso se encaixa
Esta skill é a camada de revisão e julgamento: use-a para decidir se uma abstração é profunda, nomeada para a decisão certa e vale a pena extrair. `find-shared-code` a usa como teste de admissão ao varrer o histórico recente em busca de código que vale a pena compartilhar. O Apêndice abaixo dá o raciocínio de cada autor.
---
## Apêndice: As Lentes em Profundidade
O modo de falha que cada autor captura, e o único movimento que cada um oferece. A tabela acima é a referência rápida; este é o raciocínio por trás dela.
### Ousterhout — Módulos Profundos (a espinha)
*Uma Filosofia de Design de Software.*
- **Profundidade** = benefício (funcionalidade escondida) ÷ custo (complexidade da interface). Um módulo profundo esconde muito atrás de pouco. A interface de um módulo superficial é quase tão complexa quanto seu corpo, então não rende nada.
- **Complexidade** é qualquer coisa no sistema que dificulta entender ou modificar. Duas fontes:
- **Dependências** — você não pode mudar uma parte sem tocar outra.
- **Obscuridade** — a informação importante não é óbvia pelo código.
- **Sintomas:** amplificação de mudança (uma decisão, muitas edições), carga cognitiva (quanto você deve manter na cabeça), desconhecidos desconhecidos (você não sabe qual código uma mudança afetará).
- **Movimento chave:** puxe a complexidade *para baixo* — o módulo absorve o caso difícil para que os chamadores não precisem. Parâmetros de configuração e pass-throughs empurram a complexidade *para cima* para o chamador; isso é superficialidade.
Captura: interfaces que vazam sua implementação; helpers que não ajudam.
### Parnas — Ocultação de Informação (por que a profundidade importa)
*Sobre os Critérios a Serem Usados na Decomposição de Sistemas em Módulos (1972).*
- Decomponha em torno de **decisões de design que provavelmente vão mudar**, não em torno das etapas de
um cálculo. Cada módulo esconde uma dessas decisões.
- Este é o ancestral direto do módulo profundo. Um módulo é profundo *porque*
esconde uma decisão que, de outra forma, se espalharia pelos chamadores.
Armadilhas: um "módulo" que não esconde nada volátil — sua profundidade é cosmética. Pergunte:
o que muda por trás dessa interface que os chamadores nunca veem? Se a resposta for
"nada", o limite é decoração.
### Brooks — Complexidade Essencial vs Acidental
*No Silver Bullet.*
- Complexidade **essencial** é inerente ao domínio (a avaliação realmente é tão
intrincada assim). Complexidade **acidental** é o que nossas ferramentas e estrutura impõem.
- Apenas a complexidade acidental é removível. Um refatoramento que "limpa" movendo
complexidade essencial do domínio de um arquivo para outro não fez nada.
Armadilhas: rearranjos disfarçados de simplificação. Pergunte: a complexidade total caiu,
ou apenas mudou de lugar?
### Evans — Domain-Driven Design
*Domain-Driven Design.*
- Os limites devem ser nomeados na **linguagem ubíqua** do domínio, não em
termos genéricos de utilidade. Um módulo chamado `helpers` não nomeia nada; um módulo chamado
`AccessScope` ou `PricingPolicy` nomeia um invariante.
- Contextos delimitados evitam que invariantes de negócio vazem através das junções.
Armadilhas: decomposição correta com nomes sem sentido. Se você não consegue nomear o
módulo na linguagem do domínio, provavelmente cortou o limite no lugar errado.
### Fowler — Refatoração e Code Smells
*Refactoring.*
- Fornece os movimentos concretos, seguros e nomeados (Extract Function, Move Field, Replace
Conditional with Polymorphism) para ir do design atual para o mais profundo.
- Cada movimento preserva o comportamento e é pequeno, para que seja reversível.
Armadilhas: a lacuna entre "isso deveria ser mais profundo" e saber o próximo commit.
Ousterhout define o alvo; Fowler é o caminho.
### Beck — Design Simples, Test-First
*Test-Driven Development; XP.*
- Quatro regras do design simples, na ordem publicada por Beck: passa nos testes, sem
duplicação, revela intenção, menor número de elementos. Este programa segue a
reordenação posterior de Fowler/Haines — intenção antes da duplicação — porque
serve à regra invariante de extensão de Metz deste programa (veja Metz, abaixo):
não aja sobre duplicação até que possa nomear a intenção que ela protege.
- Test-first é um freio contra arquitetura prematura. Faça funcionar e prove
o comportamento *primeiro*, depois aprofunde a junção que os testes agora protegem.
Armadilhas: arquitetura construída antes do comportamento estar definido. Sem um teste provando o
comportamento atual, um refatoramento de "aprofundamento" é uma reescrita não verificada.
### Hickey — Simples vs Fácil
*Simple Made Easy.*
- **Simples** = não entrelaçado: um conceito, não misturado com outros (objetivo).
- **Fácil** = ao alcance, familiar, rápido de acessar (relativo a você).
- Os dois são independentes. Um helper superficial é geralmente *fácil* — rápido de escrever,
próximo — mas não *simples* se entrelaça preocupações não relacionadas.
Armadilhas: conveniência disfarçada de design. Prefira construções que mantenham conceitos
não entrelaçados mesmo quando uma entrelaçada é mais rápida de digitar.
### Metz — Prefira Duplicação a uma Abstração Errada
*"The Wrong Abstraction" (2016).*
- Duplicação é muito mais barata que a abstração errada. Uma abstração extraída
cedo demais força cada chamador futuro a se adaptar a suposições que nunca
foram verdadeiras para todos eles.
- Quando uma abstração se mostra errada, o remédio de Metz é incorporá-la de volta e deixar
a duplicação retornar, em vez de forçá-la a se encaixar em um caso para o qual nunca foi feita.
- **A regra deste programa, estendendo Metz: não centralize porque o código se repete.
Centralize quando isso proteger um invariante real e compartilhado.** Até que o invariante
se revele, tolere a duplicação.
Armadilhas: centralização excessiva — o helper compartilhado superficial que todos agora
devem contornar. Este é o contrapeso para um "DRY a qualquer custo" mecânico.
### Lei de Hyrum — Comportamento Observável se Torna Contrato
*"Com um número suficiente de usuários, todo comportamento observável do seu sistema
será dependido por alguém."*
- Seja o que for que uma interface *aconteça* fazer — ordenação, tempo, texto de erro — alguém
eventualmente vai depender. Então a superfície que você expõe é maior que a superfície
que você documentou.
- Isso apoia a preferência de Ousterhout por **interfaces pequenas e estáveis**: quanto menos
você expõe, menos pode se tornar estrutural por acidente.
Armadilhas: interfaces amplas que vão se ossificar. Cada observável extra vira
uma restrição futura.
### Como Eles se Encaixam
- **Parnas → Ousterhout:** esconda uma decisão volátil → o módulo é profundo.
- **Brooks:** confirme que a profundidade removeu complexidade em vez de apenas relocá-la.
- **Evans:** nomeie o limite na linguagem do domínio.
- **Beck → Fowler:** defina o comportamento, depois refatore em pequenos movimentos seguros.
- **Metz:** resista a centralizar até que o invariante seja real.
- **Hickey:** mantenha a interface para um conceito.
- **Hyrum:** mantenha essa interface pequena para que possa permanecer estável.
O perigo é misturar Ousterhout com uma leitura mecânica de SOLID ou Clean Code:
isso produz muitas classes e funções minúsculas com interfaces superficiais — o exato
oposto de módulos profundos. Ousterhout, com Metz como contrapeso, é o antídoto.
Tags
Para se aprofundar
Claude Ads: o skill do Claude Code que audita suas contas de anúncios
Claude Ads é um skill open source para o Claude Code: mais de 250 verificações no Google, Meta, LinkedIn, TikTok ou Amazon Ads, uma nota sobre 100 e um plano de ação priorizado, em cerca de dez minutos. Instalação, comandos, limites e como orquestrá-lo no AgentsRoom.
AGENTS.md: um único arquivo de contexto para todo agente de código (Codex, Antigravity, Claude)
AGENTS.md é o arquivo de instruções portátil que seus agentes de código leem antes de tocar no seu código. O que colocar nele, como ele se diferencia do CLAUDE.md e como manter um único contexto entre Codex, Antigravity e Claude.
Baixar AgentsRoom
Rode todos os seus agentes de IA, em todos os seus projetos, de uma única janela.
App complementar: acompanhe seus agentes em qualquer lugar
Use Claude, Codex, Antigravity CLI ou outro provedor de IA.
Envie bugs e pedidos direto para o seu backlog público.