on intent
I have argued before that writing code is like writing a story. Now, I will take it one step further and claim that reading code is like reading a story. In turn, one can see code as a method of communicating intent, explicitly, e.g. through comments, or implicitly, e.g. through data structures. On the one end, code writers must consider how their code reads from the perspective of a different programmer. On the other end, the intent behind each line may make sense to the readers only after going down the abstraction layers. Hence, coding is an art. And like any other art, beauty is in the eye of the beholder.
LifeOverflow’s video on the German criminal law related to hacking has planted a seed in my mind: how do I represent intent when interacting with the computer? It is fascinating to imagine how tech consultants need to explain to lawyers - and, in turn, to judges - why some commands become malicious in some contexts. As an analogy, it is similar to explaining why the use of knives is malicious in some scenarios and benevolent in others. A good consultant-lawyer pair would find the distinct elements in every case and tilt the balance in the desired direction, for example: by identifying the target users, what information they were supposed to access, the level of protection of the restricted data, and the level of effort it took to gain access to that data. In turn, I have become aware of how I read code: by asking what the author wanted to achieve. Take as an example the almost always auto guideline for C++. The idea is to use auto to avoid cluttering the code with irrelevant types. With a lot said about this, the gist is to ignore the rule for most cases other than the obvious ones because sacrificing reading clarity in favour of writing speed is detrimental in the long run.
Furthermore, to fully grasp the intent, it is necessary to consider aspects not directly technical, such as historical and social ones. There has recently been a case at work where I reached for a preprocessor string instead of a C/C++ string because that fitted well with the rest of the code, and I could not change everything else. A similar example is about types in Python and how older code probably does not contain any type annotations because PEP 484 has only recently seen a lot of support and extensions - PEP 673. As the last example, I have worked in a company that banned comments because they don’t add anything of value, and developers should rather spend time making their code more explicit. All of these examples show that programmers must juggle non-obvious requirements when developing software and that the functionality of their tools is not the ultimate constraining factor.
Staying on the reading side, understanding a new codebase is all about identifying the chapters and sub-chapters of intent, i.e. the main functionality behind a class or a function, combined with the meaning behind each line of code when the complete details are necessary. The trick to efficiently doing this is to start by understanding the overall story and proceed by observing how different plots and lines interlink. As a partially relevant example, understanding most of the legacy codebase I primarily work on has taken me months: one, because there are close to a million lines of code, and two, because the goal has not been to finish fast but rather to study it. Having started in a new domain, I have found that the intent behind code usually requires explanations. In this case, code has reached the status of a means to an end: I was not studying code but its use case. With that in mind, a good reason for test-driven development is that, by writing the tests before the code, one tests for intent rather than implementation details. A similar argument applies to debugging, where the programmers check how well their original ideas have translated to code. It follows that there are categories of bugs: the ones arising from a poor translation of intent skipping over details, and the ones where the shortfall is in the implementation, e.g. the ideas are there, but the specific commands and instructions come with underlying side effects and historical backgrounds. I would argue that no category is more easily fixable than the other, as both present difficult cases.
To summarise, I’ve given a couple of reasons why the unifying concept behind all coding is intent. By looking at a program through this lens, one inherits a new way of understanding codebases of any size and in any programming language. It is the intent that readers follow in a new environment, and it is the intent writers try to achieve given a set of requirements. Given an all-encompassing context, it may not be enough, but it is the biggest and most transferable building block.