This section is full of concrete rules - no synchronous I/O in a request handler, validate every mutating route, cap outbound fetch with a timeout. Each one reads like a simple instruction, but every rule in this section actually encodes something more specific: a failure mode someone already hit, compressed into an instruction cheap enough to follow without re-deriving the reasoning from scratch every time.
That's the idea this page exists to make explicit, because it's easy to treat a rules list as arbitrary style preference and either follow it blindly or ignore it when it's inconvenient. Node Project Rules Checklist is the concrete, ready-to-audit version of this section; Async & Event Loop Rules, Security Rules, Dependency Rules, Logging & Observability Rules, and API Rules are the specific rule sets, each answering "what failure does this prevent, and how strictly is it enforced." This page is about how to think about rules as a category before you read any of them.
An engineering rule is compressed institutional memory - a specific past failure, generalized into an instruction that lets the next engineer avoid it without relearning the lesson from scratch.
Insight: Teams that treat rules as arbitrary style end up either ignoring them under deadline pressure or following them past the point they still apply - both failure modes trace back to not understanding what the rule was actually protecting against.
When to Use: Deciding whether a new convention deserves to become a documented rule, choosing how strictly to enforce one, writing a rule so it survives the person who wrote it leaving, or deciding when an old rule should be revisited.
Limitations/Trade-offs: Rules trade judgment for consistency - the more you encode, the less every engineer has to reason from first principles, but also the more a codebase accumulates instructions that outlive the situation that justified them.
Related Topics: architecture decision records, CI quality gates, code review norms, static analysis enforcement.
Every durable engineering rule starts the same way: something broke, someone figured out why, and the team decided the fix shouldn't depend on everyone independently rediscovering it. A rule like "no fs.readFileSync in a request handler" isn't a style preference about function names - it's the residue of an incident where a synchronous read stalled every other request on the process. The rule is cheaper to state than the incident is to relearn, which is the entire reason rules exist: they let expensive lessons be paid for once and reused forever.
That framing matters because it distinguishes a rule from two things it's often confused with. A guideline is a default that reasonable engineers can override with judgment - "prefer async/await over raw promise chains" survives being ignored in an edge case. A rule, in the sense this section uses the word, is something the team has decided should hold without case-by-case judgment, usually because the cost of getting it wrong (a security hole, an outage, a data-loss bug) is high enough that "use your judgment" isn't an acceptable answer. A house style choice (tabs vs. spaces) is neither - it has no failure mode behind it at all, which is why formatting tools rather than rules documents own that territory.
A useful analogy: think of a rules document as a codebase's accumulated scar tissue, made legible. Scar tissue forms in response to a specific injury and then keeps protecting against it long after anyone remembers the original cut - which is exactly the trade-off worth staying aware of. A rule that's stopped protecting against anything real is a rule worth questioning, not a rule worth reflexively obeying.
Not every rule deserves the same amount of force behind it, and matching enforcement to the actual cost of violation is most of what separates a useful rules document from a wish list nobody reads. Rules in this section sit on a spectrum:
Documented convention - written down, explained, expected in review, but not mechanically checked. Appropriate for judgment calls where context matters (choosing between an event bus and a direct call inside a modular monolith).
Code-review-enforced - a reviewer is expected to catch violations, backed by a checklist or PR template so it isn't left to memory. Appropriate for things a linter genuinely can't evaluate (is this ADR's context section actually accurate).
CI-enforced (mechanical gate) - a lint rule, type check, or test that fails the build automatically. Appropriate for anything with a clear, checkable signature - banned patterns, missing input validation shape, an uncapped limit parameter.
The relationship between these tiers is directional and important: a rule generally starts as a documented convention (because someone just learned the lesson and wrote it down), and earns its way toward mechanical enforcement as the team confirms the pattern recurs and can actually be detected automatically. Skipping straight to a CI gate for something that can't be reliably checked mechanically produces false positives that erode trust in the whole gate; leaving a checkable, high-cost rule at "documented only" forever means it depends on every reviewer remembering it, indefinitely.
incident happens │ ▼lesson is named and written down ── documented convention │ (pattern recurs, is checkable) ▼reviewers watch for it explicitly ── code-review-enforced │ (a linter/CI check can express it) ▼mechanical gate blocks violations ── CI-enforced
An exception process is what keeps a rule from becoming brittle bureaucracy once it's mechanically enforced. A rule stated as an absolute ("never do X") without an escape hatch eventually meets a legitimate case it didn't anticipate - at which point the team either breaks the rule silently (undermining it for everyone) or blocks a legitimate change (undermining trust in the process). ADR Template for Node exists partly for this reason: an Architecture Decision Record is the mechanism for recording why a specific case deviates from the default rule, so the exception is visible and deliberate rather than silent.
Rules accumulate a specific kind of decay worth naming directly: rule rot, where an instruction keeps being enforced well after the situation that justified it has changed. A dependency-pinning rule written when a team had no automated audit tooling might no longer earn its keep once npm audit runs in CI on every PR - the rule isn't wrong, it's just been superseded by better enforcement of the same underlying goal. Rules that name the failure they prevent age better than rules that only name the required behavior, because a team revisiting "why do we do this" can actually evaluate whether the failure is still a real risk; a rule stated as pure instruction with no rationale just gets followed - or silently dropped - without anyone able to tell which is correct.
This is also where a rules section earns its keep at organizational scale rather than individual-engineer scale. A single senior engineer can hold "don't block the event loop" as tacit knowledge and catch violations in review by instinct. That doesn't transfer - not to a new hire, not to a second team building a second service, not to an engineer six months removed from the original incident. Writing the rule down, tiering its enforcement, and gating the checkable parts in CI is what makes the lesson organizationally durable instead of dependent on one person's memory being in the room during code review.
The Node Project Rules Checklist is worth reading through this lens specifically: it groups 25 rules into tiers (safety/security block launch, API/quality gates block GA traffic, operational maturity completes within the first month) - that tiering is itself an application of "not every rule deserves the same urgency," applied at the scale of an entire service rather than one convention.
Enforcement tier
Strength
Weakness
Best Fit
Documented convention
Cheap to write; preserves judgment for genuine edge cases
Depends entirely on memory and culture; erodes under deadline pressure
Context-dependent decisions a linter can't evaluate
Code-review-enforced
Catches nuance a mechanical check would miss
Inconsistent - depends on which reviewer, how much time they have
Rules with real judgment calls but high enough stakes to need a second set of eyes
CI-enforced gate
Applies uniformly, every time, to every contributor
Only works for genuinely checkable patterns; false positives erode trust
"A rules document and a style guide are the same thing." Style has no failure mode behind it - it's a formatter's job. A rule exists because violating it has a real, specific cost; conflating the two makes real rules feel as optional as tab-width preferences.
"Once a rule is written down, it's enforced." An unenforced rule is a wish - it holds only as long as every engineer remembers and chooses to follow it, which is precisely the failure mode rules exist to eliminate in the first place.
"Every rule should eventually become a CI gate." Only genuinely checkable patterns belong there; forcing a judgment-dependent rule into a mechanical gate produces false positives that teach engineers to route around the gate entirely.
"Rules without exceptions are stronger rules." An absolute rule with no escape hatch just relocates the exception to "silently violated" instead of "deliberately and visibly justified" - which is strictly worse for anyone auditing the codebase later.
"Old rules are safe to leave in place indefinitely." A rule can outlive the failure it was protecting against, especially once better tooling supersedes it - rules that don't get revisited accumulate as friction with no corresponding benefit.
What actually distinguishes a "rule" from a "guideline" in this section?
A guideline is a default engineers can reasonably override using judgment. A rule is something the team has decided should hold without case-by-case judgment, usually because the cost of getting it wrong is high - a security gap, an outage, data loss - not because a specific phrasing is preferred.
Why do rules need a documented "why," not just a "what"?
A rule that only states the required behavior gets followed - or silently dropped - without anyone able to evaluate whether it still applies. Naming the failure it prevents lets a future engineer judge whether that failure is still a real risk before deciding to keep, relax, or automate the rule.
How does a rule move from "documented" to "enforced in CI"?
It earns that promotion once two things are true: the pattern genuinely recurs (worth the setup cost), and it's the kind of thing a mechanical check can reliably detect without false positives. Rules that need real judgment stay at code-review enforcement instead.
Why not just make every important rule a hard CI gate immediately?
Because CI gates only work well for patterns a tool can check reliably. Forcing a judgment-dependent rule into a mechanical gate produces false positives, and false positives teach engineers to distrust or bypass the gate - undermining enforcement for the rules that genuinely need it.
What's the purpose of an exception process for a rule?
It gives a legitimate edge case a visible, deliberate path around the default, instead of forcing a silent violation or blocking a valid change outright. An ADR is a common way to record that decision so the exception is auditable rather than invisible.
How is a "rule" different from a plain style preference like tabs vs. spaces?
A style preference has no failure mode behind it - nothing breaks either way, which is why a formatter, not a rules document, owns that decision. A rule exists specifically because violating it has a real, describable cost.
What is "rule rot," and why does it matter?
It's when a rule keeps being enforced after the situation that justified it has changed - for example, a manual dependency-pinning rule that's since been superseded by automated audit tooling. Rules that don't get periodically revisited accumulate as friction without a matching benefit.
Should every service in an org follow every rule in this section identically?
Not necessarily with equal urgency - the tiering approach in Node Project Rules Checklist groups rules by how launch-blocking they are, because a safety/security rule and an operational-maturity rule don't carry the same risk if left unaddressed for a week.
Who decides whether something becomes a team rule?
In practice, whoever owns the postmortem or the pattern's recurring cost - but the decision should be visible, not tacit, which is exactly what a documented rules section and an ADR trail provide instead of leaving it in one engineer's memory.
Is a large rules document a sign of a mature engineering org, or a bureaucratic one?
It depends on whether each rule still names a real, current failure mode and has enforcement matched to its actual cost. A rules document that's periodically pruned and re-tiered is maturity; one that only ever grows and is never revisited is the bureaucratic version of the same artifact.