Magic numbers
A magic number is a literal whose meaning lives only in the author's head:
if (age >= 18), sleep(86400), status === 3. The reader cannot tell a
deliberate threshold from a typo, and a value that recurs must be found and
changed in every spot at once. The fix is a name, applied with judgment about
which literals actually need one.
Method
- Name any literal that encodes a rule or a fact.
MINIMUM_AGE = 18,
SECONDS_PER_DAY = 86400, Status.SHIPPED = 3. The name turns a mystery
value into a statement the reader can check against the domain.
- Leave the truly self-evident bare.
for (i = 0; ...), x * 2 to double,
arr[0] for the first element: naming these adds ceremony without meaning.
The test is whether a name would tell the reader anything the literal does
not already say.
- Name the recurrence even when each use looks obvious. A
0.08 tax rate
in five files is five edits and one guaranteed miss the day it changes. One
TAX_RATE constant makes the change atomic and greppable, whatever the
value's apparent simplicity.
- Put the constant where its meaning lives. A domain rule belongs beside
the domain code, or in a config it can be tuned from, not in a distant
constants.js dumping ground that forces a reader to jump away to learn one
number. Group by meaning, not by "it is a constant".
- Model a closed set as an enum, not loose constants. Order states, error
codes, and role levels are a fixed set: an enum lets the compiler reject
status === 7 and keeps the names traveling together. Scattered STATUS_A
and STATUS_B lose that check.
- Keep units and provenance in the name or a neighbor comment.
TIMEOUT_MS = 30000, not TIMEOUT = 30000; MAX_UPLOAD_BYTES, not
MAX_UPLOAD. Where the value comes from a spec or vendor limit, cite it so
no one quietly rounds it off later.
Checks
- Could a new reader change this threshold correctly using only the name, with
no source archaeology?
- Does the same value appear in more than one place under different literals?
Unify it before it diverges.
- Is a naked number carrying a unit (ms, bytes, percent) without saying so
anywhere near it?
Boundaries
Do not manufacture constants for genuinely obvious literals or one-off values
used once in clear context; over-naming buries the few numbers that matter under
noise like ONE = 1. Per-environment values belong in configuration, not source
constants. For repeated non-numeric literals and shared rules, this overlaps
code-duplication.
1---2name: magic-numbers3description: Replace unexplained literal values with named constants that carry their meaning, without over-formalizing the obvious. Use when a bare number or string encodes a rule.4---56# Magic numbers78A magic number is a literal whose meaning lives only in the author's head:9`if (age >= 18)`, `sleep(86400)`, `status === 3`. The reader cannot tell a10deliberate threshold from a typo, and a value that recurs must be found and11changed in every spot at once. The fix is a name, applied with judgment about12which literals actually need one.1314## Method15161. **Name any literal that encodes a rule or a fact.** `MINIMUM_AGE = 18`,17 `SECONDS_PER_DAY = 86400`, `Status.SHIPPED = 3`. The name turns a mystery18 value into a statement the reader can check against the domain.192. **Leave the truly self-evident bare.** `for (i = 0; ...)`, `x * 2` to double,20 `arr[0]` for the first element: naming these adds ceremony without meaning.21 The test is whether a name would tell the reader anything the literal does22 not already say.233. **Name the recurrence even when each use looks obvious.** A `0.08` tax rate24 in five files is five edits and one guaranteed miss the day it changes. One25 `TAX_RATE` constant makes the change atomic and greppable, whatever the26 value's apparent simplicity.274. **Put the constant where its meaning lives.** A domain rule belongs beside28 the domain code, or in a config it can be tuned from, not in a distant29 `constants.js` dumping ground that forces a reader to jump away to learn one30 number. Group by meaning, not by "it is a constant".315. **Model a closed set as an enum, not loose constants.** Order states, error32 codes, and role levels are a fixed set: an enum lets the compiler reject33 `status === 7` and keeps the names traveling together. Scattered `STATUS_A`34 and `STATUS_B` lose that check.356. **Keep units and provenance in the name or a neighbor comment.**36 `TIMEOUT_MS = 30000`, not `TIMEOUT = 30000`; `MAX_UPLOAD_BYTES`, not37 `MAX_UPLOAD`. Where the value comes from a spec or vendor limit, cite it so38 no one quietly rounds it off later.3940## Checks4142- Could a new reader change this threshold correctly using only the name, with43 no source archaeology?44- Does the same value appear in more than one place under different literals?45 Unify it before it diverges.46- Is a naked number carrying a unit (ms, bytes, percent) without saying so47 anywhere near it?4849## Boundaries5051Do not manufacture constants for genuinely obvious literals or one-off values52used once in clear context; over-naming buries the few numbers that matter under53noise like `ONE = 1`. Per-environment values belong in configuration, not source54constants. For repeated non-numeric literals and shared rules, this overlaps55code-duplication.