# Self Documenting Code

> Write self-documenting code when creating or refactoring functions, shaping models or schemas, designing query or transaction flows, validating boundaries, constructing result shapes, exposing package APIs, making code testable with concrete collaborators, or documenting non-obvious behavior.

- Skill: `rejot-dev/self-documenting-code` (Agent Skill, multi-file: 5 files)
- Install (CLI): `npx skillmds@latest add rejot-dev/self-documenting-code`
- Raw SKILL.md: https://api.skillmd.com/api/skills/rejot-dev/self-documenting-code/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: rejot-dev (https://skillmd.com/u/rejot-dev)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/rejot-dev/self-documenting-code

---


# Self-documenting code

Build a **semantic core** inside a **pragmatic shell**. Let types, data flow, and boundaries explain
the system.

Load the matching reference before working on its branch:

- For speculative fields or capabilities, premature protocols or options, or implementation choices
  unsupported by required behavior, read
  [`references/present-tense-design.md`](references/present-tense-design.md).
- For runtime validation, internal storage types, result mapping, or mutation return shapes, read
  [`references/trust-boundaries-and-result-shapes.md`](references/trust-boundaries-and-result-shapes.md).
- For function extraction, orchestration boundaries, direct imports, package exports, or breaking
  API migrations, read
  [`references/function-and-module-boundaries.md`](references/function-and-module-boundaries.md).
- For dependency design, mock-free tests, deterministic implementations, or test placement, read
  [`references/concrete-testing.md`](references/concrete-testing.md).

## 1. Map the behavior

Before shaping persistent data, identify:

1. what must be stored;
2. which entry points expose it;
3. which records each entry point reads together;
4. which invariants guard its writes;
5. which side effects continue asynchronously.

Treat each read path as a graph the model must support explicitly. Design the schema from actual
behavior rather than isolated record shapes.

**Use least commitment.** Encode required behavior, known invariants, and external contracts
exactly. When several designs satisfy them, choose the one that leaves everything else unspecified.
A caller or test introduced by the change is not independent demand; trace each commitment to
observable behavior or an authoritative contract.

**Complete when:** every changed entry point has an accounted-for read graph, write boundary,
invariant set, and side-effect destination, every added commitment traces to required behavior, a
known invariant, or an external contract, and the design makes no claims beyond them.

## 2. Shape the models

Make each model exact about its known domain meaning and silent about unresolved distinctions. Its
name should make every field feel inevitable.

- Represent known distinct states with distinct types.
- Use required fields for known invariants and discriminated variants for known state distinctions.
- Compose independent concepts instead of flattening them into one model.
- Brand structurally identical primitives when swapping them is a known domain error.
- Give records one clear application-facing identity when the domain defines one; keep storage
  metadata an implementation detail.
- Encode known domain types at the schema boundary, including structured or JSON-backed fields.
- Split a model when its fields no longer cohere around its name.

For persistent models, encode every required traversal as an explicit relation. Add an index for
each filter, ordering, join, uniqueness check, and pagination path. A relation expresses meaning;
its indexes make that meaning operationally viable.

**Complete when:** every changed model excludes known-invalid field combinations, distinguishes
values known to be incompatible, leaves unresolved distinctions unencoded, contains only fields
implied by its name, and supports every required access path explicitly and efficiently.

## 3. Build the semantic core

Give well-defined operations names. Extract them into **semantic functions** when they own an
independent boundary. A semantic function:

- has a name that states its domain operation;
- receives every required input explicitly;
- returns every result needed by its caller;
- limits side effects to those named by the operation;
- composes safely without knowledge of its internals;
- is small enough for focused unit tests.

Naming and extraction are separate decisions. A named inline function is often clearest when a
callback has one configuration-owned caller and its placement makes the surrounding lifecycle or
orchestration obvious. This is especially useful in hook registries, route tables, workflow
definitions, and similar declarative APIs:

```ts
hooks: {
  onUserCreated: async function queueUserSignUpVerificationEmail(payload, context) {
    // The complete configured behavior remains visible here.
  },
}
```

Extract a function when it is reused, owns independently meaningful rules, requires focused testing,
or makes its containing flow difficult to scan. Do not extract solely because a block can be given a
domain-oriented name. Compose recurring flows from semantic functions rather than duplicating their
internals. Keep short mechanical transformations inline when a helper would only rename the mapping
and hide its data flow.

**Complete when:** meaningful operations are named, reusable rules and independently meaningful
calculations have explicit boundaries, and each operation is placed where the owning flow is easiest
to understand. Extraction adds independence rather than merely reducing indentation.

## 4. Respect trust boundaries

At acquisition, establish only the contract an `unknown` value needs for the current operation.
Choose one boundary treatment:

- **cast immediately** to the required type when an authoritative source contract is trusted but its
  static type is incomplete;
- **parse or validate immediately** to the required projection when the runtime value must earn
  trust;
- **preserve it as opaque** when the operation stores or forwards it without interpretation.

A cast or parse marks the exact point and scope where trust was established. The resulting type
remains authoritative within that scope; opaque data acquires a stronger type only at the boundary
that interprets it.

