tsma – Linha de defesa contra regressoes em codigo legado

Se voce quer refatorar codigo legado com IA mas nao ha testes, se o seu LLM escreve testes mas para no meio, se quer rastrear cobertura mecanicamente enquanto controla o agente — tsma constroi essa linha de defesa.

Como refatorar codigo sem testes?

Voce herdou uma base de codigo legado com 100.000 linhas. Nao ha testes. Quer refatorar, mas tocar em qualquer coisa pode quebrar algo. Escrever testes exige entender o codigo, e entender o codigo exige documentacao – que tambem nao existe.

Ninguem toca. Apodrece ainda mais.

Todo codigo legado do mundo esta preso nesse impasse. 60-80% do orcamento de TI das empresas Fortune 500 vai para manutencao de sistemas legados. 42% do tempo dos desenvolvedores e consumido lidando com divida tecnica.

E se um LLM pudesse escrever os testes por voce?


Os problemas de delegar testes a um LLM

Peca a um LLM para “escrever testes para esta funcao” e ele produz algo. Os problemas sao tres.

Primeiro, ele nao sabe por onde comecar. Quando ha 527 funcoes, comeca pela numero 1 em ordem? Pela mais critica? Nao ha criterio.

Segundo, voce nao pode verificar a qualidade dos testes. Os testes do LLM passam. Mas estao realmente verificando o comportamento da funcao, ou sao cascas vazias que apenas chamam a funcao sem nenhum assert? Voce teria que ler cada um manualmente para saber.

Terceiro, sem feedback, os testes do LLM estagnam em 60-70%. De acordo com o estudo empirico de Schafer et al. (2023), a mediana dos testes gerados por LLM e 70.2% de statement coverage e 52.8% de branch coverage. Apenas dizer “teste esta funcao” nao alcanca 100% de cobertura de branches. Voce precisa indicar quais branches estao faltando para ele preencher as lacunas.

Nao e que LLMs nao consigam escrever testes. O problema e a ausencia de uma estrutura que diga ao LLM o que escrever e quao bem ele escreveu.


tsma: Um trilho de testes acionado por um unico comando

tsma e uma ferramenta CLI que indexa todas as funcoes de um projeto, detecta a existencia de testes, mede a cobertura e fornece feedback preciso a agentes LLM.

O agente precisa conhecer exatamente um comando:

$ tsma next

Este unico comando aciona todo o ciclo:

$ tsma next          # Mostra a proxima funcao sem teste
  → Escreva um teste
$ tsma next          # Detecta o novo teste, executa, mede a cobertura
  → 100%? PASS, avanca para a proxima funcao
  → <100%? Mostra branches nao cobertos com numeros de linha
$ tsma next          # Re-mede o teste revisado
  → Melhorou ou nao, marca DONE e segue em frente

Repita ate que “All functions complete!” apareca.


Validado em 527 funcoes

tsma foi aplicado a um projeto Go real com 527 funcoes.

ResultadoQuantidadeProporcao
PASS (100% cobertura de branches)24646.7%
DONE (best-effort)28153.3%
TODO (nao processado)00%

246 funcoes alcancaram 100% de cobertura de branches. As 281 restantes nao chegaram a 100%, mas testes foram escritos ate onde foi possivel.

Por que algumas funcoes nao conseguem alcancar 100%?


Funcoes que alcancam 100% e as que nao alcancam

Se uma funcao pode alcancar 100% de cobertura de branches depende de como ela recebe suas dependencias.

Interface (mockable) – 100% alcancavel:

type Handler struct {
    svc AuthSvc              // interface -- substituivel por um mock
}

Injete um mock nos testes e voce pode controlar cada caminho:

svc := mocks.NewMockAuthSvc(ctrl)
svc.EXPECT().Login(...).Return(result, nil)   // caminho de sucesso
svc.EXPECT().Login(...).Return(nil, err)      // caminho de falha

Tipo concreto (not mockable) – 100% impossivel:

type Handler struct {
    svc *service.SMSImportService    // ponteiro para struct -- nao substituivel
}

A implementacao real executa com dependencias internas como bancos de dados, APIs externas, etc. Voce nao pode forcar erros especificos ou valores de retorno especificos. Branches que dependem desses resultados sao inalcancaveis por testes unitarios.

Resposta do tsma: Apos o feedback sobre branches nao cobertos, tenta mais uma vez. Se os branches continuam inalcancaveis, aceita DONE. Isso nao e uma limitacao da ferramenta – reflete a testabilidade do codigo. O dilema do codigo legado sistematizado por Feathers (2004) – “para mudar o codigo voce precisa de testes, para adicionar testes voce precisa mudar o codigo” – se resolve quebrando dependencias e introduzindo interfaces (DI). Introduzir interfaces tornaria 100% possivel, mas isso significa modificar o codigo original.


Feedback transforma dramaticamente os testes do LLM

O valor central do tsma nao e a indexacao nem a medicao de cobertura. E indicar ao LLM exatamente quais branches nao estao cobertos, com numero de linha.

Sem feedback:

"Escreva testes para a funcao ListContracts"
→ LLM testa apenas o happy path
→ Cobertura 60-70%

Com feedback:

"Escreva testes para a funcao ListContracts"
→ Cobertura 65% (11/17)
→ UNCOVERED:
    line 41 -- if params.Status != nil
    line 44 -- if params.BuildingId != nil
    line 70 -- if err != nil (CountSummary)
→ LLM adiciona testes cobrindo exatamente esses branches
→ Cobertura 100%

Mesmo LLM. A unica diferenca e a presenca de feedback. Tres linhas de numeros de linha separam 60% de 100%. CoverUp (Pizzorno & Berger, 2024) demonstrou empiricamente o mesmo principio. Ao inserir repetidamente os resultados da analise de cobertura nos prompts e focar a atencao do LLM nas linhas nao cobertas, alcancaram uma mediana de line coverage por modulo de 81% – uma melhoria de 19pp em relacao a linha base sem feedback.


