跳到正文
DEV Community· Maria Paula·· 3 小時前AI 評分51

Brasileirão Pro Analytics 專案實測 Spec-Driven Development 流程

Spec-Driven Development na prática: testando o fluxo em um projeto de ML

AI 導讀

Maria Paula 在 Brasileirão Pro Analytics 專案中實測 Spec-Driven Development,將訓練及測試賽季改為參數,並按規格核對實作結果。

正文

Introdução

Pedir código a uma IA com um prompt curto deixa muita coisa em aberto. O modelo precisa presumir o que ninguém escreveu, e o erro costuma aparecer só no fim, quando o código já está pronto. O Spec-Driven Development (SDD) tenta resolver isso escrevendo uma especificação antes do código.

Um exemplo de problema que só aparece depois está no meu projeto, o Brasileirão Pro Analytics. A primeira versão calculava as estatísticas dos times (posse, desarmes, gols) como acumulados da temporada inteira e usava esses valores para prever jogos da própria temporada. Na prática, o modelo "via" resultados que ainda não tinham acontecido, um caso de data leakage. Corrigi isso no commit b96e6ba, passando a calcular as features só com jogos anteriores a cada partida.

Neste artigo eu aplico o SDD em uma tarefa seguinte do mesmo projeto: transformar a temporada em parâmetro, para o código não depender de 2025 e 2026 escritos à mão. Depois comparo o resultado com o que a própria spec exigia. Os arquivos estão na branch experimento-sdd.

O que é Spec-Driven Development

SDD é escrever uma especificação antes de codar com IA e usar essa especificação como referência para a pessoa e para o agente. A Birgitta Böckeler, no site do Martin Fowler, separa três níveis de uso:

  • spec-first: a spec é escrita antes e usada no fluxo de desenvolvimento com IA;
  • spec-anchored: a spec é mantida depois que a tarefa termina, para evoluir e manter a funcionalidade;
  • spec-as-source: a spec é o arquivo-fonte principal e a pessoa não mexe no código.

Ela também diferencia spec de memory bank. A spec descreve o comportamento de uma funcionalidade e vale para as tarefas que criam ou alteram essa funcionalidade. O memory bank reúne regras e descrições gerais do produto e da base de código, válidas para todas as sessões. No spec-kit, esse papel é da constitution, que guarda princípios de alto nível aplicados a toda mudança.

O texto do GitHub Blog (Den Delimarsky, 2 set. 2025) descreve o Spec Kit em quatro fases:

  1. specify: usuários, problema e critérios de sucesso, sem detalhe técnico;
  2. plan: stack, arquitetura e restrições;
  3. tasks: pedaços pequenos que dá para implementar e testar isoladamente;
  4. implement: o agente executa as tarefas e a pessoa revisa.

Na análise da Böckeler, o fluxo do spec-kit começa pela constitution, seguida de specify, plan e tasks.

A Thoughtworks colocou a técnica no anel Assess do Technology Radar de novembro de 2025, que significa que vale explorar para entender como ela afetará a organização. O Radar diz que os fluxos continuam elaborados e opinativos, que as ferramentas se comportam de forma bem diferente conforme o tamanho e o tipo da tarefa e que algumas geram arquivos de especificação longos e difíceis de revisar. A página do Radar indica que o item não está na edição atual, então a classificação vale para aquela edição.

O experimento

O Brasileirão Pro Analytics prevê o resultado de jogos do Campeonato Brasileiro. Ele lê os jogos de arquivos CSV, calcula as features usando só jogos anteriores a cada partida, treina uma regressão de Poisson com a temporada 2025 e testa com os jogos já realizados de 2026.

O ponto de partida foi o commit b9e399f. Nele, os anos 2025 e 2026 estavam escritos em src/features.py (valor padrão do argumento temporadas), em src/app.py e em src/avaliar_modelo.py. Os nomes dos arquivos já eram montados a partir do ano (partidas_{ano}.csv): o que estava fixo era o ano.

A tarefa foi esta: a temporada de treino e a de teste passam a ser parâmetros. Escolhi porque é pequena e tem critério de aceitação fácil de conferir.

Segui as fases do Spec Kit (constitution, specify, plan, tasks e implement). Os documentos ficaram em .spec/, uma pasta do próprio projeto, que não é a .specify/ usada pela CLI do Spec Kit. Em termos da Böckeler, foi um uso spec-first: as specs foram escritas antes da mudança e não foram atualizadas depois (as tarefas continuam sem marcação).

Passo a passo e artefatos

Constitution (.spec/constitution.md). Quatro regras: código compatível com Python 3.10+; zero data leakage, com features só a partir de eventos anteriores à rodada prevista; testes automatizados em /tests para novos métodos, lógicas de features ou mudanças de arquitetura; e README.md refletindo o estado atual e as limitações do modelo.

Specify (.spec/specify.md). Descreve o problema (anos fixos no código impedem usar 2027 sem editar vários lugares), o objetivo (receber ano de treino e de teste como parâmetros) e quatro critérios de aceite:

  1. os testes de tests/test_features.py continuam passando;
  2. a avaliação com 2025 e 2026 repete 48,0% de acurácia e 1,020 de log loss;
  3. o painel Streamlit renderiza como antes;
  4. nenhum ano sobra solto na lógica de carregamento de dados.

Plan (.spec/plan.md). Três mudanças: features.py passa a receber ano_treino e ano_teste; app.py ganha as constantes ANO_TREINO e ANO_TESTE; avaliar_modelo.py lê os anos de sys.argv, com 2025 e 2026 como padrão.

