Primitive Obsession: What It Costs and How to Fix It
Using primitive types (strings, ints, booleans) instead of small value objects for domain concepts.
What It Is
Primitive Obsession occurs when developers represent domain concepts like email addresses, phone numbers, currencies, or status codes as raw strings and integers instead of creating dedicated types. The code works, but validation logic gets scattered across every function that handles these values, and the type system cannot help catch mistakes at compile time. A US ZIP code as a string can silently accept 'banana' as a value.
Why It Costs Money
Validation logic is duplicated everywhere. When an email address is a plain string, every function that accepts an email must validate the format independently. Miss one validation site and you have a bug. Teams report finding 5-15 duplicate validation blocks per primitive concept in mature codebases.
Refactoring becomes dangerous. When a business rule changes (for example, supporting international phone number formats), developers must find and update every location that handles the raw primitive. Missing even one site creates a subtle bug that may not surface for weeks.
Type safety is lost. The compiler cannot distinguish between a string that represents an email and a string that represents a username. Function signatures like processUser(string, string, string, int) are impossible to call correctly without reading the implementation.
Specific Cost Mechanisms
- ●Scattered validation: 5-15 duplicated validation sites per concept, each a potential inconsistency
- ●Rule change propagation: each business rule change requires finding and updating all validation sites (4-8 hours per change)
- ●Type confusion bugs: wrong-argument-order bugs that pass code review because the types are all identical
Estimated Annual Cost
Cost per instance by team size and codebase size. Based on $120,000 average developer salary. See full methodology.
| Team Size | Small (<50k LOC) | Medium (50k-200k) | Large (200k+) |
|---|---|---|---|
| 3 devs | $2,400 | $5,400 | $10,800 |
| 5 devs | $4,000 | $9,000 | $18,000 |
| 10 devs | $6,000 | $13,500 | $18,000 |
| 20 devs | $8,000 | $18,000 | $18,000 |
How to Detect It
Specific rules and thresholds for automated detection. See full tool comparison.
Too many parameters, high primitive type usage
Methods with > 4 primitive parameters
Catches literal values that should be named constants or types
Flags parameter lists suggesting primitive overuse
Refactoring Patterns
Proven techniques to eliminate this smell. See all refactoring patterns.
Replace Data Value with Object
A primitive carries domain meaning (email, currency, ZIP code)
Introduce Parameter Object
Multiple primitives always travel together as function parameters
Replace Type Code with Subclass
An integer or string represents a fixed set of states