O progresso sobrevive mesmo quando o agente morre

Agentes LLM travam. Limites de tokens, erros de rede, quedas de sessao. Voce nao pode processar 527 funcoes em uma unica sessao.

tsma persiste o progresso em .tsma/session.json.

$ tsma status

527 functions
PASS:  246 (46.7%)
DONE:  281 (53.3%)
TODO:    0 (0.0%)

Se o agente morre na funcao #200? Um novo agente executa tsma next e continua da #201. session.json e o checkpoint.

Multiplos agentes podem se revezar sem conflitos. Cada funcao e atomica.


A sessao e cache; os arquivos fonte sao a verdade

Um dos principios de design do tsma: a sessao e um cache, e os arquivos fonte sao a fonte da verdade.

Se voce deletar um arquivo de teste, mesmo que session.json o registre como PASS, aquela funcao volta para TODO. A sessao nunca se dessincroniza da realidade.

Principio:
  Mesmo que session.json diga "PASS"
  Se o arquivo de teste esta ausente → TODO
  Se o arquivo fonte mudou → alvo de re-medicao

Instrucoes para o agente LLM

O agente precisa de exatamente 6 linhas de instrucoes:

1. Execute tsma next
2. Se TODO -- leia a funcao e escreva um teste
3. Se o teste falhar -- leia o erro e corrija o teste
4. Se branches nao cobertos sao mostrados -- adicione testes cobrindo esses branches
5. Se PASS/DONE -- a proxima funcao e mostrada automaticamente
6. Repita ate que "All functions complete!" apareca

O unico comando que o agente precisa conhecer e tsma next. O CLI restringe o resto.


Trens e trilhos

Vibe coding e um trem. E rapido. Mas sem trilhos, descarrila.

Todas as ferramentas de programacao com IA estao focadas em tornar o trem mais rapido. Modelos maiores, agentes mais inteligentes, melhores prompts. Mas quanto mais rapido o trem, pior o descarrilamento.

tsma e o trilho. O LLM gera testes (Neural), e o CLI define “ate aqui e nao mais” (Symbolic Constraint). A criatividade do LLM permanece intacta, mas a qualidade dos resultados e imposta pela maquina.

Antestsma
Escrita de testesHumano (lento) ou LLM (caotico)LLM escreve, CLI verifica
Por onde comecar?Humano decideCLI determina a ordem
Controle de qualidadeHumano revisaCLI mede cobertura
FeedbackNenhumNumeros de linha de branches nao cobertos
Rastreamento de progressoNenhumsession.json automatico

O LLM gera livremente. Mas corre apenas sobre o trilho chamado tsma next.


Suporte a linguagens

LinguagemIndexadorTest RunnerCobertura
Gogo/astgo testgo test -coverprofile
TypeScriptregexnpx vitest / npx jestc8 / istanbul
Pythonregexpytestcoverage.py

Go usa um parser AST para extracao precisa de funcoes. TypeScript e Python usam extracao baseada em regex.

Arquivos gerados (*_gen.go, *.pb.go), arquivos de teste e vendor/node_modules sao automaticamente excluidos da indexacao.


Instalacao e uso

make install
cd your-legacy-project
tsma next

So isso.

MIT License. github.com/park-jun-woo/tsma


Referencias

  • Schafer, M., Nadi, S., Eghbali, A., & Tip, F. (2023). An Empirical Evaluation of Using Large Language Models for Automated Unit Test Generation. IEEE Transactions on Software Engineering, 50(1), 85–105. arXiv:2302.06527
  • Pizzorno, J. A., & Berger, E. D. (2024). CoverUp: Coverage-Guided LLM-Based Test Generation. arXiv preprint arXiv:2403.16218. arXiv:2403.16218
  • Ryan, G., Jain, S., Shang, M., Wang, S., Ma, X., Ramanathan, M. K., & Ray, B. (2024). Code-Aware Prompting: A Study of Coverage-Guided Test Generation in Regression Setting using LLM. Proceedings of the ACM on Software Engineering (FSE 2024), 1(FSE), 951–971. ACM DL
  • Lemieux, C., Inala, J. P., Lahiri, S. K., & Sen, S. (2023). CodaMOSA: Escaping Coverage Plateaus in Test Generation with Pre-trained Large Language Models. ICSE 2023, 951–963. ACM DL
  • Feathers, M. C. (2004). Working Effectively with Legacy Code. Prentice Hall. ACM DL
  • Besker, T., Martini, A., & Bosch, J. (2018). Technical Debt Cripples Software Developer Productivity. TechDebt 2018, 105–114. ACM DL
  • Stripe. (2018). The Developer Coefficient. PDF
  • U.S. Government Accountability Office. (2019). Information Technology: Agencies Need to Develop Modernization Plans for Critical Legacy Systems. GAO-19-471. GAO
  • Tornhill, A., & Borg, M. (2022). Code Red: The Business Impact of Code Quality. TechDebt 2022, 11–20. arXiv:2203.04374
  • Peng, S., Kalliamvakou, E., Cihon, P., & Demirer, M. (2023). The Impact of AI on Developer Productivity: Evidence from GitHub Copilot. arXiv:2302.06590

Relacionado: Ratchet Pattern – Como fazer um agente terminar o trabalho – O padrao por tras do tsma. Por que verificacao mecanica supera o julgamento do LLM.

Relacionado: O QI do modelo importa menos que a topologia de feedback – Por que a estrutura de feedback determina resultados mais que o desempenho do modelo.

Changelog

  • 2026-05-14: Versão inicial