Often times when reading code written by other people, or even written by myself a sufficient amount of time ago, I fall prey to a sort of choke, a deer-in-the-headlights moment, where deleting or changing existing code just feels wrong. The code I am refactoring has been tested (hopefully), has been reviewed (hopefully), and has been running in production without error (hopefully) for long enough that no one may remember why some things work the way they do. It's natural to lock up and not want to remove a solution, despite the fact that everything is under source control (hopefully). After all, it works! If it's been working in production and it's written a certain way, there must be a reason it was written that way, even if the reason doesn't make any sense to the reader.
Right? (Wrong)
It took this longer to sink in for me than I would like to admit, but there's plenty of code that works, has been tested, has been reviewed, but that has been written the way it has, for no particular reason. In the same way that you or I might write the following concise block:
function(x){
return somethingElse(someOperation(x));
// this is obviously a contrived example
}
rather than this more descriptive block:
function(x){
var y = someOperation(x);
var z = somethingElse(y);
return z;
}
Someone else might split variable declarations, or needlessly break things into functions, or not break things into functions when they should, because they're just trying to get the job done. I've often felt guilty for prioritizing deadlines and goals over conciseness and clarity, and to an extent I should. At the end of the day though, stable, tested, working code is better than "elegant" code, clean code, or code that has been reviewed and revised to death, but that never gets published. Plus, as much as we try and optimize, test, review, and generally beat to death just about everything, some parts of software development simply come down to preference. This isn't furniture, we may not want to come back and fix it, but we can if we have to.
What happens when we inevitably do come back and fix old code? As mentioned, I personally struggle to delete or re-write old code. Not because I am too lazy, or because I don't understand it, but because I figure it has been written the way it has for a reason, with a plan, by someone more well-versed in the problem. Far too often, in my experience, that feeling is wrong.
My natural inclination towards wanting to have a name for everything left me looking for a good description of this nebulous feeling, this ascription of outsized value and explicit intent to existing code. Recently I found it thanks to a great podcast I listen to called No Cartridge.
The feeling we've been talking about aligns well with a concept called The Intentional Fallacy, which comes from literary theory. The Intentional Fallacy, as laid out by W.K. Wimsatt and Munroe Beardsley, is the idea that "the design or intention of the author is neither available nor desirable as a standard for judging the success of a work of literary art." In the context of literary criticism, their point is that the author's intent cannot be reconstructed from the text, that the piece speaks for itself and cannot be reverse-engineered to get at any sort of hidden point the author may have been trying to make, or that the written piece elides. Furthermore, they argue that even notes about a piece, or contemporaneous journals from the author at the time of the piece's writing, are still only secondary sources for deriving authorial intent, and that pieces of work as an end product are always the primary source. The text may be an imperfect medium, but it is the only one we have when we want to judge the intent, meaning, value, and impact, of a piece. These ideas may not be exactly the same when talking about code, but we can see a rhyme scheme starting to form, right?
When evaluating a piece of code, it is often impossible to derive the intent of implementation details from the code itself. That is to say, of course we can try to derive the intent of the author because we see the beginning and end states of the data, the invoked methods, etcetera, but why certain things are done a certain way can often be lost.
Comments –if they're there at all- can illuminate some of that "why", but comments are often written in the grips of code myopia, that tunnel vision that sets in after you've been plugging away at something for so long that you've lost track of the overall goal. They can also document and codify intent around an approach that is fundamentally flawed, or lend credence to an overwrought solution when a simpler one exists. After all, we're not always writing code in perfect conditions and with clear heads. Comments also carry the risk of mis-communicating authorial intent, or not being updated when the code is, leading to even worse problems downstream.
Documentation might help, but it's not a great idea to document implementation details, because it can lead to brittle documents that need to be updated as often as the code is. Documents are best used for describing the contract that a piece of code adheres to, not questions about why certain things are done a certain way. The answer might be something like "it was 2:00 in the morning, a weird bug was popping up if I did it any other way, I needed to get it done, and we never touched it again". These answers might not be best practices or something we want to settle on, but I think anyone who has worked on a production codebase has seen something like this.
The question of how to avoid these sorts of problems is better left to much more experienced developers, and there's plenty of writing on the subject. My only suggestion is to keep the concern of writing clear, clean, focused code top of mind. I've found lambda services and a service-oriented architecture, to be helpful, but no approach is ever a silver bullet.
My broader point is that code, once written, can only be questioned by its function and the reader's understanding of it. It cannot be questioned through the lens of the author's intent in any reliable way, even if you have access to the original author. Trust yourself, and if the code doesn't make sense after some effort, that's not an indictment of your intelligence. It means there's an opportunity to understand and improve a section of your codebase. If it was written a certain way on purpose, you will invariably run up against that limitation yourself. At that point you may have the opportunity to conquer that limitation, and permanently improve your team's work product. You have just as much ownership of your codebase as the original author, and you may have more of a mandate to change things if you have been expressly asked to refactor or update some particular functionality. When you do, I suggest you feel free to dig into as much of the prior implementation as you want. It'll be hard to conquer bigger and bigger problems if you're afraid of continuously playing around with functionality that seems opaque. Just remember to try and leave code clearer than it was when you came in, because someone might come to you one day asking why you implemented something a certain way, and you might realize that it really is hard to figure out the intent of an author 😄.
If you enjoyed this post, please feel free to recommend it on dev.to, share it, and give me a follow!
Also if you liked this topic and want to see a regular stream of similar stuff, or just want to get in touch, follow me on Twitter @dangolant! Also, big shoutout to my friends @thejaredwilcurt and @milkstarz on Twitter for notes.