Replace Magic Literal
Intent
Section titled “Intent”Find a bare literal — a number like 9.80665 or 86400, a string like "ADMIN" — that carries meaning the reader has to guess at, and replace it with a named constant. The name explains what the value is for, and the single definition becomes the one place to change it if it ever must change.
The smell
Section titled “The smell”This cures the Magic Number (and its sibling, the magic string). The tell is a literal whose meaning is not obvious from context, especially one that appears in more than one place. When 0.07 shows up in the pricing module and again in the invoice module, two problems lurk: a reader cannot tell that both mean “the sales tax rate,” and the day the rate changes, you must find and edit every copy — and you will miss one.
Before → After
Section titled “Before → After”A shipping-cost calculation peppered with unexplained numbers. After, each literal is a named constant whose name carries the intent.
// Beforefunction shippingCost(weightKg: number): number { if (weightKg > 30) { return weightKg * 2.5 + 15; } return weightKg * 2.5;}
// Afterconst RATE_PER_KG = 2.5;const HEAVY_THRESHOLD_KG = 30;const HEAVY_SURCHARGE = 15;
function shippingCost(weightKg: number): number { const base = weightKg * RATE_PER_KG; return weightKg > HEAVY_THRESHOLD_KG ? base + HEAVY_SURCHARGE : base;}# Beforedef shipping_cost(weight_kg): if weight_kg > 30: return weight_kg * 2.5 + 15 return weight_kg * 2.5
# AfterRATE_PER_KG = 2.5HEAVY_THRESHOLD_KG = 30HEAVY_SURCHARGE = 15
def shipping_cost(weight_kg): base = weight_kg * RATE_PER_KG return base + HEAVY_SURCHARGE if weight_kg > HEAVY_THRESHOLD_KG else base// Beforefunc ShippingCost(weightKg float64) float64 { if weightKg > 30 { return weightKg*2.5 + 15 } return weightKg * 2.5}
// Afterconst ( ratePerKg = 2.5 heavyThresholdKg = 30.0 heavySurcharge = 15.0)
func ShippingCost(weightKg float64) float64 { base := weightKg * ratePerKg if weightKg > heavyThresholdKg { return base + heavySurcharge } return base}// Beforefn shipping_cost(weight_kg: f64) -> f64 { if weight_kg > 30.0 { weight_kg * 2.5 + 15.0 } else { weight_kg * 2.5 }}
// Afterconst RATE_PER_KG: f64 = 2.5;const HEAVY_THRESHOLD_KG: f64 = 30.0;const HEAVY_SURCHARGE: f64 = 15.0;
fn shipping_cost(weight_kg: f64) -> f64 { let base = weight_kg * RATE_PER_KG; if weight_kg > HEAVY_THRESHOLD_KG { base + HEAVY_SURCHARGE } else { base }}flowchart LR
subgraph Before["Before"]
A["code: weight * 9.80665"]
B["code: if status == 2"]
end
subgraph After["After"]
C["GRAVITY = 9.80665"]
D["STATUS_SHIPPED = 2"]
C --> E["code: weight * GRAVITY"]
D --> F["code: if status == STATUS_SHIPPED"]
end
Before -.->|"Replace Magic Literal"| After Mechanics
Section titled “Mechanics”- Declare a constant and assign it the literal value. Give it a name that says what the value means, not what it is —
HEAVY_THRESHOLD_KG, notTHIRTY. - Find one use of the literal. Confirm it truly means the same thing as the constant — two unrelated
2s are not the same magic number. - Replace that use with the constant.
- Run your tests.
- Repeat for each remaining use that shares the meaning.
- When all matching uses are replaced, the literal lives in exactly one place. Changing the value is now a one-line edit.
When to use / trade-offs
Section titled “When to use / trade-offs”Replace a literal whenever its meaning is not self-evident, and especially whenever the same meaningful value appears more than once. The named constant doubles as documentation and as a single point of change.
Two cautions. First, do not replace genuinely self-explanatory literals: index + 1 or multiplying by 2 to double a value needs no constant — a name like ONE only adds noise. Second, watch for coincidental equality: if two 100s mean “percent scale” and “max retries,” they must become two different constants, never one shared name, or a future edit to one will silently corrupt the other.