
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.
| Resultado | Quantidade | Proporcao |
|---|---|---|
| PASS (100% cobertura de branches) | 246 | 46.7% |
| DONE (best-effort) | 281 | 53.3% |
| TODO (nao processado) | 0 | 0% |
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.
| Antes | tsma | |
|---|---|---|
| Escrita de testes | Humano (lento) ou LLM (caotico) | LLM escreve, CLI verifica |
| Por onde comecar? | Humano decide | CLI determina a ordem |
| Controle de qualidade | Humano revisa | CLI mede cobertura |
| Feedback | Nenhum | Numeros de linha de branches nao cobertos |
| Rastreamento de progresso | Nenhum | session.json automatico |
O LLM gera livremente. Mas corre apenas sobre o trilho chamado tsma next.
Suporte a linguagens
| Linguagem | Indexador | Test Runner | Cobertura |
|---|---|---|---|
| Go | go/ast | go test | go test -coverprofile |
| TypeScript | regex | npx vitest / npx jest | c8 / istanbul |
| Python | regex | pytest | coverage.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