Contexto y objetivo
Las bases de código legacy suelen estancarse por falta de visibilidad y foco. NDepend resuelve la visibilidad (métricas, reglas, dependencias, hotspots) y la IA acelera la ejecución (priorización y refactors guiados con tests). Mi objetivo: comprobar si este combo reduce deuda técnica de forma significativa en menos de una jornada.
Hipótesis.
Si combino la visibilidad de NDepend con la ejecución asistida por IA, reduzco deuda técnica y complejidad de forma relevante en <N> horas, manteniendo el comportamiento.
Diseño del experimento
Código de partida (demo):
RefactorLab.LegacyDemo (con smells intencionados: estado global, SQL sin parametrizar, métodos largos, duplicación, god objects, etc.).
Herramientas.
- .NET 8 + VS 2022.
- NDepend (trial vale).
- IA generativa (ChatGPT u otra).
Métricas a observar.
- Nº de violaciones (por severidad).
- Complejidad ciclomática media por método.
- Duplicación (clones).
- Acoplamiento entre namespaces / ciclos de dependencias.
- Debt (minutos estimados por NDepend).
Procedimiento paso a paso
1. “Foto antes” (NDepend)
-
Compila la solución y ejecuta NDepend.
- Genera el informe HTML y revisa el Dashboard.

- Exporta a JSON.

2. Priorización con IA (plan en tres oleadas)
Pega el CSV/JSONL y usa el Prompt A:
Contexto: Violaciones NDepend con columnas rule, severity, debt, file, member, line.
1) Agrupa por severity y debt; ordénalo por impacto (alto→bajo).
2) Propón 3 oleadas: Quick Wins (≤30 min), Media (30–90), Alta (>90).
3) Señala dependencias (p.ej., arreglar UserRepository antes que GodService).
4) Devuélveme [{oleada,item,archivo,miembro,regla,deuda}] + checklist.


3. Refactor por archivo (Oleada 1)
Para cada ítem, abre el archivo y usa el Prompt B:
Contexto: Archivo {ruta}. Viola: {lista_reglas}. Código actual: {pegado}.
Objetivo: refactor sin cambiar el comportamiento observable.
Aplica: parametrizar SQL; using/await using; separar responsabilidades (SRP);
reducir longitud/cc; evitar estado global.
Entrega: 1) explicación, 2) diff unified, 3) archivo final, 4) tests.
Hotspots típicos de la demo:
-
Infrastructure/UserRepository.cs
: SQL concatenado,Open/Close
manual, acoplamiento aGlobals
. -
Core/Domain/GodService.cs
: god object, excepciones tragadas. -
Core/MathHelper.cs
: duplicación yO(n²)
innecesaria, magic numbers. -
Core/Globals.cs
: estado global mutable y secretos en código.

4. Medición intermedia
-
Compila y ejecuta tests.
-
Re-ejecuta NDepend y exporta
violations-after-o1.csv/xml
. -
Compara “antes vs después” en las métricas clave.

5. Oleadas 2 y 3
Repite 3 y 4 para cerrar los cambios de complejidad media/alta.
6. Sostenibilidad: quality gates y CQLinq
Pide a la IA reglas y umbrales con el Prompt:
Genera reglas CQLinq y quality gates para impedir regresiones
(métodos largos/cc alta, uso de Globals, SQL concatenado, clones).
Incluye pasos para integrarlo en CI (build+tests+NDepend) y umbrales iniciales.
Por ejemplo:
// <Name>Resultados (antes/después) — Snapshot vs Baseline (OK con primer col. de tipo Assembly)</Name>
// Métrica | Antes (baseline) | Después (actual) | Mejora (Antes - Después)
let hasBaseline = (codeBase.OlderVersion() != null)
let hoursPerDay = 8d // ajusta si trabajáis a 7.5h/día, etc.
// --- Code Smells High+Critical ---
let smellsBefore =
hasBaseline
? (double)IssuesInBaseline.Count(i => i.Severity == Severity.High || i.Severity == Severity.Critical)
: 0d
let smellsAfter =
(double)Issues.Count(i => i.Severity == Severity.High || i.Severity == Severity.Critical)
// --- Complejidad ciclomática media (métodos) ---
let ccBefore =
hasBaseline
? codeBase.OlderVersion().Application.Methods.Where(m => m.NbLinesOfCode > 0).Average(m => m.CyclomaticComplexity)
: 0d
let ccAfter =
Application.Methods.Where(m => m.NbLinesOfCode > 0).Average(m => m.CyclomaticComplexity)
// --- Ciclos entre namespaces (namespaces afectados) ---
let nsInCycleBefore =
hasBaseline
? (double)codeBase.OlderVersion().Application.Namespaces.Where(n => n.Level == null).Count()
: 0d
let nsInCycleAfter =
(double)Application.Namespaces.Where(n => n.Level == null).Count()
// --- Deuda técnica (a MINUTOS) ---
let debtBeforeMD = hasBaseline ? IssuesInBaseline.Sum(i => i.Debt).ToManDay() : 0d
let debtAfterMD = Issues.Sum(i => i.Debt).ToManDay()
let debtBeforeMin = debtBeforeMD * hoursPerDay * 60d
let debtAfterMin = debtAfterMD * hoursPerDay * 60d
// --- Duplicación (clones) por nombre/categoría de regla ---
let clonesBefore =
hasBaseline
? (double)IssuesInBaseline.Count(i =>
i.Rule.Name.ToLower().Contains("duplicate")
|| i.Rule.Name.ToLower().Contains("clone")
|| i.Rule.Category.ToLower().Contains("duplicate")
|| i.Rule.Category.ToLower().Contains("clone"))
: 0d
let clonesAfter =
(double)Issues.Count(i =>
i.Rule.Name.ToLower().Contains("duplicate")
|| i.Rule.Name.ToLower().Contains("clone")
|| i.Rule.Category.ToLower().Contains("duplicate")
|| i.Rule.Category.ToLower().Contains("clone"))
// --- (opcional) Tiempo invertido en minutos (edita si quieres reflejar esfuerzo real) ---
let timeSpentBefore = 0d
let timeSpentAfter = 0d
// Selecciona primero un IAssembly (a) para cumplir la restricción del "first result argument"
from a in Application.Assemblies.Take(1)
from idx in new[]{0,1,2,3,4,5}
let Metrica =
(idx==0) ? "Code Smells (High+Critical)" :
(idx==1) ? "Complejidad ciclomática media (mét.)" :
(idx==2) ? "Duplicación (clones detectados)" :
(idx==3) ? "Ciclos entre namespaces" :
(idx==4) ? "Debt (minutos NDepend)" :
"Tiempo invertido (min)"
let Antes =
(idx==0) ? smellsBefore :
(idx==1) ? ccBefore :
(idx==2) ? clonesBefore :
(idx==3) ? nsInCycleBefore :
(idx==4) ? debtBeforeMin :
timeSpentBefore
let Despues =
(idx==0) ? smellsAfter :
(idx==1) ? ccAfter :
(idx==2) ? clonesAfter :
(idx==3) ? nsInCycleAfter :
(idx==4) ? debtAfterMin :
timeSpentAfter
let Mejora = (Antes - Despues)
// ¡OJO! Proyectamos primero 'a' (IAssembly) para evitar el error del primer argumento
select new { a, Metrica, Antes, Despues, Mejora }
Resultados (antes/después)