**Program to established trust.** Internal code uses that precise contract directly. When a
downstream consumer appears to need shape checks, fallback coercions, optional access for required
fields, or catch-and-continue handling, strengthen the producer's type or boundary instead. Keep
checks that represent genuine domain invariants or operational uncertainty, such as absent records,
concurrency conflicts, and external failures.

Construct known result shapes directly:

- map typed records into public shapes with explicit local object construction;
- use native operations provided by the known type;
- query only the fields required for the decision or mutation;
- return immediately known mutation values directly, including generated identifiers;
- omit values represented only by unresolved database expressions until a later read resolves them;
- strengthen imprecise types at their schema or adapter source rather than compensating in every
  consumer.

**Complete when:** every obtained `unknown` is cast, validated, or deliberately preserved as opaque
at its acquisition boundary, downstream code relies on only the trust established there without
defensive shape checks or fallback coercions, genuine domain and operational failures remain
explicit, reads fetch only operation-relevant state, and returned shapes contain the resolved values
required by the current result contract without pretending unresolved values are concrete.

## 5. Keep definitions canonical

Give each concept one definition, one name, and one direct import path.

- Import a symbol from the file that defines it.
- Expose package entry points by mapping public paths directly to defining files.
- Update consumers to the clearest current API instead of preserving compatibility names, path
  aliases, forwarding modules, or re-exports.
- During pre-1.0 development, assume full breaking changes: choose the cleanest current design and
  migrate all callers atomically.

Canonical paths make ownership visible: a reader can follow an import directly to the
implementation, and repository search finds every consumer without resolving indirection.

**Complete when:** every changed symbol has one authoritative definition and public path, all
consumers use its current name directly, and obsolete compatibility layers have been removed.

## 6. Design for concrete tests

Make explicit, at the boundary that owns them, the nondeterminism and infrastructure that changed
behavior depends on. Pass required clocks, identity generators, storage, transports, and external
services through visible boundaries rather than acquiring them from hidden global state.

Favor pure semantic functions and small adapters around infrastructure. Provide lightweight local or
in-memory implementations when current tests or development environments need substitution, and give
the shared contract only the operations those collaborators require. Exercise the same public
behavior production uses so module interception, monkey-patching, and behavioral mocks remain
unnecessary.

Co-locate each test with the source it specifies so behavior and verification move together, such as
`route.ts` beside `route.test.ts`.

**Complete when:** changed behavior is testable through explicit inputs and collaborator contracts
containing only the operations it requires, tests sit beside their source, and the same production
paths run under test.

## 7. Make transactions read like plans

Express a state change as one coherent unit of work:

1. schedule all required reads upfront;
2. derive decisions from the retrieved snapshot;
3. validate permissions and invariants;
4. apply the writes;
5. enqueue post-commit effects.

Prefer one transaction or database round-trip per operation. Retrieve related data together through
the modeled query graph, then derive locally. Keep external or long-running work outside the
transaction. When an operation genuinely requires **retrieve → external work → mutate**, make that
split explicit in its structure and naming.

**Use database time as the single source of truth for database-coordinated time.** Timestamps used
for persistence lifecycle, scheduling, leases, expiration, or transaction invariants should be
generated and compared inside the database unit of work. Do not mix process time with database time
within the same decision. Preserve externally owned timestamps as separate facts, and use revisions
or sequences—not wall-clock time—for causal ordering.

**Complete when:** every database-dependent decision is backed by an upfront read, every write
follows its validation, and each operation has the smallest explicit transaction boundary that
preserves consistency.

## 8. Contain the pragmatic shell

Keep endpoint handlers, jobs, provisioning flows, and other orchestration in **pragmatic
functions**. Their names should identify the use case or entry point.

Assign each boundary one role:

- **entry points** parse and validate input, establish context, map errors, and serialize responses;
- **services** coordinate reads, permissions, invariants, and writes within the unit of work;
- **background handlers** perform external calls, retries, and long-running side effects after
  commit;
- **serializers** translate internal records into stable public shapes.

For inbound events, make the synchronous path acknowledge durable intent: validate the event,
enqueue its full work item with a deterministic identity when deduplication matters, and return. Let
the background handler own external calls and subsequent state changes.

Pragmatic functions should delegate stable domain rules to the semantic core. When callers depend on
part of an orchestration function, extract that behavior as a semantic function instead of widening
the orchestration function's incidental contract.

Configuration should reveal its lifecycle locally. Keep a named callback inline when moving it would
force readers away from a hook registry, route table, workflow definition, or similar declaration to
understand the configured behavior. Extract the callback when it has an independent contract rather
than merely to shorten the declaration.

**Complete when:** orchestration reads as a sequence of named domain operations, shared callers
depend on semantic functions, asynchronous effects have an explicit durable handoff, and declarative
configuration keeps locally owned behavior visible.

## 9. Add comments for surprises

Prefer names, types, models, and boundaries as the explanation. Use comments for information the
code cannot express clearly: a counterintuitive invariant, external constraint, compatibility
reason, or deliberate tradeoff.

Place the explanation beside the code it governs. State the reason or surprising behavior rather
than paraphrasing the implementation.

**Complete when:** every retained comment contributes information absent from the code, and every
non-obvious constraint a maintainer needs is documented at its point of use.

