tsma – Linea de defensa contra regresiones en codigo legado Image: AI generated

Si quieres refactorizar código legado sin tests usando IA, si le pediste al LLM que escribiera tests y solo hizo la mitad antes de detenerse, si quieres rastrear la cobertura mecánicamente mientras controlas al agente – tsma construye esa línea de defensa.

Como refactorizar codigo sin pruebas?

Heredaste una base de codigo legado de 100.000 lineas. No hay pruebas. Quieres refactorizar, pero tocar cualquier cosa podria romper algo. Escribir pruebas requiere entender el codigo, y entender el codigo requiere documentacion, que tampoco existe.

Nadie lo toca. Se pudre aun mas.

Todo el codigo legado del mundo esta atrapado en este punto muerto. El 60-80% del presupuesto de TI de las empresas Fortune 500 se destina al mantenimiento de sistemas legados. El 42% del tiempo de los desarrolladores se consume en gestionar deuda tecnica.

Y si un LLM pudiera escribir las pruebas por ti?


Los problemas de delegar las pruebas a un LLM

Pide a un LLM “escribe pruebas para esta funcion” y producira algo. Los problemas son tres.

Primero, no sabe por donde empezar. Cuando hay 527 funciones, empiezas por la numero 1 en orden? Por la mas critica? No hay criterio.

Segundo, no puedes verificar la calidad de las pruebas. Las pruebas del LLM pasan. Pero realmente verifican el comportamiento de la funcion, o son cascarones vacios que solo llaman a la funcion sin ningun assert? Tendrias que leer cada una manualmente para saberlo.

Tercero, sin retroalimentacion, las pruebas del LLM se estancan en 60-70%. Segun el estudio empirico de Schafer et al. (2023), la mediana de las pruebas generadas por LLM es 70.2% de statement coverage y 52.8% de branch coverage. Solo decir “prueba esta funcion” no alcanza el 100% de cobertura de ramas. Necesitas indicarle que ramas faltan para que complete los huecos.

No es que los LLM no puedan escribir pruebas. El problema es la ausencia de una estructura que le diga al LLM que escribir y que tan bien lo escribio.


tsma: Un rail de pruebas impulsado por un solo comando

tsma es una herramienta CLI que indexa todas las funciones de un proyecto, detecta la existencia de pruebas, mide la cobertura y proporciona retroalimentacion precisa a agentes LLM.

El agente necesita conocer exactamente un comando:

$ tsma next

Este unico comando impulsa todo el ciclo:

$ tsma next          # Muestra la siguiente funcion sin prueba
  → Escribe una prueba
$ tsma next          # Detecta la nueva prueba, la ejecuta, mide la cobertura
  → 100%? PASS, pasa a la siguiente funcion
  → <100%? Muestra las ramas no cubiertas con numeros de linea
$ tsma next          # Re-mide la prueba revisada
  → Mejore o no, marca DONE y continua

Repite hasta que aparezca “All functions complete!”.


Validado en 527 funciones

tsma se aplico a un proyecto Go real con 527 funciones.

ResultadoCantidadProporcion
PASS (100% cobertura de ramas)24646.7%
DONE (best-effort)28153.3%
TODO (sin procesar)00%

246 funciones alcanzaron el 100% de cobertura de ramas. Las 281 restantes no llegaron al 100%, pero se escribieron pruebas hasta donde fue posible.

Por que algunas funciones no pueden alcanzar el 100%?


Funciones que alcanzan el 100% y las que no

Si una funcion puede alcanzar el 100% de cobertura de ramas depende de como recibe sus dependencias.

Interfaz (mockable) – 100% alcanzable:

type Handler struct {
    svc AuthSvc              // interface -- reemplazable con un mock
}

Inyecta un mock en las pruebas y puedes controlar cada camino:

svc := mocks.NewMockAuthSvc(ctrl)
svc.EXPECT().Login(...).Return(result, nil)   // camino de exito
svc.EXPECT().Login(...).Return(nil, err)      // camino de fallo

Tipo concreto (not mockable) – 100% imposible:

type Handler struct {
    svc *service.SMSImportService    // puntero a struct -- no reemplazable
}

La implementacion real se ejecuta con dependencias internas como bases de datos, APIs externas, etc. No puedes forzar errores especificos ni valores de retorno especificos. Las ramas que dependen de esos resultados son inalcanzables por pruebas unitarias.

