How does EK9 detect magic literals?
← Code Quality · Ref: Q317
EK9 detects magic literals using a two-tier system. Magic literals are unnamed numeric or string values embedded directly in code. They obscure intent and make code harder to maintain.
TIER 1: NUMERIC LITERALS AT DECISION POINTS (E11064)
An Integer or Float literal used as an operand of a comparison or a range is flagged. Comparisons are decision points where the meaning of the value matters most. Instead of 'if age >= 18', use a named value 'if age >= legalAdultAge'. The operators are ==, <>, !=, <, >, <=, >=, <=>, <~>, contains and matches (so 'values contains 5' is flagged too).
Not flagged by E11064: the values 0, 1, -1, 0.0 and 1.0; every other literal type (String, RegEx, Character, Boolean, Date, Money and so on - 'word == "admin"' and 'word matches /ad.*/' are fine); and literals inside a constant declaration, require/assert, a constrain declaration or a named argument. A for-range's bounds (for i in 1 ... 10) are not checked.
TIER 2: REPEATED STRING LITERALS (E11065)
A plain double-quoted String literal with 3 or more characters of content that appears 3 or more times in the same file, or 4 or more times across files of the same module, is flagged. Repetition means the value has significance worth naming. Only Strings are counted: numeric, Boolean, Character and RegEx literals, backtick templates and "" are never counted. Literals inside a constant declaration, a list or dict literal, require/assert, a constrain declaration, a range, string concatenation or a named argument are not counted either. A constructor call is NOT exempt: label the arguments or name a shared constant.
CONSTANT ORGANISATION (E11066-E11067)
E11066: a module's constants are spread across 3 or more files - consolidate them into one file.
E11067: a module has 10 or more constants across 2 or more files - give them a dedicated constants file.
Neither check is about constant naming.
NAMED CONSTANTS PATTERN
Define meaningful names at the top of the function or in a constants block:
maximumRetries <- 3 timeoutSeconds <- 30 minimumPasswordLength <- 8
The name documents WHY this value was chosen.
See Q311 for the full quality checks catalog. See Q140 for defining constants. See Q141 for constant immutability.
Example
defines module qa.codequality.magic defines function <?- Demonstrates using named constants instead of magic literals. Every threshold has a meaningful name that documents its purpose. -?> classifyTemperature() as pure -> temperatureCelsius as Float <- classification as String: "moderate" freezingPoint <- 0.0 coldThreshold <- 10.0 hotThreshold <- 30.0 if temperatureCelsius <= freezingPoint classification: "freezing" else if temperatureCelsius <= coldThreshold classification: "cold" else if temperatureCelsius >= hotThreshold classification: "hot" <?- Named constants make password validation self-documenting. -?> isPasswordStrong() as pure -> password as String <- rtn as Boolean: false minimumLength <- 8 if length password >= minimumLength rtn: true defines program MagicLiteralsDemo() stdout <- Stdout() currentTemp <- 25.0 weatherReport <- classifyTemperature(currentTemp) stdout.println(`Temperature ${currentTemp}C is ${weatherReport}`) testPassword <- "SecurePass123" strongEnough <- isPasswordStrong(testPassword) stdout.println(`Password strong: ${strongEnough}`)
Common mistakes
E11064 — A Float literal such as 30.0 in a comparison is a magic literal (only 0.0 and 1.0 are exempt). Name it hotThreshold so the reader knows what the value means. See ek9 -h E11064 for details.
Incorrect:
if temperatureCelsius <= freezingPoint
classification: "freezing"
else if temperatureCelsius <= coldThreshold
classification: "cold"
else if temperatureCelsius >= 30.0
Correct:
hotThreshold <- 30.0 if temperatureCelsius <= freezingPoint classification: "freezing" else if temperatureCelsius <= coldThreshold classification: "cold" else if temperatureCelsius >= hotThreshold
E11064 — Using a raw literal 8 in a comparison is a magic literal. Name it minimumLength to document why this value was chosen. See ek9 -h E11064 for details.
Incorrect:
if length password >= 8
Correct:
minimumLength <- 8 if length password >= minimumLength
Other ways to ask this
- What are magic numbers in EK9?
- Why does EK9 reject literal values in comparisons?
- How do I fix E11064 magic literal error?
Coming from another language?
Java: SonarQube has MagicNumber rule but optional and configurable. Checkstyle MagicNumber only catches numerics, not strings. Rust: clippy has no magic number detection. Go: no magic number detection. Python: pylint has magic-value-comparison but optional. C++: no standard magic number detection. EK9: two-tier magic literal detection, mandatory compiler error: numeric literals at comparisons and ranges (E11064, except 0/1/-1/0.0/1.0), and repeated String literals (E11065, 3+ per file or 4+ per module).
Keywords: number, literal, constant, value, comparison, E11064, E11065, metric, repeated, magic, named, quality, clean-code