{"id":1331,"date":"2026-10-15T04:01:20","date_gmt":"2026-10-15T07:01:20","guid":{"rendered":"https:\/\/adrianosantostreina.com.br\/blog\/spec-driven-development-implementacao-projetos-existentes\/"},"modified":"2026-10-15T04:01:31","modified_gmt":"2026-10-15T07:01:31","slug":"spec-driven-development-implementacao-projetos-existentes","status":"publish","type":"post","link":"https:\/\/adrianosantostreina.com.br\/blog\/spec-driven-development-implementacao-projetos-existentes\/","title":{"rendered":"Especifica\u00e7\u00e3o Como Artefato Vivo: Implementando Spec-Driven Development em Projetos que J\u00e1 Existem"},"content":{"rendered":"<p>Voc\u00ea j\u00e1 recebeu um c\u00f3digo gerado por IA que compilava, passava nos testes que existiam e ainda assim estava completamente errado em rela\u00e7\u00e3o ao que o time realmente precisava? O agente entendeu &#8220;cadastrar usu\u00e1rio&#8221; como uma opera\u00e7\u00e3o simples de insert, enquanto a regra de neg\u00f3cio exigia valida\u00e7\u00e3o de duplicidade por CPF, envio ass\u00edncrono de e-mail de confirma\u00e7\u00e3o e um rollback espec\u00edfico se a integra\u00e7\u00e3o com o sistema de billing falhasse. Nenhuma dessas regras estava em lugar nenhum al\u00e9m da cabe\u00e7a de quem especificou a tarefa verbalmente numa call de quinze minutos.<\/p>\n<p>Esse \u00e9 o sintoma mais comum de projetos que adotaram IA generativa no fluxo de desenvolvimento sem resolver o problema real: a especifica\u00e7\u00e3o nunca foi um artefato formal, sempre foi um entendimento t\u00e1cito distribu\u00eddo entre product owners, desenvolvedores seniores e algum documento desatualizado no Confluence. Quando o c\u00f3digo era escrito s\u00f3 por humanos, esse entendimento t\u00e1cito se propagava por osmose \u2014 reuni\u00f5es, code review, &#8220;pergunta pro fulano&#8221;. Quando parte do c\u00f3digo passa a ser gerado por um agente que n\u00e3o participou de nenhuma dessas conversas, a lacuna fica vis\u00edvel e cara.<\/p>\n<p>Neste artigo o foco n\u00e3o \u00e9 explicar o que \u00e9 Spec-Driven Development em tese, mas mostrar como estruturar esse fluxo em um projeto que j\u00e1 est\u00e1 rodando, com c\u00f3digo legado, d\u00e9bito t\u00e9cnico e press\u00e3o de prazo \u2014 os ingredientes reais do dia a dia, n\u00e3o o cen\u00e1rio ideal de greenfield.<\/p>\n<h2>Por que specs informais quebram quando a IA entra no fluxo<\/h2>\n<p>Segundo o guia da <a href=\"https:\/\/www.augmentcode.com\/guides\/what-is-spec-driven-development\" target=\"_blank\" rel=\"noopener\">Augment Code sobre Spec-Driven Development<\/a>, a diferen\u00e7a central entre esse modelo e o desenvolvimento tradicional \u00e9 que a especifica\u00e7\u00e3o deixa de ser um documento de refer\u00eancia e passa a ser a fonte de verdade execut\u00e1vel \u2014 o c\u00f3digo \u00e9 tratado como artefato derivado, n\u00e3o como ponto de partida.<\/p>\n<p>Isso muda a mec\u00e2nica do trabalho de forma concreta. Em vez de abrir uma branch e come\u00e7ar a escrever a fun\u00e7\u00e3o, voc\u00ea escreve primeiro um arquivo de especifica\u00e7\u00e3o que descreve comportamento, contratos de entrada e sa\u00edda, casos de borda e crit\u00e9rios de aceite. S\u00f3 depois esse arquivo alimenta o agente \u2014 Claude Code, Cursor, ou qualquer ferramenta equivalente \u2014 que gera a implementa\u00e7\u00e3o.<\/p>\n<p>A <a href=\"https:\/\/www.ibm.com\/think\/topics\/spec-driven-development\" target=\"_blank\" rel=\"noopener\">IBM, em artigo da pesquisadora Anna Gutowska sobre o tema<\/a>, refor\u00e7a um ponto que costuma ser subestimado: a especifica\u00e7\u00e3o formal reduz a ambiguidade que os modelos de linguagem exploram para &#8220;preencher lacunas&#8221; com suposi\u00e7\u00f5es plaus\u00edveis, mas erradas. Um LLM n\u00e3o erra por falta de capacidade t\u00e9cnica na maioria dos casos \u2014 erra porque interpreta razoavelmente bem um enunciado mal definido, e a interpreta\u00e7\u00e3o razo\u00e1vel nem sempre \u00e9 a correta para o seu dom\u00ednio de neg\u00f3cio.<\/p>\n<blockquote><p><strong>\ud83d\udca1 Dica do Mestre:<\/strong> Antes de adotar SDD formalmente, vale assistir \u00e0 apresenta\u00e7\u00e3o <a href=\"https:\/\/www.youtube.com\/watch?v=0DgML1VH6t4&amp;vl=en-US\" target=\"_blank\" rel=\"noopener\">Spec-Driven Development (SDD) Engenharia de Software Ag\u00eantica<\/a>, que detalha como equipes estruturam o ciclo de vida da especifica\u00e7\u00e3o em projetos com m\u00faltiplos agentes trabalhando em paralelo.<\/p><\/blockquote>\n<h2>Estruturando o arquivo de especifica\u00e7\u00e3o<\/h2>\n<p>Na pr\u00e1tica, uma spec n\u00e3o \u00e9 um PRD gen\u00e9rico de produto. Ela precisa ser estruturada o suficiente para que um agente \u2014 ou um humano revisando o output do agente \u2014 consiga verificar objetivamente se a implementa\u00e7\u00e3o atende ou n\u00e3o aos crit\u00e9rios. Um formato que funciona bem combina contexto, contrato e casos de teste no mesmo arquivo.<\/p>\n<p>Veja um exemplo de spec para uma funcionalidade de cancelamento de assinatura em um sistema de billing, escrito em Markdown estruturado \u2014 formato que a maioria dos agentes de c\u00f3digo consegue parsear sem ambiguidade:<\/p>\n<pre><code>## Spec: Cancelamento de Assinatura\n\n### Contexto\nUsu\u00e1rio autenticado solicita cancelamento da assinatura ativa.\nSistema deve processar o cancelamento sem interromper o acesso\nat\u00e9 o fim do ciclo de cobran\u00e7a j\u00e1 pago.\n\n### Contrato da fun\u00e7\u00e3o\n```\ncancelSubscription(userId: string, reason: CancelReason): Promise\n```\n\n### Regras de neg\u00f3cio\n1. Se o usu\u00e1rio n\u00e3o tiver assinatura ativa, retornar erro `NO_ACTIVE_SUBSCRIPTION`\n2. O acesso permanece v\u00e1lido at\u00e9 `currentPeriodEnd`, n\u00e3o \u00e9 revogado na hora\n3. Deve registrar `reason` em log de auditoria com timestamp e userId\n4. Deve disparar evento `subscription.cancelled` na fila (n\u00e3o bloquear a resposta)\n5. Se a chamada \u00e0 fila falhar, a opera\u00e7\u00e3o de cancelamento N\u00c3O deve\n   ser revertida \u2014 logar erro e seguir (consist\u00eancia eventual)\n\n### Casos de teste obrigat\u00f3rios\n- Cancelamento de assinatura ativa retorna sucesso e mant\u00e9m acesso at\u00e9 o fim do per\u00edodo\n- Cancelamento de usu\u00e1rio sem assinatura retorna NO_ACTIVE_SUBSCRIPTION\n- Falha no envio do evento n\u00e3o impede o cancelamento de ser persistido\n- reason inv\u00e1lido (fora do enum CancelReason) retorna erro de valida\u00e7\u00e3o antes de tocar no banco\n\n### Fora de escopo\n- Reembolso proporcional (feature separada, ver spec billing-refund.md)\n- Reativa\u00e7\u00e3o de assinatura cancelada\n<\/code><\/pre>\n<p>Note que essa spec n\u00e3o descreve implementa\u00e7\u00e3o \u2014 n\u00e3o diz se vai usar um ORM espec\u00edfico ou qual biblioteca de fila. Ela descreve comportamento observ\u00e1vel e restri\u00e7\u00f5es. Isso \u00e9 deliberado: a spec precisa sobreviver a uma troca de stack sem precisar ser reescrita.<\/p>\n<h2>O agente como executor, o humano como revisor de contrato<\/h2>\n<p>Com a spec pronta, o fluxo muda de &#8220;pe\u00e7a para o agente implementar a feature X&#8221; para &#8220;pe\u00e7a para o agente implementar exatamente o contrato descrito neste arquivo&#8221;. A diferen\u00e7a na pr\u00e1tica \u00e9 grande. Um exemplo de instru\u00e7\u00e3o para um agente como Claude Code, referenciando o arquivo de spec diretamente:<\/p>\n<pre><code>Implemente a fun\u00e7\u00e3o cancelSubscription seguindo exatamente\no contrato em specs\/cancel-subscription.md.\n\nUse o reposit\u00f3rio SubscriptionRepository j\u00e1 existente em\nsrc\/billing\/subscription.repository.ts para persist\u00eancia.\n\nGere tamb\u00e9m os testes descritos na se\u00e7\u00e3o \"Casos de teste\nobrigat\u00f3rios\" usando o framework de testes do projeto (Vitest).\n\nN\u00e3o implemente nada listado em \"Fora de escopo\".\n<\/code><\/pre>\n<p>O retorno do agente, nesse modelo, passa a ser avali\u00e1vel contra um checklist objetivo: cada caso de teste da spec foi coberto? O contrato de assinatura de fun\u00e7\u00e3o bate? As regras de neg\u00f3cio numeradas est\u00e3o todas refletidas em algum ponto do c\u00f3digo ou dos testes? Isso transforma a revis\u00e3o de c\u00f3digo gerado por IA de um exerc\u00edcio de &#8220;isso parece certo&#8221; para um exerc\u00edcio de verifica\u00e7\u00e3o de conformidade, que \u00e9 mais r\u00e1pido e muito menos sujeito a vi\u00e9s de confirma\u00e7\u00e3o.<\/p>\n<p>Um exemplo em TypeScript do que esse agente provavelmente geraria, j\u00e1 alinhado ao contrato:<\/p>\n<pre><code>export async function cancelSubscription(\n  userId: string,\n  reason: CancelReason\n): Promise {\n  const subscription = await subscriptionRepository.findActiveByUserId(userId);\n\n  if (!subscription) {\n    return { success: false, error: 'NO_ACTIVE_SUBSCRIPTION' };\n  }\n\n  if (!Object.values(CancelReason).includes(reason)) {\n    return { success: false, error: 'INVALID_REASON' };\n  }\n\n  await subscriptionRepository.markAsCancelled(subscription.id, reason);\n  await auditLog.record({\n    userId,\n    action: 'subscription_cancelled',\n    reason,\n    timestamp: new Date(),\n  });\n\n  try {\n    await eventQueue.publish('subscription.cancelled', {\n      userId,\n      subscriptionId: subscription.id,\n    });\n  } catch (err) {\n    logger.error('Falha ao publicar evento de cancelamento', err);\n    \/\/ intencional: n\u00e3o propaga o erro, cancelamento j\u00e1 foi persistido\n  }\n\n  return {\n    success: true,\n    accessUntil: subscription.currentPeriodEnd,\n  };\n}\n<\/code><\/pre>\n<p>O detalhe do try\/catch silencioso em torno da publica\u00e7\u00e3o do evento n\u00e3o \u00e9 acidente nem gambiarra do agente \u2014 \u00e9 a regra 5 da spec sendo cumprida \u00e0 risca. Sem a spec expl\u00edcita, essa decis\u00e3o de design ficaria a crit\u00e9rio da interpreta\u00e7\u00e3o do modelo, e boa parte dos agentes, por padr\u00e3o, propagaria o erro e falharia a opera\u00e7\u00e3o inteira, o que violaria a regra de neg\u00f3cio real.<\/p>\n<h2>Retrofitting: aplicando SDD em c\u00f3digo legado<\/h2>\n<p>A parte mais dif\u00edcil n\u00e3o \u00e9 adotar SDD em uma feature nova \u2014 \u00e9 introduzi-lo em um m\u00f3dulo que j\u00e1 existe, sem spec nenhuma, com anos de decis\u00f5es impl\u00edcitas acumuladas. O caminho mais sustent\u00e1vel aqui \u00e9 inverso ao fluxo padr\u00e3o: em vez de escrever a spec e depois o c\u00f3digo, voc\u00ea usa o agente para extrair uma spec candidata a partir do c\u00f3digo existente, e ent\u00e3o a equipe valida e corrige essa spec antes de trat\u00e1-la como fonte de verdade.<\/p>\n<pre><code>Analise o arquivo src\/billing\/subscription.service.ts e gere\numa especifica\u00e7\u00e3o no formato specs\/*.md (contexto, contrato,\nregras de neg\u00f3cio numeradas, casos de teste observados).\n\nListe separadamente qualquer comportamento que pare\u00e7a ser bug\nn\u00e3o intencional, marcando como \"COMPORTAMENTO SUSPEITO \u2014 validar\ncom o time\" em vez de documentar como regra.\n<\/code><\/pre>\n<p>Essa instru\u00e7\u00e3o final \u00e9 cr\u00edtica. C\u00f3digo legado frequentemente tem comportamento que nunca foi intencional \u2014 um null check que mascarava um bug de outra camada, uma ordem de opera\u00e7\u00f5es que s\u00f3 funciona por acidente de timing. Se o agente documentar esses comportamentos como regra de neg\u00f3cio oficial, a spec vira um contrato que perpetua o bug para sempre. Separar explicitamente &#8220;comportamento documentado&#8221; de &#8220;comportamento suspeito&#8221; evita esse tipo de fossiliza\u00e7\u00e3o de defeito.<\/p>\n<p>Segundo a <a href=\"https:\/\/www.reddit.com\/r\/vibecoding\/comments\/1rkh6r7\/specdriven_development_is_the_only_way_to_scale\/\" target=\"_blank\" rel=\"noopener\">discuss\u00e3o na comunidade r\/vibecoding sobre escalabilidade de SDD<\/a>, esse \u00e9 exatamente o ponto em que times que tentam adotar o modelo de forma apressada trope\u00e7am: tratam a primeira vers\u00e3o da spec gerada automaticamente como definitiva, sem o ciclo de revis\u00e3o humana, e acabam formalizando d\u00edvida t\u00e9cnica em vez de pag\u00e1-la.<\/p>\n<h2>Valida\u00e7\u00e3o cont\u00ednua: a spec n\u00e3o pode ficar desatualizada<\/h2>\n<p>O risco mais s\u00e9rio de longo prazo em SDD \u00e9 o mesmo risco que qualquer documenta\u00e7\u00e3o sempre teve: divergir da realidade do c\u00f3digo com o tempo. A diferen\u00e7a \u00e9 que, nesse modelo, a spec n\u00e3o \u00e9 s\u00f3 documenta\u00e7\u00e3o \u2014 \u00e9 o contrato que o agente vai usar na pr\u00f3xima altera\u00e7\u00e3o. Se ela estiver desatualizada, o agente vai gerar c\u00f3digo consistente com uma regra que n\u00e3o existe mais.<\/p>\n<p>A pr\u00e1tica que resolve isso na maioria dos times que adotaram o fluxo de forma madura, de acordo com a an\u00e1lise publicada no <a href=\"https:\/\/dev.to\/krlz\/spec-driven-development-in-2026-what-it-is-the-tooling-and-how-teams-actually-use-it-2fk2\" target=\"_blank\" rel=\"noopener\">artigo sobre ferramentas e ado\u00e7\u00e3o real de SDD em 2026<\/a>, \u00e9 tratar a spec como parte do pull request, n\u00e3o como documento \u00e0 parte. Toda altera\u00e7\u00e3o de comportamento exige altera\u00e7\u00e3o correspondente na spec, no mesmo commit, revisada pelo mesmo reviewer. Isso pode ser refor\u00e7ado via CI:<\/p>\n<pre><code># Exemplo de verifica\u00e7\u00e3o simples em pipeline (pseudoc\u00f3digo shell)\nif git diff --name-only origin\/main | grep -q \"src\/billing\/\"; then\n  if ! git diff --name-only origin\/main | grep -q \"specs\/.*cancel-subscription.*\\.md\"; then\n    echo \"ERRO: altera\u00e7\u00e3o em src\/billing\/ sem atualiza\u00e7\u00e3o de spec correspondente\"\n    exit 1\n  fi\nfi\n<\/code><\/pre>\n<p>Esse tipo de verifica\u00e7\u00e3o \u00e9 propositalmente simples e n\u00e3o cobre todos os casos, mas cumpre o papel de lembrete estrutural: ningu\u00e9m altera o contrato de neg\u00f3cio sem tocar no arquivo que o descreve. Times maiores evoluem isso para ferramentas mais sofisticadas de rastreamento, mas o princ\u00edpio \u00e9 o mesmo em qualquer escala.<\/p>\n<h3>Quando SDD n\u00e3o compensa o esfor\u00e7o<\/h3>\n<p>Vale ser honesto sobre o custo. Escrever specs estruturadas para cada mudan\u00e7a pequena \u00e9 overhead desnecess\u00e1rio em prot\u00f3tipos, scripts internos de uso \u00fanico ou c\u00f3digo com vida \u00fatil curta. SDD compensa quando o c\u00f3digo vai ser mantido por m\u00faltiplas pessoas, por muito tempo, e quando os agentes de IA v\u00e3o continuar operando sobre ele repetidamente \u2014 \u00e9 um investimento que se paga em manuten\u00e7\u00e3o futura, n\u00e3o em velocidade imediata de entrega. Aplicar o modelo a um script de migra\u00e7\u00e3o que roda uma vez s\u00f3 \u00e9 desperd\u00edcio de disciplina.<\/p>\n<h2>Leve essa discuss\u00e3o para quem j\u00e1 est\u00e1 testando na pr\u00e1tica<\/h2>\n<p>Spec-Driven Development ainda est\u00e1 em forma\u00e7\u00e3o como pr\u00e1tica de mercado \u2014 as ferramentas, os formatos de arquivo e as conven\u00e7\u00f5es de integra\u00e7\u00e3o com agentes mudam r\u00e1pido, e trocar experi\u00eancia com quem est\u00e1 aplicando isso em projetos reais, com as mesmas dores de legado e prazo, vale mais do que qualquer guia te\u00f3rico. Se voc\u00ea quer discutir implementa\u00e7\u00f5es concretas, trocar templates de spec e ver o que tem funcionado (ou n\u00e3o) em times diferentes, entre na <a href=\"https:\/\/adrianosantos.link\/ComunidadeDevAI\" target=\"_blank\" rel=\"noopener\">Comunidade Dev&#8217;s AI<\/a>.<\/p>\n<h2>Conclus\u00e3o<\/h2>\n<p>Spec-Driven Development n\u00e3o resolve o problema de agentes de IA gerarem c\u00f3digo errado por falta de intelig\u00eancia \u2014 resolve o problema, muito mais comum, de agentes gerarem c\u00f3digo tecnicamente correto para uma interpreta\u00e7\u00e3o diferente da que o time pretendia. A especifica\u00e7\u00e3o formal, tratada como artefato vivo e versionado junto do c\u00f3digo, fecha essa lacuna de forma verific\u00e1vel, em vez de depender de prompts cada vez mais longos tentando compensar contexto que nunca foi escrito em lugar nenhum. O esfor\u00e7o inicial de estruturar specs \u00e9 real, mas ele se compara ao mesmo esfor\u00e7o que qualquer engenharia s\u00e9ria j\u00e1 fazia com contratos de API bem definidos \u2014 s\u00f3 que agora esse contrato tamb\u00e9m orienta quem, ou o que, escreve o c\u00f3digo.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Voc\u00ea j\u00e1 recebeu um c\u00f3digo gerado por IA que compilava, passava nos testes que existiam e ainda assim estava completamente errado em rela\u00e7\u00e3o ao que o time realmente precisava? O agente entendeu &#8220;cadastrar usu\u00e1rio&#8221; como uma opera\u00e7\u00e3o simples de insert, enquanto a regra de neg\u00f3cio exigia valida\u00e7\u00e3o de duplicidade por CPF, envio ass\u00edncrono de e-mail [&hellip;]<\/p>\n","protected":false},"author":127,"featured_media":1332,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1331","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog"],"_links":{"self":[{"href":"https:\/\/adrianosantostreina.com.br\/blog\/wp-json\/wp\/v2\/posts\/1331","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/adrianosantostreina.com.br\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/adrianosantostreina.com.br\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/adrianosantostreina.com.br\/blog\/wp-json\/wp\/v2\/users\/127"}],"replies":[{"embeddable":true,"href":"https:\/\/adrianosantostreina.com.br\/blog\/wp-json\/wp\/v2\/comments?post=1331"}],"version-history":[{"count":1,"href":"https:\/\/adrianosantostreina.com.br\/blog\/wp-json\/wp\/v2\/posts\/1331\/revisions"}],"predecessor-version":[{"id":1333,"href":"https:\/\/adrianosantostreina.com.br\/blog\/wp-json\/wp\/v2\/posts\/1331\/revisions\/1333"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/adrianosantostreina.com.br\/blog\/wp-json\/wp\/v2\/media\/1332"}],"wp:attachment":[{"href":"https:\/\/adrianosantostreina.com.br\/blog\/wp-json\/wp\/v2\/media?parent=1331"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/adrianosantostreina.com.br\/blog\/wp-json\/wp\/v2\/categories?post=1331"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/adrianosantostreina.com.br\/blog\/wp-json\/wp\/v2\/tags?post=1331"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}