Problema
La dependencia entre archivos puede manifestarse de dos formas:
- Explícita/declarada — imports, calls, documentación cruzada.
- 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_checklistde 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
| Caso | Grafo estático | Acoplamiento 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:
- Resultado del BFS estático (direct_docs, related_endpoints, related_models).
- 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_querycon 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_querycon campocochanged_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
- Review de PR: “¿Qué archivos debería tocar yo?” → static + empirico.
- Impacto de cambio: “¿Qué puede romperse?” → static (imports) + empirico (co-cambios no obvios).
- Onboarding: “¿De dónde empiezo a leer?” → sigue el grafo.
- 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]]
Referenciado desde
- bib_impact_query — Tool CORE: impacto de cambios en dependencias y co-cambios
- bib_record_cochanges — MCP handler interno para registrar co-cambios git
- Cairn cherry-pick #2: Señal de co-cambio git en bib_impact_query
- Tabla D1: bib_cochanges — Registro de pares de archivos co-cambiados
- Tabla D1: bib_edges — Grafo estático de dependencias entre archivos