← Home ← Codex ← DEBT ← Engine
Browse by Category
+ added · updated 7d
← Back to glossary

Tell Don't Ask Principle

Code Quality PHP 5.0+ Intermediate
debt(d7/e5/b5/t5)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). The detection_hints indicate automated detection is 'no' and while phpstan is listed as a tool, it cannot reliably detect TDA violations as a general rule — it would only catch specific patterns. The code_pattern shows the smell is contextual (is this getter driving logic that belongs in the object?) and requires a reviewer to understand domain responsibilities, not a static analysis rule.

e5 Effort Remediation debt — work required to fix once spotted

Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix says 'move logic into the class that owns the data', which sounds simple, but in practice the common_mistakes describe feature envy and anemic domain models — patterns that often permeate a service layer or domain, requiring logic to be redistributed across multiple classes and their callers, touching multiple files per fix instance.

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

Closest to 'persistent productivity tax' (b5). Applies across web, cli, and queue-worker contexts (broad applies_to). Violating TDA consistently leads to anemic domain models, which slow down every future domain change as logic is scattered in services rather than in the objects that own the data. It's not purely architectural (b7/b9) but it does persistently tax maintainers across many work streams.

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

Closest to 'notable trap (a documented gotcha most devs eventually learn)' (t5). The misconception field explicitly states the trap: developers believe TDA means 'never read state from objects', but it actually only prohibits querying state to make decisions the object itself should make. Getters for display/reporting are fine. This is a well-documented nuance that most developers misunderstand until they've been corrected, aligning with t5.

About DEBT scoring →

Also Known As

tell-dont-ask principle TDA command vs query objects

TL;DR

Tell objects what to do rather than asking for their state and deciding externally — keep behaviour with the data it operates on.

Explanation

Tell Don't Ask (Pragmatic Programmers / Martin Fowler) states that you should command objects to perform operations rather than querying their state and acting on it externally. Asking for data and making decisions outside the object is procedural thinking dressed in classes. The fix is to move the decision logic inside the object — this is often a feature envy refactoring and increases cohesion. It doesn't mean getter methods are always wrong, but their use should be questioned.

Common Misconception

Tell don't ask means never reading state from objects. It means not querying an object's state to make decisions that the object itself should make — getters for reporting and display are fine; getters used to drive logic that belongs inside the object are the smell.

Why It Matters

Tell, Don't Ask means instructing objects to perform behaviour rather than querying their state to make decisions externally — it keeps logic where the data is and prevents anemic domain models.

Common Mistakes

  • Querying object state to make a decision and then calling a method based on it — give the object the decision.
  • if ($user->isAdmin()) $user->setPermission('all') — the User should handle permission assignment internally.
  • Feature envy: a service that queries every property of a domain object to compute something the object could compute.
  • Confusing TDA with avoiding queries entirely — queries for display or reporting are fine; avoid them for behaviour decisions.

Code Examples

✗ Vulnerable
// ASK — pulling data out to make decisions
if ($user->getAccount()->getBalance() < $order->getTotal()) {
    throw new InsufficientFundsException();
}
✓ Fixed
// TELL — give the object the information and let it decide
$user->getAccount()->debit($order->getTotal()); // Account throws InsufficientFundsException internally

Added 15 Mar 2026
Edited 22 Mar 2026
Views 112
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
1 ping F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 0 pings F 0 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 1 ping W 2 pings T 0 pings F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W 1 ping T 0 pings F 0 pings S
No pings yet today
No pings yesterday
Amazonbot 12 Ahrefs 8 SEMrush 7 Google 6 ChatGPT 6 Scrapy 6 Perplexity 4 Unknown AI 4 PetalBot 4 Bing 2 Applebot 2 Meta AI 1 Twitter/X 1 Brave Search 1 Qwen 1 Sogou 1
crawler 61 crawler_json 4 pre-tracking 1
DEV INTEL Tools & Severity
🟢 Low ⚙ Fix effort: Medium
⚡ Quick Fix
Instead of asking an object for data then acting on it, tell the object to do the work itself — move logic into the class that owns the data
📦 Applies To
PHP 5.0+ web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
$order->getStatus() === 'pending' then calling something else — should be $order->isPending() or $order->cancel()
Auto-detectable: ✗ No phpstan
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: High ✗ Manual fix Fix: High Context: Class Tests: Update


✓ schema.org compliant