The Question: What Do You Actually Need to Understand?
We've talked about seams before — how they protect against merge conflicts and downstream cascades in multi-agent workflows. But there's a deeper question: what is the mental model that makes all of this work?
You don't need to understand how every subsystem works. You don't need to memorize every class hierarchy or database table. What you actually need is the topology: the modules, the interfaces between them, and the seams where one piece of code can change independently of another.
🤔 The Question That Started Everything
With so many layers of abstraction in modern codebases, what's the minimum understanding you need to safely spin out parallel agentic processes? The answer isn't "understand the whole system" — it's "understand the seams."
But understanding seams means understanding seam types. Not just the abstract concept — the specific mechanical patterns that recur in every codebase. Once you can spot them, you can read any codebase's topology at a glance.
The Chain: Modules → Interfaces → Seams → Topology
Let's build the model from the ground up. Each link in the chain depends on the one before it.
1. Modules
A module is a unit of ownership — a package, a service, a library, a bounded context. The key insight: a module is defined by what it hides, not by what it exposes. If two files share a global config object with no contract, they're not separate modules regardless of what directories they live in.
2. Interfaces
An interface is the contract between modules. A function signature. An API endpoint. An event schema. A database table definition. The interface says "if you give me X, I'll give you Y" — but critically, it says nothing about how.
This is where most people stop, but the distinction that matters is the one between an interface and a seam:
| Thing | What it is | Example |
|---|---|---|
| Interface | The contract itself — the what | "You call POST /auth/login with {email, password} and get back {token}" |
| Seam | The place where that contract lives — the actual boundary line in the codebase | The line in auth.ts where export function login(...) is defined |
3. Seams
Michael Feathers, in Working Effectively with Legacy Code, gave us the canonical definition:
A seam is a place where you can alter behavior in your program without editing in that place.
Translated to agentic coding:
💡 Key Insight
A seam is a line in the codebase where two agents can work independently, as long as the contract on that line doesn't change. If Agent A is working inside module X and Agent B is working inside module Y, and the only thing connecting them is a seam, they cannot collide. No merge conflicts. No coordination overhead.
4. Topology
This is the graph. Nodes = modules. Edges = seams. Draw this graph and you know the maximum parallelism of your agentic processes:
Figure 1 — A codebase topology: nodes are modules, edges are seams
Notice the users table. Two modules share it — Auth Service and Order Service both read and write to it. That's a seam, but a weak one. The graph tells you: if two modules share a database table, they are in the same agent cell. Do not parallelize across that boundary without a migration contract that both agents see.
The Eight Fundamental Seam Types
Seams aren't abstract. They're specific mechanical patterns. Every connection between two pieces of code is one of these eight types. Master them and you can read any codebase's topology at a glance.
| # | Seam Type | The Contract | Example |
|---|---|---|---|
| 1 | Function Call | Function signature — parameter types, return type, name | def send_email(to: str, subject: str, body: str) -> bool |
| 2 | Interface / Abstract Class | Language-enforced contract — compiler checks it | interface PaymentProcessor { charge(amount: Money, customer: Customer): ChargeResult } |
| 3 | HTTP / RPC | URL, method, request shape, response shape, status codes, error format | POST /api/orders with JSON body → 201 Created { order_id } |
| 4 | Message / Event | Topic name, message schema, ordering guarantees, delivery semantics | Kafka "order.placed" — publisher doesn't know who consumes, subscriber doesn't know who publishes |
| 5 | Database / Schema | Table definition, column types, constraints | CREATE TABLE users (id UUID PRIMARY KEY, email TEXT NOT NULL UNIQUE, ...) |
| 6 | File System | File path, naming convention, format, atomicity guarantees | write_json("/exports/orders_2026-07-14.json", data) → reader picks it up |
| 7 | Environment / Config | Config key name, value shape, default behavior | features.new_checkout.enabled: false — flip to change behavior with zero code change |
| 8 | Dependency Injection | Constructor parameter or setter — the thing being injected | class OrderService(payment: PaymentProcessor, notify: Notifier) |
Each type has different characteristics. A function call seam is simple but couples callers to the module at build time. An HTTP seam decouples at the process level — the caller has no idea what language, framework, or database the server uses. A message queue seam adds asynchrony. A database seam is the most dangerous because there's no enforcement at access time.
How Seams Compose: The Real Picture
In a real codebase, seams stack. A single request might cross all eight types:
Figure 2 — A single request crossing multiple seam types
The topology is just these basic seams, composed. Once you can spot each type — the function call, the HTTP endpoint, the shared table, the config flag — you can draw the graph for any codebase.
Seam Strength: Not All Boundaries Are Equal
This is where the mental model gets practical. The strength of a seam determines whether you can safely parallelize work across it:
| Seam Type | Strength | Enforcement | Agentic Risk |
|---|---|---|---|
| Typed RPC / gRPC | Strong | Protobuf contract, compile-time check | Very low |
| REST + OpenAPI spec | Strong | Auto-generated client, schema validation | Low |
| Message queue + schema registry | Strong | Avro/JSON Schema enforced at publish | Low |
| Interface / Abstract Class | Medium | Compiler-checked, but both sides share a build | Medium — both sides can drift |
| Function Call | Medium | Signature convention, no formal contract | Medium |
| Dependency Injection | Medium | Constructor contract, test-time verification | Medium |
| Environment / Config | Medium | Documented keys, runtime fallback | Medium — old and new code co-exist |
| REST without spec | Weak | Implicit contract, manual testing | High |
| File System | Weak | Path convention, no schema enforcement | High |
| Shared Database Table | Weakest | No enforcement at access time | Very high — no seam, just shared state |
| Event without schema | Weakest | Implicit shape, runtime surprise | Very high |
The practical rule that emerges:
⚠️ The Rule of Seam Strength
Strong seams → safe parallelization boundary. Two agents can work on either side independently.
Weak seams → risk boundary. Agents need explicit coordination — the contract is implicit and breakable.
Shared state (no seam) → same agent cell. If two modules share a database table without a schema migration contract, they are not separate modules for agentic purposes. One agent owns both.
Three Gaps in the Model
The topology diagram is useful, but it's incomplete if you stop there. Three things the diagram doesn't show:
Gap 1: Data lineage creates hidden seams
Two services might never call each other directly, yet still share a table:
Figure 3 — Data dependencies: invisible on the call graph but real at runtime
Auth and Notification don't call each other. On the topology diagram, they look independent. But they share the users table. If Agent A changes the schema and Agent B changes the notification query, you get a runtime bug — and no import-level seam caught it. A database schema is an interface. A Kafka topic schema is an interface. Include them in your topology.
Gap 2: Compile-time topology ≠ runtime topology
Two libraries that import each other at build time might be separate services at runtime. Conversely, two services that look independent on the deployment diagram might share a build pipeline, a linter config, or a CI step — and two agents editing those files can collide. For agentic coding, the runtime topology matters more, but the build-time topology still creates collision surfaces.
Gap 3: Documented seams ≠ real seams
The OpenAPI spec says one thing. The actual implementation does another. A function marked private is imported in 14 places because someone used export *. A config key that "only Auth uses" is read by the billing service because someone copy-pasted. The real skill is seam-detection, not seam-creation. You're usually working in a codebase where the seams are poorly defined. Import graphs, data flow analysis, and runtime traces reveal the real boundaries.
The Corrected Mental Model: A 7-Step Process
Here's the full framework. When approaching a codebase for agentic coding:
Figure 4 — The 7-step mental model for agentic coding
Walking through each step:
Step 1 — Draw the module graph. Nodes are modules (what hides its internals). Don't trust directory structure. Look at imports, data access, and config usage.
Step 2 — Identify every boundary as a seam. For each arrow between modules, name the seam type. Is it a function call? An HTTP endpoint? A shared table? Use the eight-type vocabulary.
Step 3 — Classify each seam by strength. Strong: typed contract, compile-time enforcement, separate processes. Weak: implicit contract, shared build, no schema enforcement. Weakest: shared mutable state with no contract at all.
Step 4 — Apply the rule. Strong seams are safe parallelization boundaries. Weak seams are risk boundaries — agents need coordination. Shared state with no seam means those modules are one module for agentic purposes.
Step 5 — Group into agent cells. A cell is a set of modules connected by weak seams. Cells are separated by strong seams. One agent per cell at a time.
Step 6 — Declare interface contracts. Before spinning out an agent, be explicit: which seams is it allowed to change, and which can it only consume? If a contract must change across a strong seam, that's a deliberate coordination point — both agents need to know.
Step 7 — Spin out agents. Each agent writes to its own cell. Contracts across strong seams don't change without coordination. Merge conflicts are avoided because no two agents edit the same file — and semantic conflicts are avoided because the contracts are explicit.
What This Looks Like in Practice
Let's make it concrete. You're looking at a typical web app:
| Module Pair | Seam Type | Strength | Can Parallelize? |
|---|---|---|---|
| React frontend ↔ Node API | HTTP (REST + OpenAPI) | Strong | ✅ Yes — separate processes, typed contract |
| Node API ↔ PostgreSQL | Database schema | Weak | ⚠️ Risk — schema changes cascade |
| Auth module ↔ Billing module | Shared users table | Weakest | ❌ No — same agent cell |
| API ↔ Stripe (external) | HTTP (typed SDK) | Strong | ✅ Yes — external boundary |
| Logger utility ↔ All modules | Function call | Medium | ⚠️ Risk — shared utility, changes affect everyone |
| Feature flag ↔ Checkout flow | Config | Medium | ⚠️ Risk — old and new code co-exist until flag removed |
From this, your agent cells look like:
Figure 5 — Agent cells: safe parallelization units
Three agents can work in parallel: one on the frontend, one on the backend core, one on the logger. The contracts across strong seams (the OpenAPI spec) don't change. The weak seams inside Cell 2 are handled by a single agent.
The Hard Part Nobody Talks About
The model works beautifully when the seams exist. The reality of most codebases is messier:
- Modules that look separate share a global config object
- Two services that "don't talk" both read from the same Redis key
- A "private" function is imported in 14 places because someone did
export * - The OpenAPI spec exists but doesn't match the actual implementation
- A migration that "only affects Auth" breaks the billing queries because they joined on the renamed column
This is why the first three steps of the model — draw, identify, classify — are the ones that take real work. You're discovering seams, not designing them from scratch. Import graphs, data lineage analysis, and runtime traces are your tools.
But once you've done that work — once you can look at a codebase and see the eight seam types, classify their strength, and draw the agent cells — everything else follows. No merge conflicts. No semantic breakage. No downstream cascades. Just independent agents, working in parallel, on a topology you understand.
🔑 The Core Principle
You don't need to understand how every subsystem works. You need to understand the seams — the specific, mechanical patterns that define where one module ends and another begins. Once you can spot the eight seam types in any codebase, you can draw the topology, classify the boundaries, group into agent cells, and spin out parallel work with confidence.
The failing pattern is: two agents editing code without knowing which boundaries they share. The seam-based pattern is: every agent works inside its cell, contracts across strong seams are explicit, and shared state means shared ownership.