You called it to find something out.
It found out,
and it also wrote a row.
The function is called getAccount.
It looks the account up,
and when there is no account
it makes one,
because that was convenient
the week somebody needed it
and it saved a line at the call site.
So a read is a write now,
and nothing in the name admits it.
Watch what that costs.
None of it arrives as a bug report.
You lose the right to call it twice.
A function that only answers
can be called in a loop,
in a log line,
in a test,
in a debugger while you step over it,
and the world is the same afterwards.
The moment it writes,
every one of those is a decision.
Someone adds a call inside a logging statement
to help diagnose an incident,
and changes what production does
while trying to watch it.
Caching stops being safe.
You cannot put a result in front of a function
whose real work happens on the way through.
Retries stop being safe,
and retries are not yours to control.
A read can be repeated by any layer,
by a proxy, by a client,
by a framework you have never opened,
and most of your infrastructure
assumes it is allowed to.
Tests stop being honest.
Setup calls the lookup,
which quietly creates the very thing
the test was written to prove
would be created.
The rule underneath is older than our tools.
A function answers a question
or it changes the world.
Not both.
Asking should be free,
repeatable
and dull.
Changing should announce itself
in the name,
at the call site,
to somebody reading fast
on a Thursday afternoon.
So when you are tempted
to let a read do a little extra
because it is the handy place
and the alternative is one more function,
write the other function.
The convenience is worth about a week.
The surprise lasts as long as the code does,
and it will be found by somebody
who trusted the name
and had no reason on earth not to.
Names are not decoration
and they are not documentation.
They are the contract
everyone reads instead of the body.
– Serguey Asael Shinder
Top comments (0)