Lectura rápida
NDepend me dio el foco (qué arreglar primero y por qué), y la IA me dio tracción (cómo arreglarlo, con diffs y tests). En una tarde cerré casi la mitad de la deuda prioritaria y eliminé ciclos de dependencias.
Lo que ha mejorado (bien)
- Code Smells graves: de 4 a 1 (−3). Menos issues High/Critical es claramente mejor.
- Deuda técnica (minutos): de 33.17 a 22 (−11.17). Menos deuda ⇒ menos esfuerzo pendiente. En NDepend, menos deuda es mejor.
- Interés anual nuevo: negativo (baja el coste anual de dejar issues sin arreglar), lo cual es bueno.
- Duplicación y ciclos entre namespaces: siguen en 0 (ideal; lo normal es evitar ciclos y duplicidad).
Lo que ha empeorado (pero no preocupa)
-
Complejidad ciclomática media (métodos): sube a 1.35. En esta métrica, más alto suele ser peor, pero 1.35 está muy por debajo de los umbrales habituales (≈10–15) a partir de los cuales preocupa la mantenibilidad. O sea: ha subido un poco, pero sigue en zona verde.
Único “rojo” pendiente (lo explica el informe de NDepend)
-
Queda 1 issue High nuevo y 1 regla crítica violada por API Breaking Changes: Fields en
Globals.Cache
. Es un tema de compatibilidad con la baseline; o lo solucionas restaurando la firma antigua, o actualizas la baseline tras los cambios para que desaparezca como “nuevo”.
Resumen
He reducido issues graves y deuda, mantengo duplicación/ciclos en 0, y la CC media sube ligeramente pero sigue excelente. Todo va en la dirección correcta; sólo me falta cerrar el API breaking o ajustar la baseline.
Lecciones: “usar herramientas con cabeza”
-
Prioriza con datos, no con intuición. El informe de NDepend orienta dónde el esfuerzo rinde más.
-
Refactors incrementales. Oleadas breves (60–90 min) + re-medición ⇒ progreso tangible sin romper nada.
-
Checklist y diffs. Pide a la IA diffs y tests para cada archivo; evita cambios masivos sin control.
-
CQLinq como un policia. Convierte lo aprendido en reglas que bloqueen regresiones.
-
CI con quality gates. Si la deuda severa sube, la build debe fallar. Sin excusas.
Limitaciones y próximos pasos
-
La IA no sustituye revisiones: PRs y code reviews siguen siendo clave.
-
Ajusta las reglas a tu contexto (no todas las violaciones tienen el mismo impacto).
-
Extiende el pipeline con análisis de seguridad (SAST/secret scanning) y rendimiento.
Conclusión
No se trata de “programar en directo” ni de magia. Se trata de usar herramientas con cabeza: NDepend para ver con claridad, IA para ejecutar con velocidad. Si conviertes el informe en un plan de oleadas y lo blindas con quality gates, puedes pasar de legacy a base sostenible en horas, no semanas.
Por cierto, en octubre habrá nueva vesion 2026.1 que dará soporte a VS 2026, .NET 10 y C# 14.