SuperAPI AI Directory

codebase-design

Design & creation

View repository

Updated 10-16 03:13

About this skill

Codebase Design

Get the structure right before writing code. Apply the following discipline when discussing any structural decision. When refactoring an existing structure, first write out the new structure, migrate all callers, then delete the old paths. When unsure about a partition, use tracer bullets to first vertically cut through one call chain, validate the structure, then spread out horizontally.

Module Partitioning

  • Aggregate by reason for change. A module changes for only one reason: if two things change for different reasons at different times, split them into two modules. If a module cannot clearly state "what I change for," the partitioning is not done.
  • Unidirectional dependencies. Dependency arrows all point in one direction across the whole graph; high-level layers do not directly depend on low-level details: business policies do not reference concrete storage or network implementations. When a low-level layer needs to serve a high-level layer, the high-level layer declares the interface and the low-level layer implements it (dependency inversion), making dependencies point from details to abstractions.
  • Circular dependencies are a signal to split. If A depends on B and B depends on A, it means the third module that both depend on has not yet been extracted: first extract the shared part to break the cycle, and do not hide the cycle with lazy references or runtime lookups.
  • Place new things first. For a new requirement, first find which existing module's reason for change it belongs to. Only create a new module if it fits none of them. A new module must have a new reason for change, not just for a clean directory.

Interface Design

  • Minimal exposure surface. Export only the symbols callers need: every export freezes a piece of internal implementation. Exports with no callers should be demoted back to private.
  • Converge public types at a few entry points. Callers get the entire public surface from one or two entry files. Types scattered in deep directories force callers to understand the internal layout.
  • Derive the shape from the user. Write the caller code first, then define the interface: awkward call sites are the defect list for the interface, and the caller code naturally becomes the interface's first test.

Design for AI Navigation

The structure serves both humans and agents, organized by discoverability:

  • Stable directory conventions: things of the same kind are always in the same kind of location, so switching modules does not require relearning the layout.
  • One entry file per module, with the public surface fully visible from the entry.
  • AGENTS.md and CONTEXT.md pointers point to entry files, not scattered internal implementations.
  • Collaborate with domain-modeling: write finalized terminology into CONTEXT.md, and record hard-to-decide partitioning decisions (where two ways of splitting each have costs) as ADRs.

Testability

  • Inject dependencies through interfaces: network, files, clock, and randomness all come in through interfaces, and business logic is not directly bound to IO.
  • For a new module, first think about how to test it: if you cannot say which dependencies to replace to drive this module independently, go back to module partitioning and change the design.

Completion Criteria

  • The partitioning scheme is explainable: each module states in one sentence what it changes for
  • The dependency graph is acyclic: the dependency directions between modules have been listed and verified
  • The interface exposure surface is minimal: there are no exported symbols outside the entry