Reparo v1.0: he automatizado la corrección de deuda técnica en el pipeline de CI/CD
Llevo años conviviendo con lo mismo en cada proyecto: SonarQube acumula cientos de issues, el equipo los reconoce, pero nunca hay un sprint donde se prioricen frente a las features del producto. La deuda técnica crece, el código se vuelve más frágil y el coste de mantenimiento se dispara. Es un problema sistémico, no de voluntad.
Así que decidí construir algo que lo resolviera de verdad. Reparo es una herramienta CLI escrita en Rust que conecta SonarQube con Claude AI para corregir issues de calidad de código de forma autónoma. Escanea, prioriza, corrige, valida y abre un PR — todo sin intervención humana.
El problema real
No voy a descubrir nada nuevo: la deuda técnica es el elefante en la habitación de cualquier equipo de desarrollo. Todos sabemos que está ahí. Los dashboards de SonarQube lo gritan. Pero entre la presión por entregar, las reuniones y los deadlines, el refactoring se queda siempre para «el próximo sprint». Y el próximo sprint nunca llega.
He probado los enfoques habituales. Dedicar un porcentaje del sprint a deuda técnica. Hacer «cleanup weeks». Asignar issues a desarrolladores junior como ejercicio formativo. Ninguno escala. Ninguno es sostenible.
Lo que sí escala es automatizarlo. Si tenemos CI/CD para compilar, testear y desplegar, ¿por qué no para limpiar?
Qué hace Reparo exactamente
Reparo no es un wrapper bonito alrededor de un LLM. Es un orquestador que sigue un pipeline de validación estricto. Cada corrección pasa por build, tests, linter y re-escaneo en SonarQube antes de aceptarse. Si algo falla en cualquier punto, revierte y pasa al siguiente issue.
El flujo completo es este:
Primero, lee la configuración desde un archivo reparo.yaml versionado junto al proyecto, valida la conectividad con SonarQube y detecta qué edición tienes (Community, Developer o Enterprise). Después crea una rama de fix, ejecuta el formateador (Prettier, Black, Spotless, lo que uses) y hace un commit separado con los cambios de formato para que no se mezclen con las correcciones.
Antes de tocar nada, comprueba que el build y los tests pasan. Si la cobertura está por debajo del umbral configurado, genera tests automáticamente hasta alcanzarlo. Esto es clave: corregir código sin tests es jugar a la ruleta rusa.
Luego entra en el bucle de corrección. Para cada issue de SonarQube, ordenados de más crítico a menos, comprueba la cobertura en las líneas afectadas, genera tests si hace falta, aplica el fix con Claude, y ejecuta toda la cadena de validación: clean, format, build, test, lint. Si pasa todo, re-escanea con SonarQube para confirmar que el issue ha desaparecido. Si se confirma, commit. Si no, revert.
Después hay una fase de deduplicación: detecta bloques de código duplicado vía SonarQube y los refactoriza, siempre verificando que los tests sigan en verde.
Al final, crea un PR en GitHub con tres archivos de documentación: un resumen ejecutivo, un changelog de deuda técnica y un listado de lo que necesita revisión manual.
Lo que me costó más: proteger los tests
Este fue el punto donde más iteré. El instinto de un LLM cuando un test falla después de un fix es… modificar el test. Y eso es exactamente lo que no quieres.
Reparo tiene una regla estricta: si Claude modifica un archivo de test durante una corrección, esos cambios se revierten automáticamente. Si el fix sigue pasando los tests originales sin la modificación, se acepta. Si no, se marca como pendiente de revisión humana y se documenta en REVIEW_NEEDED.md.
Los tests son la fuente de verdad. Las correcciones se adaptan al comportamiento esperado, no al revés. Sin esta garantía, la herramienta no tendría ningún valor.
Self-healing: cuando el fix rompe algo
Otro aprendizaje: los fixes no siempre compilan a la primera. A veces Claude genera código que rompe el build, o que hace fallar un test que no estaba directamente relacionado.
Reparo tiene lógica de reintento integrada. Si el build falla, le pasa el error de compilación a Claude y le pide que lo corrija, sin tocar tests. Lo mismo con errores de lint. Todo configurable en número de intentos. En la práctica, la mayoría de fixes que necesitan un reintento se resuelven en el segundo o tercer intento.
Configuración
Todo se define en un reparo.yaml que puedes versionar con tu proyecto. Soporta interpolación de variables de entorno con ${VARIABLE}, que es lo que necesitas para CI/CD donde los tokens y URLs se inyectan como secrets.
Un ejemplo para un proyecto Angular:
sonar:
project_id: "my-angular-app"
url: "${SONAR_URL}"
token: "${SONAR_TOKEN}"
execution:
max_issues: 10
min_coverage: 80
min_file_coverage: 50
claude_timeout: 600
commands:
setup: "npm install"
build: "npm run build"
test: "npm test"
coverage: "npm run test:coverage"
format: "npx prettier --write ."
lint: "npx eslint src --max-warnings=0"
Al definir tus propios comandos, funciona con cualquier stack: Node.js, Java con Maven o Gradle, Python con pytest, lo que sea. Si tiene build, test y lint invocables por CLI, Reparo lo soporta.
La prioridad de configuración es flags CLI > variables de entorno > YAML > valores por defecto.
Dónde encaja en tu pipeline
El caso de uso que más sentido tiene es ejecutarlo como job programado. En mi caso lo tengo configurado como una GitHub Action que se ejecuta semanalmente: los lunes a las 2 de la mañana arranca, corrige los issues más críticos y deja un PR preparado para que el equipo lo revise el martes.
name: Fix Tech Debt
on:
schedule:
- cron: '0 2 * * 1'
workflow_dispatch:
jobs:
fix-debt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Fix technical debt
env:
SONAR_URL: ${{ secrets.SONAR_URL }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
REPARO_MAX_ISSUES: "10"
REPARO_TIMEOUT: "1800"
run: reparo --path . --dangerously-skip-permissions
También lo he usado en modo dry-run antes de merges importantes, para tener una foto de qué deuda técnica se está introduciendo.
Por qué Rust
Podría haberlo hecho en Python o TypeScript. Elegí Rust por dos razones: rendimiento en el parsing de reportes de cobertura (lcov puede ser pesado en proyectos grandes) y fiabilidad. Reparo ejecuta operaciones de git, llamadas a APIs, procesos de build y tests en paralelo o secuencia. Los errores en este tipo de orquestación son difíciles de depurar en lenguajes dinámicos. El sistema de tipos de Rust me ha evitado una cantidad considerable de bugs que en otro lenguaje habrían aparecido en producción.
Limitaciones y lo que no es
Reparo no es una bala de plata. Hay cosas que no hace y que no pretende hacer.
No sustituye la revisión humana. Cada PR que genera debe pasar por code review como cualquier otro. Lo que sí hace es quitar de encima el trabajo mecánico de ir issue por issue, corregir, comprobar que no se ha roto nada y documentar.
No entiende la intención de negocio de tu código. Si un issue de SonarQube requiere una decisión arquitectónica, Reparo lo intentará y probablemente el fix no pasará la validación, quedando documentado como NeedsReview. Eso está bien: es exactamente lo que debería ocurrir.
Y depende de que tengas tests. Sin una suite de tests razonable, las correcciones no se pueden validar con confianza. Por eso el paso de coverage boost existe: para asegurar un mínimo antes de empezar.
El proyecto
Reparo es open source bajo licencia MIT. Está en GitHub y acepto feedback, issues y contribuciones. Si tu equipo tiene un SonarQube lleno de issues que nadie toca, dale una vuelta. Es posible que te sorprenda cuánto puede resolver una herramienta así ejecutándose una vez a la semana.
0 comentarios