When you start learning Object-Oriented Programming, sooner or later it happens.
Someone says:
“Let’s make a simple example.”
And the example is almost always the same:
class Person {
public string $name;
public int $age;
public function think() {}
public function decide() {}
}
The legendary Person class.
It looks harmless.
It even looks natural.
But there’s a small problem.
This class is lying to you.
And it’s doing it with good intentions.
The first (necessary) lie of OOP
The lie is not technical.
It’s conceptual.
That Person class silently suggests that:
- a person can be fully described
- their state is knowable
- their decisions are modellable
- their values are consistent
Of course, we know this isn’t true.
And yet, we keep using this example.
Why?
Because programming is not about describing reality.
It’s about reducing chaos into something we can manage.
The Person class is not reality.
It’s a map.
And maps always lie a little.
The problem is not simplification
The problem is forgetting it
Simplification is not a mistake.
It’s unavoidable.
The problem starts when:
- we confuse the map with the territory
- we assume the model is reality
- we blindly trust what the software “decides”
This is where serious problems begin, especially in complex systems.
A small thought experiment
Let’s try a game.
What if we wrote a Person class that explicitly admits its limits?
Not a simulation of a human being.
Not an artificial mind.
Just an honest object.
Something like this:
final class Person
{
private string $id;
private string $name;
// Observed state (never complete)
private array $states = [];
// Declared values (not always respected)
private array $values = [];
// Action history (not the truth)
private array $history = [];
public function __construct(string $id, string $name)
{
$this->id = $id;
$this->name = $name;
}
// Observe a piece of reality
public function observe(string $key, mixed $value): void
{
$this->states[$key] = $value;
$this->history[] = [
'type' => 'observation',
'key' => $key,
'time' => time(),
];
}
// Declare a value
public function declareValue(string $value): void
{
$this->values[] = $value;
}
// Decide under uncertainty
public function decide(array $options): string
{
// Not optimal, just plausible
$choice = $options[array_rand($options)];
$this->history[] = [
'type' => 'decision',
'choice' => $choice,
'time' => time(),
];
return $choice;
}
// Expose history, not truth
public function history(): array
{
return $this->history;
}
}
What actually changes?
This Person class openly states that:
- state is partial
- values are not hard rules
- decisions are not optimal
- history is not truth, just a trace In other words:
the software does not pretend to understand everything
Paradoxically, this makes it safer.
Why this matters (outside toy examples)
In real systems — business software, enterprise platforms, infrastructure —
problems rarely happen because software is “stupid”.
They happen because software:
- makes silent decisions
- auto-corrects without saying so
- acts without leaving traces
- closes in on itself
A system becomes dangerous when it stops admitting its own limits.
A simple idea to take with you
OOP is not meant to simulate human beings.
It’s meant to make explicit what we can model
and, more importantly, what we cannot.
The classic Person class is not wrong.
It’s incomplete.
The mistake is not writing it.
The mistake is forgetting that it lies a little.
Conclusion
The best software is not the one that “decides better”.
It’s the one that:
- exposes its rules
- leaves traces
- asks for confirmation
- admits uncertainty
Maybe the first thing we should teach
when we introduce the Person class
is not how it works…
but where it stops.
Top comments (0)