Skip to content

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 extends clause 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.

A simplified layered OS: user programs depend on system libraries, which depend on the kernel; drivers depend on the kernel too, giving it high fan-in User programs System libraries 3 entities inside Kernel Drivers

fan-in: everything depends on 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).