When Ahmed and I wrote the @reuse-when annotation for every contract in DarJS, we weren’t writing documentation.
We were writing query targets.
The distinction is real. Documentation is written from the perspective of someone who already found the function and wants to understand it. A query target is written from the perspective of someone who doesn’t know the function exists yet, describing the problem they’re trying to solve. Those are completely different texts — and most codebases only contain the first kind.
The gap between implementation language and caller language
Here’s the Trackable mixin’s description if you read the source: “Injects four audit fields via lifecycle hooks.”
Here’s what a developer types when they need it: “I need to track who created and updated each record.”
These two strings share almost no vocabulary. “Injects,” “audit,” “lifecycle,” “hooks” — none of those words appear in the search. “Track,” “created,” “updated,” “record” — none of those appear in the implementation description.
This is the reason code is hard to find. Not because the function doesn’t exist. Because the language used to describe it doesn’t match the language used to search for it. The LLM bridges this gap by rephrasing, but only because it’s read millions of examples of both sides. It’s expensive. And it works by learned analogy, not by retrieval.
The @reuse-when field closes this gap directly. You write it from the caller’s perspective, in the vocabulary they’d use before writing new code:
@reuse-when You need automatic created/updated timestamps and user tracking
on any model without writing hooks manually
That sentence is optimized for the moment of need, not for the moment of study. The words “automatic,” “timestamps,” “tracking,” “model,” “manually” are search words — the terms someone uses when they’re about to write something that already exists.
What makes a good @reuse-when
Bad: “You need to use Trackable.” (Circular. Names the solution, not the problem.)
Bad: “You need audit fields.” (Too compressed. What’s an audit field? Depends who you ask.)
Bad: “You need to track model changes.” (Too vague. Track what? Store where? Why?)
Good: “You need automatic created/updated timestamps and user tracking on any model without writing hooks manually.”
The good version names:
- the trigger condition: automatic, without writing manually
- the specific data: timestamps, user tracking
- the scope: any model
- the avoided work: hooks manually
Writing this well is harder than it sounds. It forces you to articulate what problem this actually solves — not what it does internally, but why someone would reach for it. If you can’t write a clean @reuse-when, the function may not have a clear single purpose.
That’s a discovery tool as much as a documentation tool. Several functions in the DarJS codebase got refactored during the annotation pass because their @reuse-when came out ambiguous. The annotation surfaced a design problem the code had silently carried.
The @role constraint as a design tool
The @role field takes exactly one value from a closed 12-word vocabulary:
transformer · validator · query · mutation · factory · renderer · adapter · guard · hook · coordinator · config · registry
You pick one word. If your function is “sort of a transformer but it also writes to the database,” you have a mixed-responsibility function. The constraint surfaces it.
This is the same pressure as naming a function well, but applied systematically. A function named processOrder can do anything. A function with @role mutation that’s also reading from an external API is immediately conspicuous — the role is wrong or the function needs to be split.
In practice, about 5% of functions in the DarJS annotation pass had role conflicts. The @role field forced those out. The functions were already wrong before the annotation — the annotation just made the wrongness visible.
The full contract structure
Here’s a complete contract from the DarJS @darjs/mixins package:
/**
* @contract
* @role hook
* @domain core
* @does Injects four audit timestamp/user fields and populates them
* automatically via beforeCreate and beforeUpdate hooks.
* @tags audit, created_at, updated_at, created_by, updated_by,
* timestamp, mixin, track
* @reuse-when You need automatic created/updated timestamps and user tracking
* on any model without writing hooks manually
* @complexity simple
* @example class Invoice extends Model.with(Trackable) {}
* @module @darjs/mixins
*/
Each field has a job for a different consumer:
@does— the developer reading the source@tags— the TF-IDF index keyword bag@reuse-when— the natural-language query target@role— the classification for routing and graph construction@complexity— the LLM escalation gate@example— the copy-paste usage@module— the import path, shown in results
Humans use @does, @example, @module. NLP uses @reuse-when, @tags, @role. The LLM router uses @complexity. Each field has exactly one primary consumer and was written for that consumer.
Corpus results from DarJS
110 contracts across five packages. Some @reuse-when examples from the actual corpus:
SoftDeletable: “You need records to be archivable and restorable rather than permanently deleted”
Stockable.reserve: “You need to hold stock for a customer order before it is fulfilled”
Model.delete: “You need to delete a record and want soft-delete to be applied automatically when available”
MoroccoPlugin.validate: “You need to validate a country-specific identifier (tax number, business ID) before saving”
PageDefGenerator.generate: “You need to generate the full CRUD route, form, and list definition for a model in one call”
Each one names the problem, not the solution. Each one uses the vocabulary a developer would use before knowing the function exists.
Run dar find "hold stock before an order" and Stockable.reserve surfaces at the top. Run dar find "validate Moroccan business identifier" and MoroccoPlugin.validate is first. These aren’t coincidences — they’re the direct result of writing @reuse-when from the caller’s perspective.
Self-interest as a better motivator than documentation discipline
Most developers resist annotation because it feels like documentation overhead — something you do for other people, at a cost to your own time.
The @reuse-when frame changes the incentive. You’re not writing for a future reader. You’re writing the query string that will find this function when you need it three months from now. The sentence that surfaces it in dar find is the sentence you’ll type when you’ve forgotten it exists.
Self-interest is a more reliable motivator than team discipline. The developer who writes a good @reuse-when benefits directly when they can run dar find and get their own code back as a result. The developer who skips it gets llm-generate — which means writing something that may already exist, in a slightly different form, with a slightly different interface, creating the kind of quiet duplication that accumulates into inconsistency over time.
The enforcement is mechanical: a pre-commit hook blocks any commit where _publish/ has diverged from packages/. Keep the contracts in sync, or don’t commit. The discipline isn’t optional — it’s wired into the workflow.
That’s what writing code for machines actually means. Not clever syntax. Not specific patterns. Writing the interface layer — the @reuse-when, the @role, the @does — in a language that both humans and retrieval systems can use. The code stays as it is. The contracts are what change.