BloatersMedium

Primitive Obsession: What It Costs and How to Fix It

Using primitive types (strings, ints, booleans) instead of small value objects for domain concepts.

Annual Cost$2.4k - $18k
Severity
3/5
CategoryBloaters
Detection4 tools

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.

Threshold: When the same primitive value is validated in 3+ locations, it warrants a value object. Parameter lists with 4+ primitives of the same type are strong indicators.

Why It Costs Money

1

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.

2

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.

3

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 SizeSmall (<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.

SonarQube
squid:S107 / squid:S1200

Too many parameters, high primitive type usage

CodeClimate
argument-count

Methods with > 4 primitive parameters

ESLint
no-magic-numbers

Catches literal values that should be named constants or types

PMD
AvoidUsingShortType / ExcessiveParameterList

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)

Effort: 1-3 hours per value object
Impact: Eliminates all duplicated validation for that concept

Introduce Parameter Object

Multiple primitives always travel together as function parameters

Effort: 1-2 hours
Impact: Reduces parameter count and enables shared validation

Replace Type Code with Subclass

An integer or string represents a fixed set of states

Effort: 2-4 hours
Impact: Enables compile-time checking of state transitions

Related Smells