Escrevendo Boas Especificações
Você raramente escreve uma especificação em uma página em branco. Você descreve uma mudança em linguagem simples, o /opsx:propose elabora os requisitos e cenários, e então você os aperfeiçoa. Esta página trata dessa última parte — o que "bom" significa e como orientar a IA nessa direção.
Ela é a companheira de Revisando uma Mudança: revisar é identificar os pontos fracos de um rascunho, escrever é saber do que um rascunho forte é feito.
Uma especificação é comportamento, não código
Uma especificação diz o que seu sistema faz, em termos que qualquer pessoa possa verificar — não como ele é construído. Ela é composta por requisitos (declarações de comportamento) e cenários (exemplos concretos que os comprovam).
### Requirement: Session Timeout
The system SHALL expire a session after 30 minutes of inactivity.
#### Scenario: Idle timeout
- GIVEN an authenticated session
- WHEN 30 minutes pass with no activity
- THEN the session is invalidated and the user must re-authenticateMantenha o como — a fila, a biblioteca, o esquema da tabela — em design.md ou no código. Quando comportamento e implementação se misturam em um único requisito, o requisito deixa de ser testável e começa a ficar desatualizado no momento em que o código muda.
O que torna um bom requisito
Um bom requisito é um único comportamento, declarado de forma tão clara que você poderia entregá-lo a outra pessoa para testar.
Uma declaração, um
SHALL/MUST. Se um requisito tem três cláusulas de "e também", são na verdade três requisitos. Separe-os.Observável. Alguém fora do código deve ser capaz de dizer se ele se cumpre. "O sistema DEVE exibir um banner de erro quando o upload exceder 10 MB" é observável. "O sistema DEVE lidar com uploads grandes de forma adequada" não é.
A força adequada. O OpenSpec usa as palavras-chave do RFC 2119, e elas significam coisas diferentes:
Palavra-chave Significado MUST/SHALLUm requisito rígido. Inegociável. SHOULDUma recomendação forte, com margem para uma exceção justificada. MAYGenuinamente opcional. Use
MUST/SHALLpor padrão. UseSHOULDapenas quando você realmente quer dizer "a menos que haja um bom motivo para não".
O teste para um requisito: um testador que nunca viu o código conseguiria dizer se ele passou? Se não, ele precisa ser afiado.
O que torna um bom cenário
Os cenários são onde um requisito justifica sua existência. Cada um é um GIVEN / WHEN / THEN concreto que poderia se tornar um teste automatizado.
- Ele exercita seu requisito. Um cenário que apenas reafirma o requisito com outras palavras não testa nada. Torne-o uma situação específica com um resultado específico.
- Cubra os casos que importam, não apenas o caminho feliz. O login válido é fácil. A entrada vazia, o token expirado, o segundo clique, a coisa que dá errado — são esses os lugares onde os bugs vivem, e onde um cenário vale mais.
- Nomeie o caso no título. "Scenario: Rejects an expired token" diz a um revisor o que está coberto de relance; "Scenario: Test 2" não diz.
Um hábito útil: antes de aprovar, pergunte qual é o caso que eu ficaria chateado de ver quebrado? — e certifique-se de que um cenário o nomeia.
Escolha o tipo de delta adequado
Uma mudança descreve suas edições nas especificações com três tipos de seção. Usar o certo mantém suas especificações arquivadas honestas:
## ADDED Requirements— comportamento totalmente novo que não existia antes.## MODIFIED Requirements— comportamento que já existia e está mudando. Inclua a nova versão completa; uma breve nota sobre o que mudou ajuda o revisor.## REMOVED Requirements— comportamento que está sendo removido, com uma linha explicando por quê.
No arquivamento, ADDED é adicionado à especificação principal, MODIFIED substitui a versão antiga e REMOVED é removido dela. Remova o último requisito de uma capacidade e ela é aposentada: em vez de deixar uma especificação vazia, o arquivamento exclui openspec/specs/<capability>/spec.md. Como esse é o único passo de arquivamento que remove um arquivo, ele precisa ser solicitado — adicione retire_capabilities: true ao .openspec.yaml da mudança, junto com o schema: que esse arquivo já precisa. Sem isso, o arquivamento é abortado e informa você. A aposentadoria exclui o arquivo inteiro, então também é recusada enquanto a especificação contém algo fora do título, ## Purpose e seus blocos de requisitos — uma seção ## Notes, um comentário sob um requisito. O abortamento nomeia essas linhas; mova-as para ## Purpose ou para um requisito, ou exclua a especificação manualmente. Para uma especificação no checkout do chamador, a saída do arquivamento também nomeia o git checkout que restaura um arquivo commitado; stores selecionados recebem orientações de recuperação escopadas ao checkout. Se você marcar uma mudança real como ADDED, acabará com dois requisitos em conflito; se descrever comportamento novo como MODIFIED, não há nada para substituir. Na dúvida, abra a especificação atual e veja se o requisito já está lá.
Há mais uma seção que vale a pena conhecer. Quando seu delta cria uma capacidade que ainda não existe, abra-o com ## Purpose — uma ou duas frases sobre para que serve a capacidade. O arquivamento a usa como o Purpose da especificação principal que cria; pule-a e você terá um placeholder TBD para preencher manualmente. Uma especificação existente já tem um Purpose, então o do delta é ignorado ali — edite openspec/specs/<capability-path>/spec.md diretamente para alterá-lo. Aqui, <capability-path> é o diretório relativo a specs/, como user-auth em um projeto plano ou identity/user-auth em um projeto organizado por domínio.
Dimensione corretamente a mudança
O erro de autoria mais comum não é um requisito mal redigido — é uma mudança que tenta ser três mudanças.
Uma boa mudança tem uma única intenção que você pode expressar em uma frase. "Adicionar um alternador de modo escuro." "Limitar a taxa do endpoint de login." "Migrar sessões de cookies." Se descrever a mudança exige muitos "e também", esse é o sinal de que deve ser dividida.
Sinais de que uma mudança é grande demais:
- O escopo da proposta parece uma lista de recursos não relacionados.
- Revisá-la levaria uma tarde inteira, então ninguém fará.
- Duas pessoas não conseguiriam trabalhar nela sem colidir.
- Metade das tarefas poderia ser lançada sozinha.
Mudanças menores são mais fáceis de revisar, mais fáceis de construir em uma sessão focada e mais fáceis de raciocinar seis meses depois, quando o arquivamento é tudo o que resta. Você sempre pode executar várias mudanças em paralelo — veja Editando e iterando e Workflows.
O oposto também acontece: uma correção de erro de digitação de uma linha não precisa de três requisitos e um documento de design. Ajuste a cerimônia à importância.
Como orientar a IA rumo a um bom rascunho
Como o /opsx:propose faz o primeiro rascunho, a qualidade do que você recebe de volta acompanha a qualidade do que você lhe dá. Você não precisa escrever requisitos manualmente — precisa orientar bem a IA:
- Declare a intenção e o limite. "Adicionar um alternador de modo escuro que segue a configuração do SO no primeiro carregamento — não toque na API de tema existente." A metade fora do escopo importa tanto quanto a metade dentro do escopo.
- Nomeie os casos que importam para você. "Certifique-se de que há um cenário para um usuário que já escolheu um tema manualmente." A IA cobre o que você aponta.
- Depois edite. É Markdown simples. Aperte um
SHALLvago, exclua um cenário que não testa nada, adicione o caso que faltou — ou peça à IA: "o requisito de timeout é vago, fixe-o em 30 minutos."
Rascunhe, afie, repita. Algumas rodadas disso produzem uma especificação em que você confiaria, que é o objetivo principal.
Uma lista de verificação rápida
Para onde ir a seguir
- Revisando uma Mudança — a passagem de dois minutos que pega o que escapou.
- Conceitos — o modelo mais profundo por trás de especificações, mudanças e deltas.
- Exemplos e Receitas — mudanças reais do início ao fim.