Code, before writing any
April 10, 2023
This will be one of the shorter ones, where I wish to share a really underrated professional hack. At least in my opinion. I had the fortune to learn this early and it somehow stayed with me from my early days. And that is the importance of structuring your thoughts and understanding the problem in front of you, before even starting to work on it.
It is laughable how simple it is. Write it down. Hmm?? Before writing a single line of code, I have a simple process. Read the problem statement and try to map my understanding of it in simple terms. Breaking down the problem to its basics on a simple piece of paper. Or just in an editor, using comments or markdown. I noticed that it depends on the size of the problem and amount of the focus I wish to dedicate to it. When working on larger features, I usually get the pen and paper as it does require some investment upfront compared to the bug.
You will often hear this referred to as pseudocode. This may or not may be the case, as I sometimes just try to make a mental map of the problem. Whatever works for the given scenario at hand. If it is an algorithmic kind of problem, pseudocode makes sense. If it is an architectural or just clarification of the problem statement, then I don’t find the pseudocode a good “fit”. Again, this will heavily depend on you and how you “clarify” things for yourself. I bounce between simple diagrams, sometimes pseudocode is in the mix, whatever gives me the mental map of the maze. This is the entrance, now find the exit.
The less cognitive load I am having when thinking about the problem means the process is working. I am able to break it down to a simple and clear understanding of each piece and how they fit into the final puzzle. After that, comes the easy part: Writing actual code. One of the most common sayings I have, when it comes to the topic:
💡 How do I know what I am going to deliver if I don’t even understand it enough to put it on a piece of paper?
Simple meaning and, in my experience, shows a lot. Running head first does work sometimes, depending on your understanding of the problem domain. But often ends up in frustration and in hours of wasted time. Which could have been spent, a fraction of it, to just try and explain the problem and see if you actually understand what will be when X + Y = ?
This way of “thinking before coding” comes with a number of benefits. The first among them will be: Your code will have a good set of meaningful tests associated with it. Oh, do tell? When I have it clear in my head, what is the thing I wish to deliver and in what format, I translate that into the set of tests. The process is similar to Test Driven Development (TDD), on the other hand, is less tedious. I started in recent years calling this Requirments Driven Testing, where I translate those clear thoughts from the paper into the number of both functional and non-functional tests. Depending on the problem size, the number will be much lower. Mostly as I try to write them as E2E as possible. And then for a non-functional, small number of unit tests that cover some assumptions I took in the implementation. I noticed that this kind of tests, except helping me to verify my thought process, will help me with just making a small transition from paper to code. I translate a scribble on paper to a test that gives meaning to what I am trying to implement in the code. And while tackling one by one, requirements are implemented and tested at the same time. But I will be elaborating on this a bit more in some future posts.
The next benefit of this approach is: Refactoring comes naturally. As you’re solving the problem, while paired with the previous benefit of tests, it will become clearer when actually you need to introduce some “higher” concepts from the books. Things like patterns or such. No need to over-optimize, to begin with, just solving the problem with a newfound focus will give you freedom from the cognitive load of thinking about the problem. You already know what is the solution, now you’re just translating it into the methods. So the brain has the capacity now to be clever and burden free of one less thing. While the tests you wrote beforehand will give you one less thing to worry about, will it still work if I add an X pattern here?
One of my favorite benefits is: A piece of mind when releasing. Ok, you lost me, say whaaat?? Simply put, the confidence you develop this way is so much higher than just banging on the keyboard and praying to whatever you pray to when pressing the deploy button. Without even exaggerating, I am 95% confident that the things I release to the customer will be working. LOL, but where did that 5% go?? As with anything, 100% in the software world is wishful thinking. Here I give 1% to things I can’t control, for example, Microsoft releasing some new patch or update to SDK that breaks my code in unpredictable ways. The other 4% I attribute to the biggest foe of modern software development. Bad communication and alignment within the team/project. It does happen that people think: Nah, this won’t break. Just to find out that someone changed some state you depend on and your feature goes belly up. But other than that, I know I spent my time understanding the problem, wrote tests, and added monitoring and logging. I did my dulidigance to the best of my ability. What I could think of, I prepared for. All this will just add to that “magic number” of 95%.
💡 The code I trust the least in production is my own. So I test it and test it and test it. I will spend more time making sure it works than implementing it because when it breaks the amount of time invested in fixing it is much higher than if I just used a fraction of it to verify my assumptions.
This is also one of the things I often repeat, like a broken record. But it should be a mantra for most developers, in my opinion. Simple reason: The amount of time (and will not mention revenue or customer trust) it is needed to fix the issue that could have been easily preventable is exponentially higher. Then we go from a single developer to: N developers trying to fix the issue. The math there is simple, right?
To finish up. Here I mentioned some of the benefits, from my point of view and experience, from clarification of the problem domain to yourself. In some future posts, I will write more about how to make it a bit part of a process within the team using things like ADRs and some other ways. The point is simple: Make the problem statement clear for yourself and the outcome will be predictable. And the process above is my own, so feel free to make one for yourself and reap the benefits of it.
Until next time, write down your thoughts.