| name | fable-code |
| description | What Fable-quality code looks like and how to produce it: altitude, naming, comments, idiom-matching, error handling, tests, dependencies. Load before writing or changing any code that another human or agent will later read — which is all code. Complements fable-method (process) with the artifact-level standard. |
Fable code
The unit of code quality is the diff, and the metric is: can a reviewer
verify it by reading it? Everything below serves that.
Read before you write. Open the surrounding code first and match its
idiom — naming style, comment density, error style, file layout. Your
personal style is worth less than the codebase's consistency. A diff that
introduces a second way of doing something the repo already does one way
is a defect even when it works.
Write at the altitude of the problem. No abstraction for single-use
code. No configurability nobody asked for. No handling of states that
cannot occur. The structure of the solution should be readable off the
structure of the problem; every layer you add that isn't in the problem
is a layer every future reader must see through.
Duplicate twice, abstract on the third — and only for same-reason
change. Two copies that will change for the same reason are one
abstraction waiting; two copies that merely look alike are a trap, and
unifying them couples things that need to diverge. Coincidental
similarity is not duplication.
Comments state what the code cannot. A comment earns its line only by
carrying a constraint invisible in the code: "must run before X because
Y," "this API silently truncates past 4096," "deliberately O(n²), n≤20."
Never narration ("increment the counter"), never provenance ("added to
fix bug"), never self-justification aimed at a reviewer. If you're
explaining what the next line does, rename things until you don't have to.
Name for the caller, not the implementation. A name should tell the
call site what it gets, not how the inside works — resolveOwner, not
walkParentChainAndCheckCache. When you can't name something cleanly,
that's the design telling you the boundary is in the wrong place; move
the boundary, don't reach for a vaguer word.
Fail loudly at boundaries, trust the interior. Validate at the edges —
user input, network, file, cross-service — and fail with messages that
name the actual problem and value. Inside your own call graph, don't
re-check what the type system or the caller's contract already
guarantees; defensive re-validation everywhere is noise that hides the
real checks.
Delete what your change orphans; flag what it doesn't. Your diff owns
its own garbage — dead branches, unused imports, obsolete comments your
change created. Pre-existing dead code gets flagged in the report, never
deleted unasked: reviewability of THIS change beats tidiness of the repo.
Tests pin contracts, not internals. Write the test that would catch
the regression you'd actually be sad about, phrased against observable
behavior — inputs and outputs, not private call order. A test coupled to
internals fails on every refactor and trains people to update tests
without reading them, which is worse than no test. And a test you never
saw red verifies nothing: break the code once, watch it fail, restore.
Every dependency is a marriage. You inherit its bugs, its release
cadence, its security surface, its transitive tree. Prefer thirty lines
of your own boring code to a small package; take a dependency when it
owns a genuinely hard domain (crypto, parsing, timezones) — never to
save an afternoon.
Leave the diff reviewable. One concern per commit; a moved file and a
changed file are two commits; formatting churn never shares a diff with
logic. If you notice the diff needs a narrator, restructure it until it
doesn't — the diff is the deliverable, the code is just what it produces.