uma-filosofia-de-design-de-software

por Rob ZappSem instalaçõesSem likesAtualizado em 8 de outubro de 2026Categoria: Engenharia

O que ele faz

Use ao escrever, alterar ou revisar código sempre que a mudança adicionar um nome exportado ou importável, criar um módulo, classe, componente, helper, hook, serviço ou wrapper, centralizar código repetido ou alterar uma API. Regras de Ousterhout (módulos profundos, ocultação de informações, redução da complexidade) além do teste invariante para compartilhamento de código, o teste do custo para o leitor e uma nota de design obrigatória ao final.

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: uma-filosofia-de-design-de-software
description: Use ao escrever, alterar ou revisar código sempre que a mudança adicionar um nome exportado ou importável, criar um módulo, classe, componente, helper, hook, serviço ou wrapper, centralizar código repetido ou alterar uma API. Regras de Ousterhout (módulos profundos, ocultação de informações, redução da complexidade) além do teste invariante para compartilhamento de código, o teste do custo para o leitor e uma nota de design obrigatória ao final.
---

# A Filosofia do Design de Software (John Ousterhout)

## Quando usar esta habilidade

Use esta habilidade quando você estiver projetando, escrevendo, alterando ou revisando código. Ela se aplica ao design de módulos, mudanças em APIs, decomposição, refatoração, nomes, comentários, testes e trabalho de desempenho. Use-a também quando uma mudança parecer estranha ou quando uma alteração se espalhar por muitos arquivos.

## O viés a corrigir

Código que funciona não é o mesmo que código simples. Pequenos pedaços, padrões familiares, flags, wrappers e documentação extra podem tornar um design mais complexo. Eles fazem isso quando adicionam ao que o leitor precisa saber, ou quando vazam conhecimento para outros módulos.

## Regras de decisão

- Meça um design pela quantidade que ele reduz a complexidade. Prefira o design que diminui a carga do leitor. A complexidade tem quatro sinais. Uma mudança precisa de edições em muitos lugares. Dependências estão ocultas. Passos devem ocorrer em uma ordem fixa. O leitor deve manter muitos fatos em mente.
- Trate o design como um trabalho contínuo. Um primeiro patch que funciona não está terminado se ele torna mudanças posteriores mais difíceis. Para uma decisão sobre uma interface, uma divisão de módulo ou uma abstração, compare dois ou mais designs possíveis.
- Prefira módulos profundos. Um módulo profundo tem uma interface pequena e esconde uma grande quantidade de complexidade. Rejeite serviços de passagem, wrappers finos de biblioteca e pequenos módulos auxiliares. Rejeite qualquer extração que adicione nomes mas não reduza a carga do leitor.
- Projete uma interface em torno do que o chamador deve saber, não em torno de como a implementação funciona. Evite sequências frágeis de configuração, flags de modo, botões de configuração e argumentos que mostram escolhas internas.
- Oculte as decisões que podem mudar. Exemplos são representações internas, formato de armazenamento, protocolos, formatos de arquivo e truques de desempenho. Contabilidade, normalização e casos de borda são outros exemplos. Mantenha cada um dentro do módulo que detém o conhecimento.
- Puxe a complexidade para dentro do módulo que detém o detalhe. Aceite uma implementação mais complexa quando ela oferece aos chamadores um contrato mais simples e remove trabalho repetido de cada ponto de chamada.
- Faça um módulo geral no nível certo. Não ajuste um módulo para um único chamador. Não adicione uma abstração vaga para necessidades futuras. Mantenha casos raros de borda fora do caminho principal e coloque comportamentos especiais em seu próprio lugar.
- Una ou divida módulos pela complexidade total. Não una ou divida por tamanho, pela ordem em que o código é executado, por hábito ou por aparência. Mantenha estado, comportamento, regras e decisões relacionadas juntos. Divida-os somente quando o novo limite for mais profundo e um leitor puder entender cada lado sozinho.
- Faça o conjunto de exceções menor. Quando possível, mude a interface ou as regras para que estados inválidos não possam ocorrer. Não faça cada chamador repetir o mesmo código defensivo.
- Use comentários para reduzir a complexidade. Anote contratos de interface, regras que devem permanecer verdadeiras, decisões de design ocultas e suas razões. Também anote fatos difíceis que os chamadores não devem precisar saber. Não repita o código em um comentário. Não use um comentário para esconder um nome ruim, uma divisão ruim ou fluxo de controle confuso.
- Trate nomes, consistência e clareza como informações de design. Um nome diz ao leitor a abstração, não o mecanismo. Operações relacionadas usam as mesmas convenções. Código que surpreende o leitor adiciona complexidade, mesmo quando é curto.
- Escreva testes contra contratos públicos e APIs estáveis. Teste a complexidade oculta e os casos especiais através desses contratos. Não deixe que a facilidade de um teste force uma interface rasa ou vazada.
- Adicione uma mudança de desempenho, um padrão, um paradigma ou um framework apenas por uma de duas razões. Ela reduz a complexidade nesta base de código, ou evidências mostram que a troca é necessária. Oculte cada otimização atrás de uma interface estável.