Respuesta de tsma: Despues de la retroalimentacion sobre ramas no cubiertas, intenta una vez mas. Si las ramas siguen siendo inalcanzables, acepta DONE. Esto no es una limitacion de la herramienta – refleja la testabilidad del codigo. El dilema del codigo legado sistematizado por Feathers (2004) – “para cambiar el codigo necesitas pruebas, para agregar pruebas necesitas cambiar el codigo” – se resuelve rompiendo dependencias e introduciendo interfaces (DI). Introducir interfaces haria posible el 100%, pero eso significa modificar el codigo original.


La retroalimentacion transforma dramaticamente las pruebas del LLM

El valor central de tsma no es la indexacion ni la medicion de cobertura. Es indicarle al LLM exactamente que ramas no estan cubiertas, con numero de linea.

Sin retroalimentacion:

"Escribe pruebas para la funcion ListContracts"
→ LLM prueba solo el happy path
→ Cobertura 60-70%

Con retroalimentacion:

"Escribe pruebas para la funcion 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 agrega pruebas cubriendo exactamente esas ramas
→ Cobertura 100%

El mismo LLM. La unica diferencia es la presencia de retroalimentacion. Tres lineas de numeros de linea separan el 60% del 100%. CoverUp (Pizzorno & Berger, 2024) demostro empiricamente el mismo principio. Al insertar repetidamente los resultados del analisis de cobertura en los prompts y enfocar la atencion del LLM en las lineas no cubiertas, lograron una mediana de line coverage a nivel de modulo del 81% – una mejora de 19pp respecto a la linea base sin retroalimentacion.


El progreso sobrevive incluso cuando el agente muere

Los agentes LLM se caen. Limites de tokens, errores de red, sesiones cortadas. No puedes procesar 527 funciones en una sola sesion.

tsma persiste el progreso en .tsma/session.json.

$ tsma status

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

Si el agente muere en la funcion #200? Un nuevo agente ejecuta tsma next y continua desde la #201. session.json es el punto de control.

Multiples agentes pueden turnarse sin conflictos. Cada funcion es atomica.


La sesion es cache; los archivos fuente son la verdad

Uno de los principios de diseno de tsma: la sesion es un cache, y los archivos fuente son la fuente de verdad.

Si eliminas un archivo de prueba, incluso si session.json lo registra como PASS, esa funcion vuelve a TODO. La sesion nunca se desincroniza de la realidad.

Principio:
  Incluso si session.json dice "PASS"
  Si falta el archivo de prueba → TODO
  Si el archivo fuente cambio → objetivo de re-medicion

Instrucciones para el agente LLM

El agente necesita exactamente 6 lineas de instrucciones:

1. Ejecuta tsma next
2. Si es TODO -- lee la funcion y escribe una prueba
3. Si la prueba falla -- lee el error y corrige la prueba
4. Si se muestran ramas no cubiertas -- agrega pruebas cubriendo esas ramas
5. Si es PASS/DONE -- la siguiente funcion se muestra automaticamente
6. Repite hasta que aparezca "All functions complete!"

El unico comando que el agente necesita conocer es tsma next. El CLI restringe el resto.


Trenes y vias

Vibe coding es un tren. Es rapido. Pero sin vias, descarrila.

Todas las herramientas de programacion con IA se centran en hacer el tren mas rapido. Modelos mas grandes, agentes mas inteligentes, mejores prompts. Pero cuanto mas rapido va el tren, peor es el descarrilamiento.

tsma es la via. El LLM genera pruebas (Neural), y el CLI define “hasta aqui y no mas” (Symbolic Constraint). La creatividad del LLM se mantiene intacta, pero la calidad de los resultados la impone la maquina.

Antestsma
Escritura de pruebasHumano (lento) o LLM (caotico)LLM escribe, CLI verifica
Por donde empezar?Humano decideCLI determina el orden
Control de calidadHumano revisaCLI mide cobertura
RetroalimentacionNingunaNumeros de linea de ramas no cubiertas
Seguimiento del progresoNingunosession.json automatico

El LLM genera libremente. Pero corre solo sobre la via llamada tsma next.


Soporte de lenguajes

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

Go usa un parser AST para extraccion precisa de funciones. TypeScript y Python usan extraccion basada en regex.

Los archivos generados (*_gen.go, *.pb.go), archivos de prueba y vendor/node_modules se excluyen automaticamente de la indexacion.


Instalacion y uso

make install
cd your-legacy-project
tsma next

Eso es todo.

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 hacer que un agente termine el trabajo – El patron detras de tsma. Por que la verificacion mecanica supera al juicio del LLM.

Relacionado: El IQ del modelo importa menos que la topologia de retroalimentacion – Por que la estructura de retroalimentacion determina los resultados mas que el rendimiento del modelo.

Registro de cambios

  • 2026-05-14: Versión inicial