Tasks (.spec/tasks.md). Três tarefas, uma para cada arquivo do plano.

Implement (commit b21411b, 28/10/2026, 21:51). A mudança alterou src/app.py (8 linhas adicionadas, 4 removidas) e src/avaliar_modelo.py (15 adicionadas, 13 removidas). O features.py não mudou: o plano supunha chamadas de leitura com caminho fixo nesse arquivo, mas carregar_partidas já recebia temporadas e montava os nomes com o ano. Existe um único commit da implementação, então não há registro de erros ou correções no meio do caminho.

Verificação dos resultados

Comparei o commit com os critérios da spec e com as regras da constitution.

Item Resultado
Testes continuam passando Sim: os 3 testes de tests/test_features.py passam
Mesmos resultados com 2025 e 2026 Sim: python src/avaliar_modelo.py, com e sem os argumentos 2025 2026, repete 48,0% de acurácia e 1,0201 de log loss, com saída idêntica à de antes
Painel Streamlit renderiza como antes Sim, aberto no navegador com streamlit run src/app.py (conferência manual, sem registro em experimento/)
Nenhum ano solto na lógica de carregamento Parcial: features.py mantém temporadas=(2025, 2026) como padrão; app.py e avaliar_modelo.py mantêm 2025 e 2026 como padrão, como o plano previa
Constitution, regra 3: testes para o que é novo Não: nenhum teste foi adicionado
Constitution, regra 4: README atualizado Não: o README não menciona os argumentos de avaliar_modelo.py

O arquivo experimento/depois_previsoes.txt é idêntico ao antes_previsoes.txt, mas esse teste não passa por app.py nem por avaliar_modelo.py, que são os arquivos alterados. O que valida a mudança é a comparação das métricas.

Outras temporadas (2027)

Não existe partidas_2027.csv no repositório, e ainda não há resultados de 2027. Em testes com arquivos criados só para esta verificação (nenhum está no repositório), o comportamento foi este:

  • arquivo vazio: o avaliar_modelo.py termina com EmptyDataError e o app mostra a mensagem de erro de leitura;
  • arquivo só com cabeçalho: os dois terminam com ValueError do StandardScaler, por falta de amostras;
  • calendário com jogos sem placar: o app abre e mostra as previsões, mas exibe 0,0% de acurácia, que na verdade significa "sem jogos para avaliar"; o avaliar_modelo.py falha pelo mesmo ValueError.

O parâmetro funciona para anos que têm dados. O código ainda não trata uma temporada sem jogos realizados, e carregar um arquivo não valida o modelo: sem resultados não há acurácia nem log loss.

Tempo

O arquivo experimento/tempo.txt registra o início às 21:32, e o commit da implementação saiu às 21:51. Foram cerca de 20 minutos no total, com documentos, mudança no código e verificação. Não separei o tempo de cada etapa.

Benefícios e limitações

Os autores que li apontam estes benefícios. No texto do GitHub Blog:

  • Menos adivinhação. Com um prompt vago o modelo presume muitos requisitos, e a spec reduz isso.
  • O quê separado do como. A spec fixa a intenção, e o plano pode mudar sem reescrever tudo.
  • Tarefas testáveis. Cada tarefa pequena pode ser implementada e testada isoladamente.

E estas limitações. Na análise da Böckeler:

  • Tamanho do processo. O Kiro transformou um bug pequeno em 4 user stories e 16 critérios de aceitação. O spec-kit pareceu exagerado para uma funcionalidade de tamanho médio.
  • Controle. O agente ignorou instruções e, em um caso, recriou classes que já existiam.
  • Rigidez. Ela compara o spec-as-source com o MDD e teme herdar as desvantagens do MDD e dos LLMs, ou seja, a rigidez de um e a imprevisibilidade do outro.

O Radar da Thoughtworks acrescenta que podemos estar reaprendendo uma "lição amarga": escrever à mão regras detalhadas para a IA não escala.

No meu experimento, os critérios de aceite da spec funcionaram como lista de conferência. Compará-los com o commit mostrou lacunas que eu não tinha percebido: o padrão (2025, 2026) que continua em features.py, a falta de teste novo e de atualização do README, que eram regras da minha própria constitution, e o resultado do painel sem registro. Também mostrou que o plano descrevia o features.py de um jeito diferente do que ele era. A spec ajudou a achar esses problemas, mas depois da implementação, e não os evitou.

Sobre o tamanho do processo, foram quatro documentos curtos (40 linhas ao todo) para uma mudança em dois arquivos. É a proporção que a Böckeler questiona. Não comparei com um prompt direto.

Conclusão

Para uma funcionalidade pequena, como a parametrização de temporadas, o SDD me pareceu excessivo: quatro documentos para alterar dois arquivos. Essa é minha impressão, e ela se apoia em evidências limitadas. O tempo total foi de cerca de 20 minutos, sem divisão por etapa, e não repeti a tarefa com um prompt direto, então não posso dizer que o SDD foi mais lento ou menos eficiente do que essa alternativa.

O que a spec deu de concreto foi uma lista de critérios para conferir o resultado, e foi essa conferência que mostrou o que faltava. Minha hipótese é que o SDD compensa mais em mudanças com vários pontos de falha e critérios de aceitação mais amplos, como uma pipeline de web scraping ou a migração do banco de dados, e menos em refatorações pequenas, hotfixes ou na adição de um único parâmetro. Não testei esses cenários.

Este é um caso isolado (uma funcionalidade, um projeto) e não serve de regra geral. Os documentos, o código e os registros do experimento estão na branch experimento-sdd, para quem quiser conferir.

Referências

來源:DEV Community · dev.to