Last week I wrote a post called “Code Age vs Time to Recall” that briefly graced the front page of Hacker News. I’d recommend you read it before this one. Ironically it’s one of the few posts I’ve written that didn’t call for at least one redraft; at the time of writing it’s been viewed nearly 4000 times (my most-viewed post by far.) In summary, I spoke about the way old code is often perceived to be bad in development teams because it takes longer to recall. How can we reduce this “time to recall” by writing code that’s easy to remember? And to what extent are we responsible for sharing the context around our decisions with others?
Remembering things plays such an important role in our work as developers: strands of conversations, Slack messages, Trello cards, spec documents. All of these different sources only increase our cognitive load; at some point we’re bound to forget something. I think if we’re able to parse and condense a complex set of requirements into code form, we owe a courtesy to our colleagues (and future selves) to explain the context to our decisions, to help them understand why we did things a certain way.
While it would be futile to attempt to commit to memory code we didn’t interact with very often, we can speed up the process of recall by writing code that’s easy to understand. Doing so might involve using certain build tools or linters or ensuring the formatting of our files is correct; as one HN commenter said, “Painful now, pleasurable later, hopefully.”
Where a few years ago I might have scoffed at the idea of a temporary variable, variable names can themselves convey meaning (assuming creating one isn’t detrimental to performance) and the same applies to class and function names (language permitting) which serve to structure and expound the code they encapsulate. We often talk about names being too verbose (wordy) but sometimes verbosity is a superpower. Much better is it, in my opinion, to overcommunicate what something does and why it is, than to risk its purpose being unclear in a month or year’s time.
Git commit messages are extremely valuable sources of context. An underappreciated feature of Sourcetree is that you can search for past commits by message (and file change.) A well written commit message should both describe what changed and why, for example, “Replace duplicate Task markup with CommentList component”. If I can understand the context to a decision merely by reading a commit message, my time to recall is significantly reduced. Alternatively there is some context to be found in the date of the commit and the branch name, which might also tie in with a pull request. As an aside, I’ve always written my commit messages as imperatives (so they can be read in order as a list of instructions) but I’m sure there are other conventions for this.
Some of the comments on last week’s HN post spoke of automated testing, which itself serves as a form of “functional documentation”. Tests are the link between a set of requirements (potentially a non-technical document) and code. Again, if we’re able to understand the requirements surrounding code, the time to recall is lessened. In many ways, tests also provide a kind of “entrenchment” in that they prevent unnecessary changes being made later on. If a test fails as a result of a refactor, the code no longer meets the requirement.
Not only do we often make decisions in code as individuals, but often we make decisions with other developers in pull requests. Not only is the clarity of what you say important to the developer making the changes but the more information you can supply (references to files by name, line numbers etc), the more easily you or one of your colleagues will find what they need at some point in the distant future. I’d go as far to say that you should write your PR comments with the assumption that they’ll be read again by someone else at a later time.
Whether formally acknowledged or not, I think all developers are responsible to some extent for reducing the “time to recall” the context surrounding our code. We owe it to ourselves and our colleagues to share as much of this context as possible, in code, in commit messages, in tests and in pull requests so that old code can be understood just as well as the new.