Primitive Obsession¶
The code smell of representing domain concepts that carry structural commitments — units, valid ranges, well-formedness, identity — as bare primitives like int or string, so the type system cannot enforce the distinctions and they survive only as validation duplicated at every callsite.
Core Idea¶
Primitive obsession is the software-design code smell in which a codebase represents domain concepts carrying rich structural commitments — units, valid ranges, well-formedness rules, identity semantics — as bare primitives (int, string, float). The flattening discards the domain's distinctions from the representation, so the compiler and reader can no longer tell a money amount from a temperature; the distinctions survive only as ad-hoc validation and conversion duplicated at every callsite. Fowler catalogued it in Refactoring with typed countermoves.
Scope of Application¶
Lives within software design and refactoring — wherever a type system can encode the domain's distinctions and a compiler can enforce them.
- Object-oriented refactoring and DDD — Fowler's Replace Data Value with Object; value objects and domain primitives.
- Functional / type-driven design — Haskell newtypes, TypeScript branded types, sum types making illegal states unrepresentable.
- Strongly-typed embedded systems — compile-time units-of-measure algebra (F#, Ada SPARK, Boost.Units).
- Modern multi-paradigm languages — Rust newtypes, Scala/Kotlin value classes, Swift wrappers.
- Code-quality review — flagged alongside its Fowler siblings (god class, data clumps).
Clarity¶
Naming primitive obsession separates the symptom a maintainer keeps tripping over — duplicated validation, parameter-swap bugs the compiler waves through — from its single cause: the absence of a domain type to carry the constraint. Without the name each instance looks like an isolated lapse patched at the callsite, which proliferates the pattern. It also makes a deceptively reasonable local decision auditable, reframing "of course an email is a string" as "what is the summed, non-local cost of the type system not knowing?"
Manages Complexity¶
A troubled codebase presents a scatter of unrelated complaints: duplicated validation, a swap bug, a milliseconds-versus-microseconds outage, a lat/long data clump, magic strings. Primitive obsession collapses that list to one diagnostic keyed off a single binary per field — primitive where a domain type is warranted, or not — from which the whole syndrome is predicted. The corrective side collapses too, to a small fixed toolbox that moves enforcement off the callsites into the type layer once.
Abstract Reasoning¶
The concept licenses a diagnostic move (collapse a scatter of symptoms to one cause and predict the rest of the cluster), a boundary-drawing move (separate a generic primitive from a flattened domain value, by whether the type system's ignorance carries a summed, non-local cost), and an interventionist move (apply Replace Data Value with Object or parse-don't-validate at the boundary, rejecting the reflex of one more callsite check, and predict errors shift from runtime to compile time).
Knowledge Transfer¶
Within software design the concept transfers as mechanism, well-established across paradigms — the same diagnostic, non-local-cost test, and enforcement-in-the-type-layer stance carrying from OO refactoring to functional type-driven design to embedded units-of-measure systems, since all share an encodable type system. Beyond software the named smell does not transfer non-metaphorically, but the principle it instantiates — represent the domain's commitments in the medium rather than flattening them — is already the parent prime abstraction applied at the representation layer, which carries the cross-domain lesson. The refactoring vocabulary and compiler payoff stay home.
Relationships to Other Abstractions¶
Current abstraction Primitive Obsession Domain-specific
Parents (1) — more general patterns this builds on
-
Primitive Obsession is a kind of Code Smell Domain-specific
Primitive Obsession is the code-smell species whose repeated use of bare built-ins provides defeasible evidence that warranted domain distinctions are missing from the type representation.
Hierarchy paths (4) — routes to 4 parentless roots
- Primitive Obsession → Code Smell → Evidence → Provenance → Traceability → Observability
- Primitive Obsession → Code Smell → Evidence → Provenance → Attestation → Authentication
- Primitive Obsession → Code Smell → Evidence → Provenance → Traceability → Transformation → Function (Mapping)
- Primitive Obsession → Code Smell → Evidence → Provenance → Custody Transfer → State and State Transition → Phase Space
Neighborhood in Abstraction Space¶
Primitive Obsession sits in a crowded region of the domain-specific corpus (13th percentile for distinctiveness): several abstractions share nearly its structure, so a description that fits it tends to fit its neighbors too.
Family — Surface Form & Underlying Structure (23 abstractions)
Nearest neighbors
- Abstract Syntax Tree — 0.88
- Long Parameter List — 0.87
- Near-equivalence Mapping — 0.87
- Data Class — 0.86
- Greenspun's Tenth Rule — 0.86
Computed from structural-signature embeddings · 2026-07-12