Nice Topic,
I’ve been building Giulia, an Elixir daemon that provides AST-level code intelligence via REST API. It parses your codebase with Sourceror, builds a dependency graph with libgraph, runs semantic search with Bumblebee/Nx (all-MiniLM-L6-v2, 80MB, on CPU), and stores everything in ETS. It runs on OTP, serves any client over HTTP, and responds in under 300ms.
The idea is simple: give AI coding agents (or any tool) structural understanding of Elixir codebases instead of letting them grep around like it’s 1985.
To test it on something I didn’t write, I cloned Commanded and ran a full analysis. Here’s what Giulia found.
The Headlines
-
66 modules, 438 functions, 500 graph vertices, 520 dependency edges
-
Zero red-zone modules in the heatmap — speaks to Commanded’s maturity
-
Zero behaviour fractures — every @behaviour contract is fully satisfied
-
Zero orphan specs — every @spec matches its function
-
Only 4 dead functions out of 438 (0.9%)
-
2 circular dependencies — one is a 10-module cycle through the entire command execution pipeline
-
7.3% spec coverage — only 32 specs for 438 functions across a public framework
The Insight That Static Analysis Misses
Most tools rank modules by complexity alone. Giulia combines complexity with topology — and the results tell a very different story.
Event.Handler has the highest complexity score in the project at 93. It has 36 functions across 1,549 lines and wears five hats: behaviour definition, macro, GenServer, event processor, and telemetry emitter.
Sounds dangerous, right? Except Giulia’s knowledge graph shows it has only 1 downstream dependent. It’s a leaf node. Refactoring it is low-risk.
Aggregates.Aggregate scores lower at 63 complexity. Only 716 lines, 28 functions, clean code with zero structural redundancy.
But it has degree 17 in the dependency graph — the highest in the project. Fan-in 6, fan-out 11, part of a 10-module circular dependency cycle. It’s the #1 change risk module (score 844) because modifying it affects the entire command execution pipeline from dispatch through persistence.
Half the code, lower complexity, but double the danger.
Any tool that ranks by one dimension misses this. Giulia gives both.
Three Core Modules, Side by Side
| Aspect |
Aggregates.Aggregate |
Event.Handler |
ProcessManagerInstance |
| Lines |
716 |
1,549 |
649 |
| Functions |
28 |
36 |
30 |
| Complexity |
63 |
93 |
53 |
| Change risk rank |
#1 (844) |
#3 (462) |
#2 (537) |
| Dependency degree |
17 |
9 |
11 |
| Downstream dependents |
6 |
1 |
3 |
| State persistence |
Event stream |
None |
Snapshots |
| Error recovery paths |
1 |
1 |
2 |
| Typespecs |
0 |
0 |
0 |
The three most important modules in the framework. Zero typespecs across all of them.
What Giulia Exposes via REST
Every endpoint responds in under 300ms, project-scoped with a ?path= parameter. Multi-project support — one daemon, multiple codebases indexed simultaneously.
Code Understanding: modules, functions, specs, types, callbacks, structs, module details (one call, full profile)
Knowledge Graph: impact maps (blast radius at depth N), dependents, dependencies, centrality, dependency path tracing, cycle detection
Health Metrics: change risk scores, god modules, fan-in/fan-out, coupling analysis, API surface ratios, heatmap (red/yellow/green zones), dead code detection, orphan specs, behaviour integrity
Semantic Search: two-stage retrieval — Bumblebee embeds module docs + function signatures, cosine similarity finds code by intent (“entity movement physics” finds set_velocity/2 without keyword match), surgical briefings combine semantic results with knowledge graph data
The Stack
All Elixir. All on the BEAM.
-
Sourceror for AST parsing and code analysis
-
libgraph for dependency topology, Dijkstra pathfinding, cycle detection
-
Bumblebee + Nx (EXLA) for semantic embeddings (all-MiniLM-L6-v2)
-
ETS for the artifact store (modules, functions, ASTs, vectors)
-
Bandit + Plug for the REST API
-
OTP supervision for the whole thing
No Python. No external vector database. No sidecar services. One supervised Elixir application.
I work with AI coding agents daily. They’re powerful but architecturally blind — they understand code as text, not as structure. They grep for function definitions instead of querying a graph. They can’t tell you the blast radius of a change or whether a module is a hub or a leaf.
Giulia gives them eyes. One API call returns what would take 10+ grep/read cycles and thousands of context window tokens. The AI agent I use reports ~80-90% token savings and calls the impact/centrality endpoints mandatory before planning any modification.
Giulia is a personal project, not open source yet. Happy to discuss the architecture, the analysis results, or the approach. Feedback welcome.
If someone want a full Analysis, just contact me for the document.
Best,