agentleFS
rafaelobrafaelob/pragmatic-agent-skills/skills/codebase-design/SKILL.md

codebase-design

Skill23 starsChanged 8 days agoApache-2.0View on GitHub

Use when designing a module's interface (deep modules, small interfaces, seams) or judging whether a shallow module should be deleted, inlined or deepened. Removal with proof -> kill-slop. Moves -> refactoring-catalog.

We split this skill into the rules it gives an agent

5 rules. 0 are common, 2 are distinctive, and 3 are very specific to this project.

  • 0

    Common

    Most of the 181 edited versions we read keep these.

  • 2

    Distinctive

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Use these terms for the concepts below.

    line 45
  2. Distinctive:

    When applying this skill to a module or cluster:

    line 93
  3. Very specific:

    Classify dependencies: Use the 4 categories in references/deepening.md (in-process / local-substitutable / remote-but-owned / …

    line 97
  4. Very specific:

    Read references/deepening.md to deepen a cluster of shallow modules: 4 dependency categories, seam discipline, replace-don't-layer testing …

    line 108
  5. Very specific:

    Read references/design-it-twice.md only for the rare high-stakes, hard-to-reverse seam that stays uncertain after step 4, never as the …

    line 109

“Common” is measured: kept by at least 6 in 10 of the 181 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Codebase Design — Deep Modules

Design deep modules: a lot of behaviour behind a small interface, placed at a clean seam, testable through that interface. Use this vocabulary and these principles wherever code is being designed or restructured. The aim is leverage for callers, locality for maintainers, and testability for everyone.

Glossary

Use these terms for the concepts below; they disambiguate "component," "service," "API," or "boundary" when those words are being used loosely for different things in the same discussion.

TermDefinitionAvoid
ModuleAnything with an interface and an implementation. Scale-agnostic: a function, class, package, or tier-spanning slice.unit, component, service
InterfaceEverything a caller must know to use the module correctly: type signature, invariants, ordering constraints, error modes, required configuration, performance characteristics.API, signature (too narrow)
ImplementationWhat is inside a module. Distinct from Adapter: a Postgres repo is a small adapter with large implementation; an in-memory fake is a large adapter with small implementation.—
DepthLeverage at the interface: the amount of behaviour a caller or test can exercise per unit of interface they must learn. A module is deep when large behaviour sits behind a small interface; shallow when the interface is nearly as complex as the implementation.impl-lines / interface-lines ratio
SeamA place where behaviour can be altered without editing at that place (Feathers). The location at which a module's interface lives. Where to put the seam is its own design decision.boundary (overloaded with DDD bounded context)
AdapterA concrete thing that satisfies an interface at a seam. Describes role, not substance.—
LeverageWhat callers get from depth: more capability per unit of interface learned. One implementation pays back across N call sites and M tests.—
LocalityWhat maintainers get from depth: change, bugs, knowledge, and verification concentrate in one place. Fix once, fixed everywhere.—

Deep vs Shallow

A deep module has a small interface over a deep implementation; a shallow one has a large interface over a thin, mostly pass-through implementation (avoid). When designing an interface, ask:

  • Can I reduce the number of methods?
  • Can I simplify the parameters?
  • Can I hide more complexity inside?

Core Principles

Depth is a property of the interface, not the implementation. A deep module can be internally composed of many small, swappable parts — they just are not part of the external interface. A module can have internal seams (private to its implementation) as well as the external seam at its interface.

The deletion test. Imagine deleting the module. If complexity vanishes, it was a pass-through and is earning nothing. If complexity reappears across N callers, it was earning its keep.

The interface is the test surface. Callers and tests cross the same seam. If you want to test past the interface, the module is probably the wrong shape.

Two adapters are evidence of a real seam, not a requirement for one. A second adapter (typically production + test stand-in) proves something varies across the boundary, which is why it is the easiest justification to check. But it is one sufficient reason, not the only one. A seam is also justified by:

  • independent volatility — the two sides change for different reasons, on different cadences;
  • an external contract — the boundary is where a protocol, schema, or published API is honoured;
  • a security boundary — trust, authorization, or tenancy changes across it, and collapsing it would erase the place the check belongs.

What is not a justification is a seam added because a second implementation might appear someday. Absent one of the reasons above, the seam is speculative — the cost is real now and the variation is hypothetical.

Designing for Testability

Good interfaces make testing natural without special arrangements:

  1. Accept dependencies, do not create them. Inject the gateway into the function instead of constructing it inside.
  2. Return results, do not produce side effects invisibly. An observable return value tests directly; a mutation of shared state does not.
  3. Small surface area. Fewer methods = fewer tests needed. Fewer parameters = simpler test setup.

Design Workflow

When applying this skill to a module or cluster:

  1. Name it. Agree on module, interface, seam, and adapter — use the glossary terms above, not local synonyms.
  2. Measure depth. Apply the deletion test: if complexity vanishes on deletion, the module is shallow. Count how much a caller must know vs how much behaviour they get.
  3. Classify dependencies. Use the 4 categories in references/deepening.md (in-process / local-substitutable / remote-but-owned / true-external) to decide the testing approach.
  4. Find the seam. Decide where the interface lives — this is independent of what goes behind it. Justify it with evidence: a second adapter, independent volatility, an external contract, or a security boundary. A seam with none of those is speculative.
  5. Explore alternatives — the exception, not the default. Design it once and move on; reach for references/design-it-twice.md's parallel sub-agent pattern only for a genuinely high-stakes, hard-to-reverse seam where the right interface is still uncertain after step 4. When it applies: compare candidates on depth/locality/seam, pick the strongest or propose a hybrid, using the specialist subagent the runtime lists by task type (generic/default only when none fits).
  6. Validate. Apply the interface-as-test-surface check: tests cross the same seam as callers. If tests need to reach past the interface, the module shape is wrong.

Checking for Shallow Modules

When asked to find or assess shallow modules, examine concrete candidates and apply the deletion test to each. A single caller, a single adapter or a thin wrapper is a lead, never proof: name what the boundary protects (volatility, contract, security, testability) and where its complexity would land without it. A caller count from rg misses dynamic dispatch, registrations, configuration and external consumers. For each candidate say delete, inline, deepen or keep, with that reason; this skill gives the design verdict and kill-slop owns the proof that a deletion is safe, so change code only when asked and delete through kill-slop's proof rules. Neither skill sets a size threshold for the verdict. A repo-wide ranked report of deepening opportunities is a larger exercise than this check: run it as its own piece of work, interviewing the owner on each candidate before changing anything.

Reference Files

  • Read references/deepening.md to deepen a cluster of shallow modules: 4 dependency categories, seam discipline, replace-don't-layer testing strategy.
  • Read references/design-it-twice.md only for the rare high-stakes, hard-to-reverse seam that stays uncertain after step 4, never as the default design path: parallel sub-agent pattern sized to the genuinely distinct candidates worth comparing, compare on depth/locality/seam, give an opinionated recommendation.

Attribution

Adapted from mattpocock/skills (MIT). Original skill: codebase-design.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.