Coding — consistency & least complexity
Audience: developers & AI agents writing or changing code in this repo · Scope: three repo-wide engineering rules — keep parallel things consistent, fix the root rather than adding a layer, and the JavaScript language level · Last reviewed: 2026-07-21
TL;DR — Three rules for the code (the doc standards cover the writing). (1) Keep siblings symmetric —
express parallel settings, flags, parameters, columns, and options the same way (same polarity, naming, order,
framing), so a reader learns the pattern once. (2) Fix the root, don't add a layer — when something is
inconsistent or wrong, change the existing code rather than adding a wrapper/adapter/shim that compensates for
it; only reach for a new abstraction when the same result genuinely can't be had by changing what already
exists. (3) JavaScript is ES2021 — const/let, async/await, optional chaining; never var, never
ES2022+. These are living standards — amend them when our practice changes.
1. Keep siblings symmetric
Rule. When several things do parallel jobs — a group of settings, a set of boolean flags, enum options, function parameters, table columns, sibling functions — express them the same way:
- Same polarity / direction. A group of on/off flags are all "include X" or all "exclude X", never a mix. Prefer the positive framing (include / enable / show) so that Yes / true consistently means "the thing happens."
- Same naming pattern, argument order, and shape.
include_internal/include_test, notexclude_internal/include_test; the same param order across sibling functions; the same option shape across a set.
Why. A reader — developer or operator — learns the pattern once and trusts it everywhere. A break in symmetry makes them stop and re-derive the meaning of each one ("which way does this one go?"), and it invites real bugs: arguments passed in the wrong order, the wrong "Yes" ticked, a flag read backwards.
Real example. The nudge deal-type gates were quote_nudge_exclude_internal (1 = exclude) sitting right
next to quote_nudge_include_test (1 = include) — so on the settings form "Yes" meant opposite things on
two adjacent toggles. Fix: rename the first to quote_nudge_include_internal (both 1 = include) so both read
"Include … in nudges."
Smell test. If two things are conceptually parallel but you'd describe them with opposite verbs, or you have to pause to work out which direction one of them runs, they aren't symmetric — make them so.
2. Fix the root, don't add a layer
Rule. When existing code is inconsistent, awkward, or wrong, change that existing code — the setting, the column, the logic, the name, the wording. Don't add a new abstraction (a wrapper, adapter, translation shim, inverting type, compatibility flag) whose only job is to hide the flaw.
Before adding new code, ask: can I get the same result by changing code that already exists? If yes, do that.
Why. A compensating layer is permanent complexity. Every future reader has to discover it, work out why it's there, and reason through the indirection — and the underlying flaw is still there underneath it. Changing the root is usually a one-time, verifiable cost (even when it needs a migration); the layer is a cost paid forever.
Real example. To label a gate "Include internal" while its column still meant "exclude," one option was a
new bcinvertedboolean property type that flipped the value on display and on save — a permanent inverting
layer over a misleading column. We threw it away and changed the column instead (a rename + a
behaviour-preserving migration), so the code is honest end to end with nothing to un-learn later.
When a new layer is justified. Only when the same result genuinely can't be achieved by changing existing code — e.g. the thing you'd otherwise change is third-party / owned elsewhere, or the abstraction removes real duplication across many call sites (not just papers over one inconsistency). "It avoids a migration" is not sufficient on its own: weigh the one-time root-fix cost against the forever cost of the layer, and default to the root fix.
3. JavaScript — ES2021, const/let, no ES2022+
Rule. Write ES2021 in this repo's own JavaScript:
constby default,letwhen it's reassigned. Nevervar.var's function scoping and hoisting are a bug source, and it reads as legacy next to everything around it.async/awaitfor asynchronous work rather than nested.then()chains or callback pyramids.- Optional chaining (
?.) and nullish coalescing (??) instead of long&&guard chains. - Arrow functions, template literals, destructuring, spread/rest are all fair game.
- Nothing newer than ES2021. No
??=/||=/&&=logical assignment, no.at(), no#privateclass fields, nostatic {}blocks, noObject.hasOwn(), no.findLast(), no top-levelawait.
Rules 1 and 2 together
They reinforce each other: symmetry keeps the code honest; fixing the root (instead of wrapping it) keeps it symmetric and simple. When you catch an inconsistency, the fix is almost always to change the existing thing so it matches its siblings — not to add something that translates between them.
Checklist
- Parallel things (flags, params, columns, options) use the same polarity, naming, and order.
- Toggle groups are positive-framed (include / enable / show) so Yes / true always means "it happens."
- Before adding a new type / wrapper / adapter, you confirmed the same result can't be had by changing existing code.
- Any new layer removes real duplication or bridges something you don't own — it isn't just hiding a fixable inconsistency.
- JavaScript uses
const/let(novar),async/await,?./??— and no ES2022+ syntax.