## Sinais e a resposta a cada um

- Uma funcionalidade é estranha, ou uma mudança se espalha por arquivos, ou um revisor deve encontrar dependências ocultas. Resposta: procure por falta de ocultação de informação e módulos rasos. Também procure por passos em ordem fixa e por complexidade que os chamadores carregam.
- Você adiciona um módulo, camada, serviço, auxiliar, wrapper ou fachada. Ou você adiciona um padrão, opção, callback ou argumento. Resposta: mostre que ele esconde mais complexidade do que adiciona.
- Você muda uma API. Resposta: verifique o que um chamador normal deve saber. Um chamador não deve precisar da ordem das chamadas, da representação ou do armazenamento. Um chamador não deve precisar do transporte, do cache, do protocolo ou do formato de arquivo. Um chamador não deve precisar do fluxo interno de trabalho ou de muitos passos de configuração.
- Você adiciona um caso especial, uma flag, um caminho de exceção, uma condição ou um contêiner que os chamadores podem ver. Resposta: primeiro pergunte o que o módulo proprietário pode fazer em vez disso. Ele pode remover o estado inválido, isolar o comportamento incomum ou dar uma operação mais forte.
- Você divide código, extrai uma função ou adiciona uma variável. Resposta: verifique se o novo limite ou nome carrega significado. Ele não deve apenas adicionar saltos, estado que passa ou passos intermediários que os chamadores podem ver.
- O código tem fases como `prepare`, `process` e `finalize`, ou os chamadores devem construir objetos em etapas. Resposta: verifique se a ordem temporal é o conceito real. Se não for, organize o código em torno de responsabilidades estáveis.
- Um nome é vago, nomeia um mecanismo, não é consistente ou surpreende o leitor. Resposta: pense novamente sobre o limite da abstração. Não aceite um nome que seja quase correto.
- Um comentário é longo, repete o código, explica uma interface confusa ou mostra internos para explicar o uso. Resposta: mude a abstração ou mova o contrato faltante para a interface.
- Você otimiza desempenho. Resposta: meça primeiro, depois oculte a otimização. Não abra mão da profundidade do módulo ou da ocultação de informação sem evidências de que a troca é necessária.
- Você testa ou revisa. Resposta: olhe para o comportamento público e contratos de interface. Também olhe para a complexidade oculta por trás de APIs estáveis e para casos especiais mantidos atrás da abstração.

## Lista de verificação final

- A mudança reduz o esforço para entender, modificar, verificar e estender o sistema?
- Cada elemento de interface, wrapper, camada, helper, opção e nome esconde complexidade suficiente para justificá-lo?
- As decisões importantes estão em um só lugar? As dependências são visíveis? As restrições que os chamadores precisam estão documentadas? Os detalhes internos que podem mudar estão protegidos?
- Os casos comuns funcionam sem etapas extras? Controles raros, casos especiais, truques de desempenho e detalhes de exceção ficam fora do caminho comum?
- Os nomes são exatos e consistentes? Os comentários estão atualizados, sem repetição do código? O código segue as convenções existentes, a menos que novas informações justifiquem a mudança?

## Gate

Use a lista completa quando a mudança adicionar um nome que outro código possa exportar ou importar. Use também quando a mudança criar um módulo, classe, componente, helper, hook, serviço ou wrapper, ou colocar código repetido em um só lugar. Renomeações, codemods, mudanças de configuração, mudanças de dados e correções de uma linha não precisam disso.

## Teste invariante: compartilhe apenas código que muda junto

- Extraia código compartilhado apenas quando ele protege uma regra que você pode nomear. A evidência é a co-mudança: o histórico mostra que as cópias foram corrigidas ou alteradas juntas. Código que só parece similar e muda independentemente é uma rima. Deixe rimas como duplicatas. Três blocos similares não provam uma regra.
- Uma correção deve remover o problema, não movê-lo. Seis casts movidos para um helper genérico de cast ainda são seis casts. Escreva o mapper tipado que os casts estavam escondendo.
- Quando uma abstração está errada, coloque o código de volta inline e deixe a duplicação retornar. Não force a abstração com flags.
- Não divida código apenas por seu tamanho. Um módulo de 400 linhas que esconde uma decisão é melhor que quatro módulos de 100 linhas que vazam as mesmas junções.
- Uma leitura mecânica de Clean Code ou SOLID (funções muito pequenas, uma classe para cada responsabilidade) gera módulos superficiais. Esta skill tem prioridade sobre essa pressão.

