CreaRack-SL

Grafo estático vs. empirismo en la Biblioteca — Dos formas complementarias de entender dependencias

Problema

La dependencia entre archivos puede manifestarse de dos formas:

  1. Explícita/declarada — imports, calls, documentación cruzada.
  2. Implícita/empírica — cambios sincronizados sin conexión aparente.

Un dev que modifica monitoring/api/signage.py necesita saber qué archivos revisar. El grafo estático dirá: “endpoints que llaman a este API” y “modelos que usa”. Pero omitirá static/js/pages/signage/SignageContentManager.js, que nunca importa Python pero siempre cambia cuando el API cambia.


Grafo estático (BFS, edges declarados)

Qué es

Aristas derivadas de:

  • Imports: from X import Y.
  • Calls: función A invoca función B.
  • Direct docs: archivo X está en el update_checklist de modelo/endpoint Y.
  • Config edges: definiciones en bib_edges (especificadas manualmente para relaciones complejas).

Ventajas

✅ Determinista: uno lee el código fuente y ve exactamente qué se importa. ✅ Rápido: BFS simple, 1-2 saltos capturan la mayoría de dependencias. ✅ Intención explícita: refleja lo que el código iba a hacer.

Limitaciones

❌ Ciego a acoplamiento implícito: cambios en el HTML de una feature suelen ir con cambios en su API, pero no hay import entre ellos. ❌ Código legacy sin imports: archivos viejos que no respetan módulos claros. ❌ Efectos transversales: un cambio de plantilla global que afecta múltiples vistas, sin relación explícita.


Acoplamiento empírico (señal git)

Qué es

Derivado de historial de commits: si dos archivos cambian juntos en N commits, están acoplados empíricamente.

Cuantificable con una métrica: count = número de merges donde ambos se tocaron.

Ejemplo real (backfill de CreaRack):

  • monitoring/api/signage.py ↔ static/js/pages/signage/SignageContentManager.js: count = 7.
  • forms/device_form.py ↔ templates/device_form.html: count = 5.

Ventajas

✅ Revela acoplamiento oculto: el historial es la verdad no dicha en el código. ✅ Basado en realidad: si siempre cambian juntos, es porque pertenecen al mismo dominio/feature. ✅ Escala temporal: last_seen muestra si el acoplamiento es reciente o antiguo (feature descontinuada vs. viva).

Limitaciones

❌ Ruidoso si los cambios son incidentales: refactor masivo toca 30 archivos → todos se casan entre sí (por eso MAX_FILES = 25). ❌ Requiere historial: nuevos archivos no tienen co-cambios aún. ❌ Ambiguo: count alto puede significar “siempre van juntos” O “la herramienta de build los modifica en paralelo”.


Complementariedad

CasoGrafo estáticoAcoplamiento empírico
Cambio en endpoint → qué servicios se rompen✅ Imports claros✗ Típicamente vacío
Cambio en modelo → qué tests fallaron✅ Queryset imports✗ Ruido (tests tocan todo)
Cambio en template → qué componentes JS actualizarlos✗ HTML no importa JS✅ Co-cambios revelan parejas
Refactor de lógica → dónde revisar primero✅ Empieza con imports✅ “Pero OJO con estos otros 3 archivos”

En la práctica: dev consulta bib_impact_query(file_path=X, depth=2) y obtiene:

  1. Resultado del BFS estático (direct_docs, related_endpoints, related_models).
  2. Enriquecimiento con cochanged_files (acoplamiento empírico).

Ejemplo de output:

{
  "related_endpoints": ["monitoring.api.v1:signage_create"],
  "related_services": ["monitoring.services.signage_event_recorder"],
  "cochanged_files": [
    {"file": "static/js/...", "count": 7},
    {"file": "templates/...", "count": 5}
  ]
}

Dev lee: “Mis cambios romperán la API (endpoints) y el servicio (services). Y probablemente debo revisar estos 2 archivos frontend/template que suelen cambiar conmigo.”


Implementación en CreaRack

Grafo estático

  • Tabla: bib_edges (aristas + score de confianza).
  • Tool: bib_impact_query con BFS.
  • Poblada por: scanner de imports (TS/Python), config manual.

Acoplamiento empírico

  • Tabla: bib_cochanges (pares + count + last_seen).
  • Tool: enriquecimiento en bib_impact_query con campo cochanged_files.
  • Poblada por: bib_record_cochanges (post-merge ingest + backfill).

Calibración

Parámetros de control

En bib_impact_query:

  • min_confidence: filtro de grafo estático (default 0.0 = todo).
  • cochange_min: filtro de co-cambios (default 2 = solo pares con count ≥ 2).

En bib_record_cochanges (post-merge):

  • MAX_FILES = 25: rechaza refactors masivos.
  • MAX_PAIRS = 1000: límite de una llamada batch.
  • Denylist: archivos que no contribuyen señal (base.py, scripts, docs).

Casos de uso documentados

  1. Review de PR: “¿Qué archivos debería tocar yo?” → static + empirico.
  2. Impacto de cambio: “¿Qué puede romperse?” → static (imports) + empirico (co-cambios no obvios).
  3. Onboarding: “¿De dónde empiezo a leer?” → sigue el grafo.
  4. Auditoría de acoplamiento: “¿Qué archivos están implícitamente casados?” → empirico (high count).

Evolución futura

  • Pesos en co-cambios: count es unidimensional; podríamos agregar metadata (tipo de cambio: bugfix/feature/refactor).
  • Análisis temporal: co-cambios recientes (última semana) vs. históricos (media móvil).
  • Visualización: grafo interactivo en wiki o dashboard que combine static + empirico.

Véase también

  • [[feature—biblioteca—cairn-cochanges]]
  • [[entity—functions—tool—bib-impact-query]]
  • [[entity—functions—database—bib-edges]]
  • [[entity—functions—database—bib-cochanges]]
  • [[entity—functions—service—bib-record-cochanges]]