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
#!ek9 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: E11064, E11065, clean-code, comparison, constant, literal, magic, metric, named, number, quality, repeated, value