## Custo para o leitor: o terceiro teste

O teste de profundidade e o teste invariante decidem se um limite deve existir. O teste de custo para o leitor decide se o código ao redor do limite é barato para mudar. O próximo leitor, pessoa ou agente, paga por cada linha que precisa ler. Um agente paga em tokens. Um agente encontra código por busca de texto, leituras parciais, typecheck e testes.

- **Encontrável.** Use um nome para cada conceito. Escreva-o igual em todo lugar, para que a busca em texto simples o encontre. Defeitos: nomes construídos a partir de strings, ligação por efeitos colaterais de importação, dois nomes para um conceito. Cadeias de re-exportação que escondem a definição também são defeitos.
- **Parada precoce.** Coloque o contrato no topo do arquivo ou acima da exportação. Diga o que ele promete, o que esconde e o que nunca faz. Assim o leitor pode parar cedo.
- **Verificável por máquina.** Use tipos exatos para entrar e sair de cada limite, para que um typecheck substitua a leitura dos chamadores. Defeitos: `any`, dicionários simples, flags booleanas cujo significado está só no corpo.
- **Acoplamento visível.** Dois lugares devem mudar juntos. Imponha isso com um tipo compartilhado, um teste ou uma única fonte. Se não puder, marque nos dois lugares.
- **Sem ruído.** Remova comentários que repetem o código, e código comentado. Remova ramos mortos e comentários que registram histórico de mudanças. Remova um caminho antigo que fica ao lado da sua substituição.
- **Previsível.** Siga a organização existente do repositório. Coloque o teste onde o leitor espera encontrá-lo, e faça-o rodar sozinho.

O tamanho do arquivo não está nesta lista de propósito. Um arquivo muito grande é motivo para procurar uma segunda decisão oculta. Nunca é motivo para cortar o arquivo.

## Segurança

Para código existente, primeiro escreva um teste que mantenha o comportamento atual. Depois torne o módulo mais profundo. Para código novo, escreva o teste que define o comportamento pretendido.

## Nota de design (obrigatória quando o gate se aplica)

Quando o gate se aplica, coloque uma seção com o título `## Design note` na descrição do pull request. Escreva de duas a quatro linhas:

- Cada limite que você adicionou, e a decisão que ele esconde.
- Cada duplicação que você manteve de propósito, e o motivo.
- Cada parte superficial que você aceitou, e o motivo.

Se o gate não se aplica, escreva `## Design note` seguido de `Gate not applicable: <reason>`. Também coloque a nota de design no resumo do seu passo final.

## Modo de revisão

Use esta seção quando revisar ou testar código que outro agente ou pessoa escreveu.

1. Verifique a nota de design. Quando o gate se aplica e o pull request não tem seção `## Design note`, reporte um achado bloqueante. Quando a nota não concorda com o diff, reporte um achado bloqueante.
2. Um achado de design é bloqueante somente quando atende ambas as condições:
   - Nomeia uma regra desta skill. A regra é uma regra de decisão, o gate, o teste invariante ou um item de custo para o leitor.
   - Declara um custo concreto para o leitor ou para a próxima mudança. Exemplos: "Chamadores devem conhecer a forma do armazenamento." "Um conceito tem dois nomes." "Uma mudança de limite precisa de edições em três arquivos."
3. Marque toda outra observação de design como não bloqueante. Coloque-a em uma lista separada com o título "Notas de design não bloqueantes". Uma nota não bloqueante nunca devolve o trabalho ao construtor.
4. Não reporte uma preferência como achado. Um nome diferente, organização de arquivo ou estilo é uma preferência. Torna-se achado somente quando quebra uma regra nomeada e tem custo concreto.
5. Quando o mesmo achado de design voltar em um segundo ciclo de revisão, escale-o. Não solicite a mesma mudança uma terceira vez.

## Skills relacionadas (quando instaladas)

- `find-shared-code`: uma busca apenas para relatório do histórico recente por código que vale a pena compartilhar. Usa o teste invariante e o teste de profundidade desta skill.
- `refactoring` e `working-effectively-with-legacy-code`: os passos seguros para um design mais profundo. Esta skill decide se um novo limite permanece.

## Fonte e licença

Esta skill baseia-se nas regras "mini" de A Philosophy of Software Design no repositório ciembor/agent-rules-books no GitHub (licença MIT, commit 893a88a). O gate, o teste de invariante, o teste de custo do leitor, a nota de design e o modo de revisão são acréscimos a essas regras. O repositório também contém as regras completas do livro.

Tags

designarquiteturaousterhoutrevisão