Published: April 25, 2022 • 3 min read
In Things You Should Never Do, Part I, Joel Spolsky narrates Netscape’s ruinous decision to rewrite their browser from scratch. This introduced the following concept to me:
“It’s harder to read code than to write it.”
I believe this. Today I’d like to explain why.
As an example, consider the “liking” feature of Today I Learned. This is a button under each Today I Learned post that you can click to announce: “I like this.”
When we explore TIL’s codebase, we see this method called when a visitor likes a post. I’m going to examine this method line-by-line and raise questions that I might raise while coding, as if I’d never seen it.
# app/models/posts.rb
def increment_likes
self.max_likes += 1 if self.max_likes == self.likes
self.likes += 1
notify_slack_on_likes_threshold if likes_threshold?
save
end
First line:
def increment_likes
This name suggests that likes
is a column on the model. If true, why do we
need a method? It’s not obvious.
Next:
self.max_likes += 1 if self.max_likes == self.likes
Okay, what is max_likes
? ‘Max’ as a prefix is pretty vague. Why are we
comparing that to likes
and only incrementing it when they’re equal? This
comparison would benefit from abstraction to a method like
new_max_likes?
, or abstracting the whole line to maybe_increment_max_likes
.
Then:
self.likes += 1
Incrementing the likes; I get this.
Next:
notify_slack_on_likes_threshold if likes_threshold?
So we’re notifying Slack about something called _likes_threshold
, if
likes_threshold?
is true. This abstraction feels incomplete. Why can’t
notify_slack_on_likes_threshold
know if we’re in the right state? Once
we refactor it, maybe we could give it name like maybe_notify_slack
.
Last line:
save
Why explicitly save? Could this be combined with the likes
incrementing via .update
?
That finishes the method. It’s short enough to appease Sandi Metz. But it’s doing a lot, with room to improve.
There’s a big piece of information missing from this analysis: this method was written to help us send an idempotent notification to Slack.
When we built TIL, we wanted to know which posts were popular, so we added liking. We chose to send messages to Slack when these likes crossed important thresholds (e.g. 100 likes).
max_likes
came from this. Before it, if somebody clicked ‘like’ at nine
likes, bumping the count to ten, then un-clicked it, and then clicked it again,
our code would send two Slack notifications. We needed idempotency, hence
max_likes
, which tracks the highest amount of likes that has ever been for
a post.
That feature was important, because it proved that our writing was having an impact. As we saw likes increment, we saw what resonated. It’s a little thing that helped made TIL a hit.
Someone reading this code would not have this context.
Practically, what happens when this context is absent?
If you’re rewriting an application from scratch, these questions hardly ever get asked. Given you even have the code, you can’t take the time to explore every method in this detail. You’re speed-running. Whoever wrote the feature is gone. If it is used, everyone takes it for granted.
In the unlikely event you do read this method, you’re going to say: “What’s happening here? I don’t get it. This isn’t MVP” and move on. This kind of omission is the rule in a rewrite. You’re only building the stuff that’s ‘important’, with bias, ignorance, and inattention coloring that definition.
As gnarly as legacy code is, it works and people have to come to rely on it. That is worth our attention. When we read legacy code thoughtfully, we elevate our profession.
At AWS they almost never retire an API. Once it’s in production, it’s supported forever. Many companies can’t act this way, but the spirit prevents thoughtless mistakes like breaking the likes notification.
I think code reading is the highest programming skill you can have. Scanning a line, putting it in words, precisely jumping to a function definition in multiple codebases, returning without getting lost, and adding to the bigger story you’re telling yourself or those participating— few programmers do this well. I want to encourage us all to work on being better code readers.
“It’s harder to read code than to write it.” What does this statement mean to you?
Thanks to Josh Branchaud for this blog post idea.
What are your thoughts on this? Let me know!
Get better at programming by learning with me! Join my 100+ subscribers receiving weekly ideas, creations, and curated resources from across the world of programming.