← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

Primitive Obsession

quality PHP 5.0+ Intermediate

Also Known As

primitive overuse stringly typed value type smell

TL;DR

Using raw strings, ints, and arrays to represent domain concepts instead of small dedicated value objects.

Explanation

Primitive obsession is using primitive types (string, int, bool, array) for concepts that deserve their own class — e.g. representing a phone number as a plain string, a money amount as a float, or a coordinate pair as two separate variables. Primitives have no domain validation, no domain-specific methods, and no documentation of their meaning. Introducing small value objects (PhoneNumber, Money, Coordinate) gives the type a name, centralises validation, and makes illegal states unrepresentable.

Common Misconception

Using primitives for everything is simpler than creating wrapper classes. A string for an email address cannot enforce format, a float for money cannot prevent rounding errors, and an int for a user ID can be accidentally passed where a product ID is expected — value objects eliminate these classes of bugs.

Why It Matters

Using raw strings and integers for domain concepts (email, money, coordinates) means validation and behaviour are scattered everywhere — wrapping them in value objects centralises rules and makes types self-documenting.

Common Mistakes

  • Passing $email as a string everywhere instead of an Email value object that validates on construction.
  • Using int for money — floating point errors and currency handling belong in a Money class.
  • Not noticing when a cluster of primitives always travels together — that is a data clump and a value object candidate.
  • Creating value objects without making them immutable — a mutable Email can change to invalid state after construction.

Code Examples

✗ Vulnerable
function registerUser(string $email, string $role, int $age): void {
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        throw new \InvalidArgumentException('Bad email');
    }
    // $email, $role, $age scattered everywhere as raw strings/ints
}
✓ Fixed
readonly class Email {
    public function __construct(public string $value) {
        if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
            throw new \InvalidArgumentException("Invalid email: $value");
        }
    }
}

function registerUser(Email $email, Role $role, Age $age): void {
    // types carry their own validation — impossible to pass invalid data
}

Added 15 Mar 2026
Edited 22 Mar 2026
Views 28
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings F 2 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 2 pings T 0 pings F 2 pings S 0 pings S 0 pings M 0 pings T 1 ping W 0 pings T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 1 ping S
No pings yesterday
Amazonbot 9 Perplexity 6 Ahrefs 2 Unknown AI 2 ChatGPT 2 Google 1
crawler 21 crawler_json 1
DEV INTEL Tools & Severity
🟢 Low ⚙ Fix effort: Medium
⚡ Quick Fix
Replace a cluster of related primitives (string $email, string $currency, int $amount) with a Value Object that encapsulates validation and behaviour
📦 Applies To
PHP 5.0+ web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
Function with 5+ string/int parameters representing domain concepts or repeated format-validation of same field
Auto-detectable: ✗ No phpstan psalm
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: High ✗ Manual fix Fix: High Context: File Tests: Update

✓ schema.org compliant