Components and dependencies
Three levels of granularity show up whenever people talk about code structure, and it’s worth naming them separately:
- Entities — the actual units in the code: classes, functions, methods, modules. This is what a parser sees.
- Components — groups of entities that share a purpose.
auth,billing,api-gateway. This is the level architecture usually gets discussed at, and it’s a level up from any single file. - Dependencies (also called links or edges) — the relationships between entities: one
function calling another, one class importing a module, one type inheriting from another.
Every import statement, every function call, every
extendsclause is an edge.
The diagram below shows both levels at once in a simplified OS: components stacked by dependency (user programs → system libraries → kernel ← drivers), with one component opened up to show the entities living inside it.
Simplified — illustrative, not a complete architecture.
Folders look like they define components, and often they roughly do — but folders are just where
files happen to sit on disk. Two files in the same directory can have zero dependency edges
between them (unrelated utilities dumped in a shared folder), and two files in completely
different directories can be tightly coupled (a models/user.py that every single route handler
imports). The folder layout is a guess at the component structure; the dependency edges are the
evidence. When the two disagree, the edges are right and the folder layout is stale — someone
moved files around during a refactor and never finished untangling the imports.
This is why edges matter more than folders: a component isn’t “the files under src/billing/,”
it’s “the entities that mostly depend on each other and only lightly depend on stuff outside the
group.” That’s a structural definition, not a filesystem one, and it’s what makes it possible to
detect a component whose real behavior no longer matches its folder name.
Two intuitions do most of the work once you’re looking at edges instead of folders:
- Fan-in — how many other entities depend on this one. High fan-in means a lot of things would break if you changed it; it’s load-bearing whether or not anyone marked it as public API.
- Fan-out — how many other entities this one depends on. High fan-out means it’s easily affected by changes elsewhere — it has a lot of surface area exposed to the rest of the system.
A function with high fan-in and low fan-out is the kind of thing you change carefully and rarely. A function with high fan-out and low fan-in is usually a leaf — glue code, an entry point, a script. Neither is good or bad on its own; the shape just tells you what kind of risk you’re looking at before you touch it.
In arcade-agent: parse builds exactly this graph — entities and the dependency edges
between them — from your actual source, before anything gets grouped into components. See
Agent tools for parse and the query tool that walks the
resulting graph (most_coupled, dependencies, dependents).