Spec-driven development é uma prática onde a especificação (a spec) é a fonte única da verdade: primeiro se descreve, em formato estruturado e/ou formal, o comportamento, as interfaces e os critérios de aceitação do sistema; a partir dessa spec o desenvolvimento, os testes, os mocks, o código esqueleto e as validações são derivados. Em vez de escrever código e depois documentar, você escreve uma especificação legível por humanos e por máquinas, e usa essa especificação para guiar e automatizar quase todo o fluxo de engenharia.
A explicação a seguir mostra como aplicar essa abordagem passo a passo a um sistema realista de gerenciamento de estacionamento, cobrindo artefatos, fluxo de trabalho, exemplos práticos (incluindo trechos de OpenAPI/JSON Schema), estratégias de teste e integração contínua, além de riscos e boas práticas.
Visão geral do sistema de estacionamento (domínio)
Imagine um estacionamento urbano que oferece entrada/saída com catraca, emissão de ticket, vagas ocupadas/reservadas, cobrança por tempo, integração com aplicativo do cliente e painéis de operação. As responsabilidades principais incluem registrar entradas e saídas de veículos, calcular tarifas, reservar vagas, emitir relatórios e expor APIs para o app do usuário e para parceiros (p.ex. aggregadores de estacionamento).
No spec-driven, antes de criar banco de dados, endpoints ou UI, você começa descrevendo as operações e contratos que o sistema deve oferecer: contratos de API REST (OpenAPI), modelos de dados (JSON Schema), fluxos de estado (estado do ticket: emitido → em_uso → pago → encerrado) e critérios de aceitação (cenários de negócio).
Artefatos típicos da spec
A spec pode ser uma combinação de itens humanos e máquina-legíveis. Para o estacionamento, os artefatos importantes são:
- OpenAPI (ou equivalente) descrevendo endpoints REST e contratos de payloads.
- JSON Schema ou modelos para validar recursos como Ticket, Spot, Reservation.
- Diagrama de estados (state machine) para o ciclo de vida do Ticket.
- Critérios de aceitação (Gherkin ou formatos similares) para casos de negócio, como “calcular tarifa com desconto para permanência superior a X horas”.
- Mocks/contract tests (Pact, Hoverfly, WireMock) gerados a partir da spec para desenvolvimento paralelo entre equipes.
- Documentos de alto nível que capturam regras de negócio (por ex.: regras de cobrança, políticas de reserva, níveis de SLA).
Fluxo de trabalho spec-driven aplicado ao estacionamento
- Captura e formalização das regras de negócio. Primeiro, analistas e stakeholders escrevem em linguagem natural e Gherkin os fluxos essenciais: “Ao entrar, o veículo recebe um Ticket com timestamp; ao sair, o sistema calcula o tempo e gera a cobrança; reservas bloqueiam a vaga por um período X; pagamento confirmado muda status para pago.” Esses cenários são convertidos em critérios de aceitação formais.
- Definição das APIs/contratos. Com os requisitos claros, a equipe cria uma OpenAPI que descreve os endpoints públicos e privados que o sistema oferecerá:
POST /checkin,POST /checkout,GET /spots,POST /reservations,GET /tickets/{id}etc. Os esquemas de request/response usam JSON Schema para garantir validação. - Geração de artefatos automáticos. Ferramentas de codegen (p.ex. openapi-generator) criam stubs de servidor, clientes SDK para o app, documentação interativa (Swagger UI) e mocks que o front-end e times externos podem usar imediatamente.
- Desenvolvimento paralelo com mocks. Front-end e mobile usam os mocks gerados para implementar suas interfaces sem esperar o back-end. O back-end implementa os handlers nos stubs e os testa contra os critérios de aceitação.
- Testes de contrato e integração. Testes automatizados garantem que a implementação respeite a spec. Contratos são verificados por ambas as partes (provider/consumer). Testes de ponta a ponta usam os cenários Gherkin para validar fluxos críticos (checkin → permanência → checkout → pagamento).
- CI/CD orientado por spec. Em cada commit, a pipeline valida a spec (lint), regenera mocks/clientes se necessário, executa testes de contrato e os cenários de aceitação. Se a spec muda, a pipeline pode falhar se os consumidores não atualizarem — forçando coordenação controlada.
- Evolução controlada. Versão da spec (semver) e depreciação documentada permitem mudanças evolutivas; consumidores são notificados via changelog gerado automaticamente.
Exemplo prático: OpenAPI + JSON Schema (trechos)
Abaixo há um trecho simplificado em OpenAPI 3.0 que ilustra os principais endpoints para checkin, checkout e consulta de vagas. Esse documento serviria como contrato inicial para todas as equipes.
openapi: 3.0.3
info:
title: Parking Manager API
version: "1.0.0"
paths:
/checkin:
post:
summary: Registra a entrada de um veículo e emite um ticket
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CheckinRequest'
responses:
'201':
description: Ticket criado
content:
application/json:
schema:
$ref: '#/components/schemas/Ticket'
/checkout:
post:
summary: Registra saída do veículo e calcula tarifa
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CheckoutRequest'
responses:
'200':
description: Cobrança calculada
content:
application/json:
schema:
$ref: '#/components/schemas/CheckoutResponse'
/spots:
get:
summary: Lista vagas e seus estados
responses:
'200':
description: Lista de vagas
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Spot'
components:
schemas:
CheckinRequest:
type: object
required: [license_plate, entry_point]
properties:
license_plate:
type: string
entry_point:
type: string
vehicle_type:
type: string
enum: [car, motorcycle, truck]
Ticket:
type: object
properties:
id:
type: string
license_plate:
type: string
entry_time:
type: string
format: date-time
status:
type: string
enum: [em_uso, pago, encerrado]
CheckoutRequest:
type: object
required: [ticket_id, payment_method]
properties:
ticket_id:
type: string
payment_method:
type: string
enum: [card, cash, app]
CheckoutResponse:
type: object
properties:
ticket_id:
type: string
amount:
type: number
format: float
currency:
type: string
Spot:
type: object
properties:
id:
type: string
number:
type: integer
status:
type: string
enum: [livre, ocupado, reservado]
Code language: YAML (yaml)
Esse documento pode ser usado para gerar um mock server; o app do cliente pode se integrar ao mock enquanto o back-end implementa os handlers nos stubs.
Critérios de aceitação e exemplos (formatos executáveis)
Para assegurar que a implementação atende a regras de negócio, escreva cenários Gherkin. Exemplo:
Cenário: Checkin e Checkout com cálculo simples de tarifa
Dado que um veículo com placa "ABC-1234" fez checkin às "2025-10-21T08:00:00Z"
E a tarifa é R$ 5,00 por hora
Quando o veículo fizer checkout às "2025-10-21T10:30:00Z"
Então o sistema deve cobrar R$ 12,50
E o status do ticket deve ser "pago"
Esses cenários são legíveis e podem ser automatizados com frameworks de BDD que rodem contra a API mockada/implementada.
Testes de contrato (consumer-driven contracts)
Se o app móvel (consumer) espera respostas com campo amount sempre presente e com duas casas decimais, isso deve constar na spec e ser verificado via testes de contrato. Ferramentas como Pact permitem que o consumer gere expectativas que o provider deve satisfazer. No fluxo spec-driven, contratos são derivados da mesma spec OpenAPI, evitando divergências.
Pipeline CI/CD orientado por spec (exemplo de etapas)
A pipeline deve validar que código e spec permanecem consistentes. Um fluxo típico:
- Validar sintaxe e estilo da spec (lint OpenAPI).
- Gerar stubs/mocks e clientes SDK a partir da spec.
- Executar testes unitários do back-end.
- Executar testes de contrato (consumer vs provider).
- Executar testes de aceitação automáticos (Gherkin) contra o ambiente de staging.
- Se a spec mudou, executar um job que compara versões (breaking changes) e notifica consumidores afetados.
- Gerar documentação interativa e changelog.
Modelagem de estados importantes
Defina explicitamente a state machine do Ticket. Por exemplo, estados e transições:
emitido → em_uso quando veículo entra,
em_uso → aguardando_pagamento ao solicitar checkout,
aguardando_pagamento → pago ao confirmar cobrança,
pago → encerrado após finalização e arquivamento.
Descrever isso em um artefato formal (diagrama ou máquina de estados em YAML) ajuda a validar regras concorrentes e a construir testes de estado.
Casos de contorno e regras complexas
Spec-driven força a pensar antecipadamente em casos que normalmente surgiriam tarde no ciclo:
- Concorrência: duas entradas tentando reservar a mesma vaga ao mesmo tempo. A spec deve descrever lock/optimistic concurrency ou política de reserva.
- Tarifas complexas: tarifas dinâmicas por hora, diária, descontos noturnos, isenções, multas por perda de ticket. Cada regra vira um cenário de aceitação.
- Reservas: política de hold (tempo máximo que uma vaga fica reservada), cancelamento e penalidades.
- Integração com hardware: leitores de placa e catracas possuem contratos (webhook, formato de mensagem) que também devem constar na spec.
- Falhas de pagamento e rollback: definir como o sistema lida com pagamentos recusados e como reverter mudanças de estado.
Evolução da spec e versionamento
Trate a spec como um artefato de produto com versionamento e política de compatibilidade. Use semver para indicar breaking changes; gere automaticamente um changelog com diff entre versões da OpenAPI; rotule endpoints obsoletos e forneça rota de migração para consumidores.
Métricas e sinais de sucesso
Para avaliar a eficácia do spec-driven, acompanhe métricas como tempo de integração entre equipes (front vs back), número de bugs relacionados a contratos, velocidade de onboarding de novos desenvolvedores (quanto tempo para começar a trabalhar usando mocks), número de regressões por mudança na spec, e tempo médio para detectar breaking changes.
Vantagens do spec-driven para o estacionamento
A posse de uma spec clara evita mal-entendidos entre operações (que definem regras de negócio), mobile (que precisa do contrato) e infra/hardware (catracas/leitores). Permite desenvolvimento paralelo, gera documentação interativa para usuários técnicos, fornece base para testes automáticos e reduz retrabalho causado por mudanças de entendimento.
Riscos e armadilhas
Escrever specs ruins ou ambíguas significa que você “documentou errado” em vez de ajudar. Specs excessivamente detalhadas podem se tornar burocráticas; specs muito vagas não trazem benefício. Também há custo inicial: escrever specs formais e configurar geração e testes demanda disciplina e automação. Se a equipe não praticar versionamento e compatibilidade, mudanças na spec podem quebrar consumidores.
Boas práticas e recomendações
Mantenha a spec simples e evolutiva: comece com os contratos essenciais (checkin/checkout/spots) e itere. Automatize geração de mocks e clientes para reduzir atrito. Trate a spec como código: versionamento, revisão por pull request e testes automatizados. Documente e testar fluxos de erro e concorrência. Integre validações de spec na pipeline CI para evitar divergências.
Conclusão prática
Em um sistema de gerenciamento de estacionamento, o spec-driven transforma a especificação em peça central do desenvolvimento: a partir dela você gera mocks para desenvolvimento paralelo, valida contratos automaticamente, automatiza testes de aceitação e reduz fricção entre equipes. Ao definir antecipadamente APIs, modelos de dados e regras de negócio (estado do ticket, política de cobrança, reservas), você reduz surpresas em produção e acelera entrega de valor consistente. Para começar, escreva uma OpenAPI mínima para os casos críticos, gere mocks e execute um ciclo curto de feedback entre app e back-end — desse núcleo você expande a spec até cobrir todas as regras de negócio necessárias.
Se quiser aprofundar sobre o SDD neste exemplo, associei o processo ao uso do diagrama de sequência da UML.