Why must comparison values use named constants instead of bare literals?
← Code Quality · Ref: Q695
EK9 detects 'magic literals' in comparisons and requires them to be extracted to named constants. This makes code self-documenting and maintainable.
MAGIC LITERAL DETECTION (E11064)
Bare numeric or string literals in comparisons are flagged:
if temperature > 100.0 // E11064: what does 100.0 mean? if temperature > BOILING_POINT // Correct: named constant
CONSTANT DECLARATION
Use 'defines constant' to declare named values:
defines constant BOILING_POINT <- 100.0 FREEZING_POINT <- 0.0 MAX_RETRIES <- 3
WHAT TRIGGERS E11064
Literals in comparison expressions (==, <>, <, >, <=, >=) are checked. Literals in declarations and assignments are not flagged.
BENEFITS
1. Self-documenting: BOILING_POINT vs 100.0
2. Single source of truth: change one constant, all comparisons update
3. Searchable: find all uses of BOILING_POINT
See Q310 for code quality overview. See Q313 for coding standards. See Q694 for named arguments.
Example
defines module qa.codequality.namedconstants defines constant //Named constants make comparisons self-documenting FREEZING_CELSIUS <- 0.0 BOILING_CELSIUS <- 100.0 ADULT_THRESHOLD <- 18 RETIREMENT_THRESHOLD <- 65 MAX_PASSWORD_LENGTH <- 128 MIN_PASSWORD_LENGTH <- 8 defines function <?- Correct: comparisons use named constants. Every threshold value has a meaningful name. -?> classifyWater() as pure -> temperatureCelsius as Float <- phase as String: "liquid" if temperatureCelsius <= FREEZING_CELSIUS phase: "solid" else if temperatureCelsius >= BOILING_CELSIUS phase: "gas" <?- Correct: age thresholds as named constants. -?> ageCategory() as pure -> personAge as Integer <- category as String: "adult" if personAge < ADULT_THRESHOLD category: "minor" else if personAge >= RETIREMENT_THRESHOLD category: "senior" <?- Correct: password length limits as named constants. -?> validatePasswordLength() as pure -> passwordText as String <- isValid as Boolean: false passwordLength <- length passwordText isValid: passwordLength >= MIN_PASSWORD_LENGTH and passwordLength <= MAX_PASSWORD_LENGTH defines program NamedConstantsDemo() stdout <- Stdout() stdout.println(classifyWater(-5.0)) stdout.println(classifyWater(50.0)) stdout.println(classifyWater(105.0)) stdout.println(ageCategory(15)) stdout.println(ageCategory(30)) stdout.println(ageCategory(70)) stdout.println(`Valid password: ${validatePasswordLength("secureP1")}`) stdout.println(`Too short: ${validatePasswordLength("abc")}`)
Common mistakes
E50060 — String has no toUpperCase() method in EK9. Use upperCase() instead. See ek9 -h E50060 for details.
Incorrect:
stdout.println(classifyWater(-5.0).toUpperCase())
Correct:
stdout.println(classifyWater(-5.0))
Other ways to ask this
- What is E11064 magic literal in comparison?
- How do I avoid magic numbers in EK9?
- Must I extract all literals to constants?
- What triggers the magic literal check?
Coming from another language?
Java: no magic literal enforcement (only static analysis tools). Python: no enforcement. C++: no enforcement. Rust: Clippy warns about magic numbers. Go: no enforcement. EK9: compile error for magic literals in comparisons.
Keywords: named, migrate, literal, E11064, comparison, self-documenting, clean-code, metric, magic, constant, quality