One declaration, everything derived
The schema pattern
This is the shape the guide asks for every closed set of values. The type, the guard and the list of options cannot disagree, because there is only one of them.
An agent asked to build a feature looks for something similar and copies it. That is good when it finds the best example in the codebase and bad when it finds the oldest. So the design guide does not describe good design in the abstract. It names files.
docs/designing-systems.md opens:
When planning a new feature, design it to have every quality below. Each one names reference implementations in this codebase — read the exemplar before designing, and copy its shape rather than inventing a new one. These are the systems we want more of.
defineTable,
admin APIs from defineCrudApi, forms from defineForm. "If your
plan contains a hand-rolled dispatcher, an ad-hoc form, bespoke CRUD
routes, or hand-written row (de)serialization, stop: there is a
define* factory for that already."largest-remainder.ts:
"a complete allocation algorithm with zero imports; the hardest
logic in the money paths and the easiest to test".UPDATE … RETURNING.One declaration, everything derived
This is the shape the guide asks for every closed set of values. The type, the guard and the list of options cannot disagree, because there is only one of them.
export const ContactFieldSchema = v.picklist([
"email",
"phone",
"address",
"special_instructions",
]);
export type ContactField =
v.InferOutput;
export const CONTACT_FIELDS = ContactFieldSchema.options;
export const isContactField = (s: string): s is ContactField =>
v.is(ContactFieldSchema, s);
The codebase is functional and curried, built on a small
#fp
module. A separate rule stops that turning into jargon:
Keep the functional style, but name helpers and write comments in simple domain words, not CS jargon. A helper must explain itself like "Keeps only children that can still be booked for this ticket."
The guide also lists what to use instead of writing it: valibot for
validation, @std/collections for collections, Temporal for dates
and time zones, Intl for formatting. When the house calling
convention differs from a library's, write a thin adapter, and
document the reason when hand-rolling is unavoidable.
Libraries over hand-rolled code
shows how the tickets repository moved its own helpers onto these.