The report came out right
on Monday
and in the wrong order on Tuesday.
Nobody had touched the report.
Somebody had touched a helper
three calls away,
a small function
that found the top five customers.
It sorted the list it was handed.
In place.
Because that is what the sort method
in front of them did,
and it was one line shorter
than making a copy.
The helper returned the right five.
Its own tests were green.
And every caller
who had handed it a list
got that list back
rearranged,
with no sign on the call
that anything had happened.
This is the bug
that ruins an afternoon
precisely because it is nowhere.
You read the report code.
It is correct.
You read the helper.
It is correct.
The fault lives
in the space between them,
in an assumption
neither of them wrote down,
which is who owns the list.
It has relatives.
A function that pops the key
it was interested in
off a dictionary it did not create.
A validator that trims the strings
of the object it is validating,
so checking the input
quietly changes the input.
A default argument
that is one shared list
for every call,
growing a little each time.
A config object passed to three services,
and the first one to start up
adjusts a setting for itself.
In each case
a caller lent something
and got back something else.
The rule is easy to say.
A parameter is borrowed.
Read it.
Build your answer from it.
Return the answer as a new thing.
Leave what you were lent
the way it arrived.
When a function truly exists
to change what it was given,
put that in the name,
so the change is visible
at the place where it is called.
Sort in place.
Normalise the order.
Strip the payload.
A verb the caller can see
is a warning they can read.
And where the language lets you,
make the borrowing enforceable.
A read only view.
An immutable collection.
A type that refuses the edit
before a reviewer has to.
Then the question
of who changed the list
has one answer,
and it is in the code
you are already reading.
– Serguey Asael Shinder
Top comments (0)