Failing Fast, Good Coupling and Locality
We are told to fail fast: oxymoronically, errors exist to aid you in fixing problems. We are told coupling is bad. However, coupling is sometimes good.
I think I encountered a good microcosmic example of the importance of failing fast, good coupling and locality: locality meaning how close things are to where they take effect. Usually it’s in reference to computer memory and its impact on performance. I want to discuss locality as it relates to source code and its impact on readability.
This example is about strings—%-formatted strings.
Today, I fixed a bug from two years ago.
While playing, my friend Kyle stumbled upon a funny piece of in-game text:
"Hey! Are you %s? %s from %s's Quest?!"
The game carried on like normal, but that piece gave us a laugh.
A quick and easy fix: just add the missing % (character.name)
bit.
…Ok, in this case, % ((character.name,) * 3)
However, the repercussions are worrisome:
- This pattern is used extensively throughout the codebase. How many more times does this bug appear?
- This could potentially be more harmful. The program continued in this case. If I remembered the %, but supplied the incorrect number of values, I would get a TypeError and execution would stop!
❦
What caused this to happen?
By using %-strings, I am prone to these mistakes because the compiler permits them:
- Forgetting the %-values
- Including the wrong number of %-values
That means I get runtime errors instead.
In my situation, if the program had simply failed instead when it noticed the missing % (arguments)
, I would have fixed my issue and not have to worry. I don’t believe it has the power to do that though, since %s
is dual-purposed as a percent sign, contingent on the omission of the % (arguments)
, as I had mistakenly committed.
Now %-strings are a good choice for coding in a small, manageable capacity, but Toshe’s Quest II has a lot of lines of code.
❦
How can I do better?
Enter f-strings
.
f-strings are great because they localize the formatted string values. Instead of the string template and values being side-by-side, the values become embedded within the template:
f"Hey! Are you {character.name}? {character.name} from Toshe's Quest?!"
The f-string is easier to read, which in general reduces bugs. It also completely eliminates the bug scenarios I mentioned! By coupling template and values, to forget or miscount values would mean a malformed template. That would be much easier to spot.
The caveat is that this feature did not become available until Python 3. I can see now that I had to make do with what was available, given my choice to continue using Python 2. What a wild time that was! The next question to ponder might be would it have been worth it to migrate to Python 3, and at which point during development would that have been?
