ADR Index

Every architecture decision the curriculum asks you to record, with where it is made and what it supersedes.

These are documents you write, in docs/adr/, at the moment of each decision. A reconstructed rationale is fiction, and the value of an ADR is almost entirely in the rejected options — which is the part you cannot reconstruct.


The Format

# ADR-00N: <the decision, in the imperative>

## Context
What forced the decision. What was known and unknown at the time.

## Options
A, B, C — each with its REAL cost. A strawman option is worse than no ADR.

## Decision
One sentence, active voice: "We use X."

## Consequences
What this makes easy, what it makes hard, and the observation that would make us revisit.

Three rules: write it at the time, record the rejected options with their real costs, and never edit a decision — supersede it.


The Core Fourteen

#DecisionWritten inRejected, notably
001One crate, not a workspaceWorkspaceA crate per subsystem — longer builds, accidental public API
002Stack VM, not registerLab 12 / chapterRegister (Lua's) — fewer dispatches, a register allocator
003Keep the tree walker forever as the oracleLab 8Delete it — saves 1,000 lines, loses the guarantee
004Value is a 16-byte Rust enumLab 4 / chapterNaN boxing — 8 bytes, requires unsafe
005Lua 5.4's integer/float splitLab 4Floats only — silent precision loss at 2^53
006Tracing mark-and-sweep over handles, no unsafeLab 15Rc (cannot collect cycles); raw pointers (UB on a bug)
007Interning added only after a benchmarkLab 16Intern everything up front — regresses concat-heavy code
008Insertion-ordered table iterationLab 13Lua's unspecified order — flaky tests, non-reproducible output
009Byte-wise string ordering, not strcollLab 4Lua's locale-dependent ordering — different sorts per machine
010Host-controlled module resolutionLab 22A filesystem search path — ambient authority, traversal
011Engine is !Send + !SyncLab 19Send via Arc (atomics everywhere); Sync via a lock (worse than one per thread)
012Globals are a Table, not _ENV upvaluesScope and EnvironmentsFull _ENV — more flexible, needs upvalues first
013Production profile: partially-trusted, in-processLab 23Claiming hostile multi-tenant — not achievable in-process
014Cranelift, not hand-emitted machine codeLab 30Hand-emitted x86-64 — weeks on encoding, never reaching deopt

Also Recorded Along the Way

Not numbered in the plan, and each still deserves an ADR when you make it:

DecisionWhere
Byte strings, not UTF-8 StringLab 16
Absolute jump targets, not relativeOpcode reference
Op as a typed enum, not a packed wordConstant Pools and Encoding
No string→number coercion in arithmeticLab 4
Left-to-right operand evaluation, guaranteedLab 3
No __gc, __mode, __close, __metatableMetatables
pcall does not catch Limit or Host errorsLab 21
The JIT is off by defaultJIT Architecture

From the Capstone Projects

Each supersedes or confirms an earlier one. Confirming is a fine outcome — and it is now backed by a measurement rather than a preference.

#DecisionSupersedes
015Register VM: keep or reject, with numbers002
016NaN boxing: keep or reject, with numbers and an unsafe line count004
017Incremental GC: keep or reject, with a pause distribution006
018Coroutines: the per-coroutine/shared split—

The Decision That Keeps Recurring

Determinism decided five separate questions, from five different directions:

QuestionDeterminism's answer
Recursion limit: counted depth or a real stack probe?Counted — a probe makes the failure point machine-dependent
Execution limit: instructions or wall clock?Instructions — a deadline fails differently on a loaded machine
Table iteration order?Insertion — unspecified order makes pairs tests flaky and output irreproducible
String ordering?Byte-wise — strcoll sorts differently per locale
math.random in SAFE?No — the host supplies a seed

When one criterion keeps deciding things, promote it to a written principle. Ember's lives in docs/architecture.md, and it is the reason all five answers are consistent rather than independently reasonable.


Self-check

  1. Why must an ADR be written at the time of the decision?
  2. Which part of an ADR is the part you cannot reconstruct later?
  3. Why supersede rather than edit?
  4. Which five design questions did determinism decide, and what does that tell you to do?
  5. Pick three ADRs at random and state the rejected option and its real cost, without looking.

Next: Lua Differences.