Agentic Coding

Seams, Interfaces, and the Topology of Codebases

A mental model for agentic coding — mastering the eight fundamental seam types that define every codebase's boundaries

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:

ThingWhat it isExample
InterfaceThe contract itself — the what"You call POST /auth/login with {email, password} and get back {token}"
SeamThe place where that contract lives — the actual boundary line in the codebaseThe 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 TypeThe ContractExample
1Function CallFunction signature — parameter types, return type, namedef send_email(to: str, subject: str, body: str) -> bool
2Interface / Abstract ClassLanguage-enforced contract — compiler checks itinterface PaymentProcessor { charge(amount: Money, customer: Customer): ChargeResult }
3HTTP / RPCURL, method, request shape, response shape, status codes, error formatPOST /api/orders with JSON body → 201 Created { order_id }
4Message / EventTopic name, message schema, ordering guarantees, delivery semanticsKafka "order.placed" — publisher doesn't know who consumes, subscriber doesn't know who publishes
5Database / SchemaTable definition, column types, constraintsCREATE TABLE users (id UUID PRIMARY KEY, email TEXT NOT NULL UNIQUE, ...)
6File SystemFile path, naming convention, format, atomicity guaranteeswrite_json("/exports/orders_2026-07-14.json", data) → reader picks it up
7Environment / ConfigConfig key name, value shape, default behaviorfeatures.new_checkout.enabled: false — flip to change behavior with zero code change
8Dependency InjectionConstructor parameter or setter — the thing being injectedclass 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 TypeStrengthEnforcementAgentic Risk
Typed RPC / gRPCStrongProtobuf contract, compile-time checkVery low
REST + OpenAPI specStrongAuto-generated client, schema validationLow
Message queue + schema registryStrongAvro/JSON Schema enforced at publishLow
Interface / Abstract ClassMediumCompiler-checked, but both sides share a buildMedium — both sides can drift
Function CallMediumSignature convention, no formal contractMedium
Dependency InjectionMediumConstructor contract, test-time verificationMedium
Environment / ConfigMediumDocumented keys, runtime fallbackMedium — old and new code co-exist
REST without specWeakImplicit contract, manual testingHigh
File SystemWeakPath convention, no schema enforcementHigh
Shared Database TableWeakestNo enforcement at access timeVery high — no seam, just shared state
Event without schemaWeakestImplicit shape, runtime surpriseVery 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 PairSeam TypeStrengthCan Parallelize?
React frontend ↔ Node APIHTTP (REST + OpenAPI)Strong✅ Yes — separate processes, typed contract
Node API ↔ PostgreSQLDatabase schemaWeak⚠️ Risk — schema changes cascade
Auth module ↔ Billing moduleShared users tableWeakest❌ No — same agent cell
API ↔ Stripe (external)HTTP (typed SDK)Strong✅ Yes — external boundary
Logger utility ↔ All modulesFunction callMedium⚠️ Risk — shared utility, changes affect everyone
Feature flag ↔ Checkout flowConfigMedium⚠️ 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:

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.