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

Immutability

quality PHP 8.1+ Intermediate
debt(d5/e5/b5/t5)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches' (d5), phpstan/psalm can flag mutation of readonly properties and detect public setters on value objects, but identifying conceptually-mutable-but-not-marked code requires the specialist tooling configured for it.

e5 Effort Remediation debt — work required to fix once spotted

Closest to 'touches multiple files / significant refactor in one component' (e5), the quick_fix suggests adding readonly or private setters returning new instances — but converting an existing mutable value object to immutable typically ripples through call sites that mutated it, requiring 'with*' methods and updated consumers.

b5 Burden Structural debt — long-term weight of choosing wrong

Closest to 'persistent productivity tax' (b5), immutability decisions apply across web/cli/queue contexts and shape how every value object and DTO is constructed and passed; affects many work streams but isn't quite system-defining (b7).

t5 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'notable trap' (t5), the misconception that readonly properties make the whole object immutable (when they only freeze the reference, not nested mutable state) is a documented gotcha most devs eventually learn — listed explicitly in common_mistakes.

About DEBT scoring →

Also Known As

immutable objects value objects read-only state

TL;DR

Objects whose state cannot change after construction — immutable objects are inherently thread-safe, predictable, and easy to reason about.

Explanation

Immutable objects prevent a class of bugs caused by shared mutable state: race conditions, unexpected side effects, and defensive copying. In PHP, immutability is implemented by returning new instances from mutating operations (like DateTimeImmutable), using readonly properties (PHP 8.1+), or by convention (no setters). Value objects are natural candidates for immutability. Immutability trades memory (new instances vs. mutation) for safety — an excellent trade in most PHP web request contexts.

Common Misconception

Immutability is only relevant in functional programming languages. Immutable value objects in PHP eliminate entire categories of bugs — unexpected mutation, shared state corruption, and difficult-to-trace side effects — regardless of language paradigm.

Why It Matters

Immutable objects cannot be changed after construction — they are inherently thread-safe, free from hidden side effects, and trivial to reason about. Mutable shared state is the root cause of the hardest bugs to reproduce and fix.

Common Mistakes

  • Declaring a property readonly but storing a mutable object in it — the reference is immutable, not the object's internal state.
  • Creating "immutable" objects with public setters — naming something immutable does not make it so.
  • Avoiding immutability in performance-critical code without profiling — modern PHP's copy-on-write makes immutability cheaper than it looks.
  • Making every object immutable regardless of context — value objects benefit most; entities that track change over time need mutability.

Code Examples

✗ Vulnerable
class DateRange {
    public \DateTime $start;
    public \DateTime $end;
}
$range->start = new \DateTime('tomorrow'); // surprise mutation
✓ Fixed
// PHP 8.2 readonly class — all properties are readonly
readonly class DateRange {
    public function __construct(
        public \DateTimeImmutable $start,
        public \DateTimeImmutable $end,
    ) {
        if ($end <= $start) throw new \InvalidArgumentException('end must be after start');
    }
    public function withEnd(\DateTimeImmutable $end): self {
        return new self($this->start, $end); // return new, don't mutate
    }
}

Added 13 Mar 2026
Edited 22 Mar 2026
Views 36
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 2 pings W 0 pings T 1 ping F 0 pings S 0 pings S 2 pings M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S 0 pings S 0 pings M 1 ping T 0 pings W 0 pings T 1 ping F 1 ping S 0 pings S 0 pings M 1 ping T 0 pings W 0 pings T 0 pings F
No pings yet today
No pings yesterday
Perplexity 9 Amazonbot 7 Unknown AI 4 Ahrefs 4 ChatGPT 3 SEMrush 2 Majestic 1 Google 1 Bing 1
crawler 30 crawler_json 1 pre-tracking 1
DEV INTEL Tools & Severity
🟡 Medium ⚙ Fix effort: Medium
⚡ Quick Fix
Use readonly properties (PHP 8.1) or private setters returning new instances — immutable objects are thread-safe, cacheable, and trivially testable
📦 Applies To
PHP 8.1+ web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
Value objects with public setters; DTOs mutated after creation; shared mutable state across requests
Auto-detectable: ✓ Yes phpstan psalm
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✗ Manual fix Fix: Medium Context: Class Tests: Update

✓ schema.org compliant