Graph Analysis: Impact Assessment & Architecture Evaluation
SkillDocs & knowledgeLets your agent use a graph claude skill to map dependencies and see what a code change would break.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Graph Analysis: Impact Assessment & Architecture Evaluation skill
About this capability
Query the project as a dependency graph to assess the blast radius of a change, find dead or risky elements, and evaluate module health. Use when planning a refactor, judging what a change will break, or looking for unused documents.
What this skill tells your AI
The instructions your AI receives, as published by mendixlabs/mxcli in .claude/skills/mendix/graph-analysis/SKILL.md and read by ahel’s review.
Use this skill when assessing the impact of a change, identifying risky elements, evaluating module health, or planning a structural refactor. The graph tables model the entire project as a dependency graph and expose topology metrics on top of it.
When to Read This Skill
- Before modifying an entity or microflow that might be widely used
- Before moving elements between modules
- When assessing architectural health (coupling, cohesion, god nodes)
- When planning to split a module or extract a bounded context
- When identifying dead code to remove
Setup: Two-Step Refresh
Two separate commands are needed. The full catalog must exist before community detection runs.
# Step 1: Build the full catalog (activities, widgets, cross-references)
# Skip if already fresh — check .mxcli/catalog.db mtime
./mxcli -p app.mpr -c "REFRESH CATALOG FULL"
# Step 2: Run graph algorithms (community detection, centrality, layers, cycles)
./mxcli -p app.mpr -c "REFRESH CATALOG COMMUNITIES"
What each step populates:
| Step | Tables populated |
|---|---|
REFRESH CATALOG FULL | GRAPH_GOD_NODES, GRAPH_MODULE_COUPLING, GRAPH_MODULE_COHESION, GRAPH_DEAD_ASSETS, GRAPH_ENTITY_HOTSPOTS, GRAPH_MODULE_DEPENDENCIES, GRAPH_REFKIND_DISTRIBUTION |
REFRESH CATALOG COMMUNITIES | COMMUNITIES, COMMUNITY_SUMMARY, GRAPH_CYCLES, GRAPH_LAYERS, GRAPH_CENTRALITY, GRAPH_INTEGRATION_SURFACE |
An empty table from the second row is ambiguous unless you check. Step 2 is
not a build mode — it augments whatever the catalog is, so Build mode: full
says nothing about whether it ran, and before mendixlabs/mxcli#1060 an un-run
pass and a genuinely cycle-free project produced the same 0 rows. A query now
warns requires refresh catalog communities (not run for this catalog) when the
pass is missing, and SHOW CATALOG STATUS has a Graph analysis: line. No
warning plus no rows is a real answer; treat anything else as "not computed
yet", not as "clean".
REFRESH CATALOG COMMUNITIES with a resolution modifier:
REFRESH CATALOG COMMUNITIES— default resolution (balanced granularity)REFRESH CATALOG COMMUNITIES resolution 0.6— coarser clusters (fewer, larger communities; good for monolith-to-multi-app planning)REFRESH CATALOG COMMUNITIES resolution 2.0— finer clusters (more, smaller communities; good for identifying sub-modules within a large module)
Use Case 1: Pre-Change Impact Assessment
Run before modifying any entity, microflow, or association. Takes under a minute.
-- Is this element a god node? How many things depend on it?
SELECT Asset, ObjectType, InDegree, OutDegree, Degree
FROM CATALOG.GRAPH_GOD_NODES
WHERE Asset = 'MyModule.MyEntity'
-- What layer is it in? (0 = leaf/data, higher = orchestration)
SELECT AssetName, Layer
FROM CATALOG.GRAPH_LAYERS
WHERE AssetName = 'MyModule.MyMicroflow'
-- Is it a bridge? Would breaking it disconnect subsystems?
SELECT AssetName, PageRank, Betweenness
FROM CATALOG.GRAPH_CENTRALITY
WHERE AssetName = 'MyModule.MyMicroflow'
-- Which community does it belong to? What else is in that cluster?
SELECT c.AssetName, c.CommunityId, cs.Label
FROM CATALOG.COMMUNITIES c
JOIN CATALOG.COMMUNITY_SUMMARY cs ON c.CommunityId = cs.CommunityId
WHERE c.AssetName = 'MyModule.MyEntity'
Interpretation:
InDegree > 15: many callers — changing this will require wide testingBetweenness > 100: this element is a bridge; removing or changing its signature will disconnect subsystems- High layer number (8+): orchestration-level code; changes propagate downward
- Layer 0: pure data entity or leaf microflow — changes affect only direct callers
Use Case 2: Dead Code Identification
-- All unreferenced elements project-wide
SELECT QualifiedName, ObjectType, ModuleName
FROM CATALOG.GRAPH_DEAD_ASSETS
WHERE ModuleName NOT IN ('System', 'Atlas_Core', 'Atlas_Web_Content',
'Atlas_NativeMobile_Content', 'WorkflowCommons', 'CommunityCommons',
'NanoflowCommons', 'NativeMobileResources', 'WebActions', 'DataWidgets')
ORDER BY ModuleName, ObjectType
False positives to exclude before deleting:
| Pattern | Why it's a false positive |
|---|---|
Microflows named *_Tool | Registered as LLM agent tools dynamically — never called directly in code |
ApplicationCore.ASU and ASU_* | Configured in project settings, not referenced in MDL |
| Pages used as workflow user task pages | Workflow activities reference pages via model GUID, not code edges |
| Pages used as navigation home pages | Navigation assignments are not code edges |
IVK_* microflows | Called by external systems via published REST/web services |
SCH_* microflows | Called by scheduled events — no code edge |
Safe to delete: pages, snippets, and microflows with no inbound edges that do not match any of the patterns above, and that are not referenced in navigation (check SHOW NAVIGATION).
Use Case 3: Module Health (Cohesion & Coupling)
-- Cohesion per module: low % = module does too many things / should be split
SELECT ModuleName, IntraEdges, InterEdges, CohesionPct
FROM CATALOG.GRAPH_MODULE_COHESION
ORDER BY CohesionPct ASC
-- Cross-module coupling: find unexpected entanglements
-- Filter to custom modules by replacing the IN list
SELECT SourceModule, TargetModule, Edges, RefKinds
FROM CATALOG.GRAPH_MODULE_COUPLING
WHERE SourceModule IN ('MyModule1', 'MyModule2', 'MyModule3')
AND TargetModule IN ('MyModule1', 'MyModule2', 'MyModule3')
ORDER BY Edges DESC
-- Dependency direction breakdown: what kinds of references cross module boundaries?
SELECT SourceModule, TargetModule, RefKind, Edges
FROM CATALOG.GRAPH_MODULE_DEPENDENCIES
WHERE SourceModule = 'MyModule'
ORDER BY Edges DESC
Interpreting CohesionPct:
| Range | Meaning |
|---|---|
| 70–100% | Self-contained module — good |
| 40–70% | Moderate coupling to other modules — watch if it drops further |
| < 40% | Low cohesion — module is a cross-cutter or should be split; investigate whether this is intentional (e.g. an orchestration/startup module) |
Coupling RefKinds to watch:
retrieve,datasource,create,change: data access across module boundaries — should go through a service microflow in the owning modulecall: microflow calls across modules — acceptable if intentional APIgeneralize: entity inheritance across modules — hard to refactor, flag as a blocker for splitslayout: purely structural, always acceptable
Use Case 4: Community Analysis (Bounded Context Validation)
-- What communities exist, how large are they, which modules do they span?
SELECT CommunityId, Label, Size, Modules
FROM CATALOG.COMMUNITY_SUMMARY
ORDER BY Size DESC
-- Which community does a specific element belong to?
SELECT c.AssetName, c.CommunityId, cs.Label, cs.Size
FROM CATALOG.COMMUNITIES c
JOIN CATALOG.COMMUNITY_SUMMARY cs ON c.CommunityId = cs.CommunityId
WHERE c.AssetName LIKE 'MyModule.%'
ORDER BY c.CommunityId
-- Elements from your module that landed in a foreign community
-- (indicates tight coupling to that community's domain)
SELECT c.AssetName, cs.Label AS CommunityLabel
FROM CATALOG.COMMUNITIES c
JOIN CATALOG.COMMUNITY_SUMMARY cs ON c.CommunityId = cs.CommunityId
WHERE c.AssetName LIKE 'MyModule.%'
AND cs.Label != 'MyModule'
ORDER BY cs.Label
What to look for:
- If your module's elements are split across multiple communities, the module spans more than one bounded context — consider splitting it.
- If elements from module A consistently land in module B's community, module A has a hidden dependency on B's domain.
- Two communities both labeled the same marketplace module (e.g.
AgentCommonsappears twice) signals a natural internal split within that module.
Use Case 5: Finding Bridges (Centrality)
-- Top microflows by betweenness in your custom modules
-- High betweenness = bridge; breaking it disconnects subsystems
SELECT AssetName, PageRank, Betweenness
FROM CATALOG.GRAPH_CENTRALITY
WHERE AssetName LIKE 'MyModule.%'
OR AssetName LIKE 'AnotherModule.%'
ORDER BY Betweenness DESC
LIMIT 20
-- God nodes by PageRank (most influential, not just most-referenced)
SELECT Asset, ObjectType, ModuleName, InDegree, OutDegree, Degree
FROM CATALOG.GRAPH_GOD_NODES
WHERE ModuleName IN ('MyModule1', 'MyModule2')
ORDER BY Degree DESC
LIMIT 20
Betweenness vs Degree:
Degree(InDegree + OutDegree): raw connection count. High degree = popular or knows-too-much.Betweenness: fraction of shortest paths in the graph that pass through this node. High betweenness = bridge between clusters. A node can have low degree but high betweenness (a narrow connector between two large clusters).PageRank: recursive importance — being depended on by important things matters more than being depended on by many small things.
Use betweenness to find microflows where an interface change would require coordinating across many callers simultaneously.
Use Case 6: Entity Hotspots
-- Entities used by the most microflows, and across the most modules
SELECT Entity, UsedByFlows, AcrossModules
FROM CATALOG.GRAPH_ENTITY_HOTSPOTS
WHERE Entity LIKE 'MyModule.%'
ORDER BY UsedByFlows DESC
An entity appearing in AcrossModules with 3+ module names is a candidate for an explicit service layer — instead of every module reading it directly, route access through a DS_ or SUB_ microflow in the owning module.
Use Case 7: Dependency Cycles
-- All cycles: self-references (size 1) and mutual references (size 2+)
SELECT AssetName, ModuleName, CycleId, CycleSize
FROM CATALOG.GRAPH_CYCLES
ORDER BY CycleSize DESC, CycleId
Size 1: self-referencing element (e.g. a workflow that references its own context entity — usually harmless). Size 2: mutual reference between two elements — e.g. workflow A calls workflow B which triggers workflow A. Investigate before assuming it's intentional; these can cause infinite loops at runtime. Size 3+: a dependency ring — must be broken before the module can be split or properly layered.
Module cycles are a different query, not a rollup
GRAPH_CYCLES is document-level. Two modules referencing each other almost
never means two documents do — Administration calls something in Atlas_Core from
one page while Atlas_Core reaches back from another — so GRAPH_CYCLES is
correctly empty for a genuinely circular pair of modules. Reading it as "no
circular dependencies" was mendixlabs/mxcli#1060. Ask the module question of the
module table:
SELECT ModuleName, CycleSize, RefKinds
FROM CATALOG.GRAPH_MODULE_CYCLES
ORDER BY CycleSize DESC, ModuleName
RefKinds names the kinds on that module's edges into the rest of the cycle —
the references to go and break, as opposed to everything else the module points at.
The two also read different edges. GRAPH_MODULE_CYCLES uses every reference
kind, matching GRAPH_MODULE_COUPLING; the computed asset pass
(GRAPH_CYCLES/COMMUNITIES/GRAPH_LAYERS/GRAPH_CENTRALITY) is restricted to
the structural kinds so navigational edges do not blur the clustering. On a stock
app that is about a third of the edges. GRAPH_ANALYSIS_SCOPE reports the split
per kind, which is the query to run when coupling shows an edge and the computed
tables act as though it were not there:
SELECT RefKind, Edges, InAssetGraph FROM CATALOG.GRAPH_ANALYSIS_SCOPE
ORDER BY InAssetGraph, Edges DESC
Use Case 8: Monolith-to-Multi-App Planning
For splitting a project into separate Mendix apps connected via OData, REST, or Business Events:
# Use coarse resolution for candidate app groupings
./mxcli -p app.mpr -c "REFRESH CATALOG COMMUNITIES resolution 0.6"
-- Each cross-community edge becomes an integration contract after a split
-- RefKind tells you what protocol would be needed
SELECT * FROM CATALOG.GRAPH_INTEGRATION_SURFACE
ORDER BY SourceCommunity, TargetCommunity
-- 'generalize' edges are blockers — entity inheritance cannot cross app boundaries
SELECT * FROM CATALOG.GRAPH_INTEGRATION_SURFACE
WHERE RefKind = 'generalize'
RefKind to integration protocol mapping (matches the Mechanism column the view computes):
| RefKind | Mechanism label | Protocol after split |
|---|---|---|
associate | OData / shared entity | OData / shared entity |
retrieve | OData read | OData (read) |
create, change | event / REST write | REST or Business Events |
call | REST (published microflow) | REST (synchronous) or Business Events (async) |
generalize | BLOCKER: inheritance across boundary | Blocker — cannot cross app boundaries; must restructure |
anything else (e.g. datasource, layout) | review | Inspect manually — layouts are UI-only and duplicate per app; others case-by-case |
Topological Layer Reference
GRAPH_LAYERS assigns each element a layer number via topological sort (Kahn's algorithm on the dependency DAG):
| Layer | Typical elements |
|---|---|
| 0 | Entities, associations — pure data, no outgoing calls |
| 1 | Leaf microflows: tool callbacks, event handlers, simple helpers |
| 2–3 | Business logic: workflow triggers, process microflows |
| 4–6 | Orchestration: agent calls, composites |
| 7+ | Startup / configuration: ASU chain, import pipelines |
Elements at the same layer can be parallelised safely. A microflow calling something at a higher layer than itself indicates a layering violation (upward dependency).
Quick Reference: Which Table Answers What
| Question | Table |
|---|---|
| How many things depend on X? | GRAPH_GOD_NODES (InDegree) |
| Is X a bridge between subsystems? | GRAPH_CENTRALITY (Betweenness) |
| What layer is X in? | GRAPH_LAYERS |
| What community (bounded context) is X in? | COMMUNITIES + COMMUNITY_SUMMARY |
| Which modules are tightly coupled? | GRAPH_MODULE_COUPLING |
| Is module M self-contained? | GRAPH_MODULE_COHESION |
| What is unused / dead? | GRAPH_DEAD_ASSETS |
| Are there circular dependencies between documents? | GRAPH_CYCLES |
| Are there circular dependencies between modules? | GRAPH_MODULE_CYCLES |
| Why isn't an edge I see in coupling affecting the cycles/layers? | GRAPH_ANALYSIS_SCOPE |
| Which entities are hotspots? | GRAPH_ENTITY_HOTSPOTS |
| What contracts would a split require? | GRAPH_INTEGRATION_SURFACE |
| What reference types cross module boundaries? | GRAPH_MODULE_DEPENDENCIES |
Related Skills
- assess-quality — Full quality assessment including lint, report scores, and manual review guidelines
- organize-project — MOVE command and folder structure
- write-lint-rules — Encoding a layering policy as an enforced Starlark rule
- odata-data-sharing — Publishing entities for cross-app access
Signals
- GitHub stars
- 122
- Forks
- 49
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
graph-analysis- Source
- github.com/mendixlabs/mxcli