Spec-Driven Development — explicação detalhada usando um sistema de gerenciamento de estacionamento como exemplo

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

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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).
  6. 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.
  7. 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:

  1. Validar sintaxe e estilo da spec (lint OpenAPI).
  2. Gerar stubs/mocks e clientes SDK a partir da spec.
  3. Executar testes unitários do back-end.
  4. Executar testes de contrato (consumer vs provider).
  5. Executar testes de aceitação automáticos (Gherkin) contra o ambiente de staging.
  6. Se a spec mudou, executar um job que compara versões (breaking changes) e notifica consumidores afetados.
  7. 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.

Deixe um comentário