Oct 312024
 

I came across 2 tweets (because they’re on Twitter, not X) recently that really seem to be on opposite sides of the same coin. The first, from Kelsey Hightower, was simply a call to think before coding. The second, from The Primeagen, pointing out that writing the code is far from simple. Now, these 2 tweets aren’t mutually exclusive, or even make competing points (I don’t believe for a second The Primeagen didn’t think about the problem before he started coding). But taken together they offer good reminder about both not coding from the hip, as well as the limits of trying to think through everything before starting the actual code writing.

Generally, I tend to lean towards Hightower in that coding is I also think writing the code is 1 of the last steps in problem-solving, and a largely overrated part of software development vs. understanding systems, problem domains, logical thinking, and being able to unambiguously document the results of that understanding into instructions (although AI-generated code seems to be proving that belief to be a lie). 1 of the first lessons programmers are taught is that computers are stupid, they will do exactly what they’re told to do – no more, and no less, which is why well thought-out algorithms are so important (for what I maintain is the absolute best representation of what this looks like in the real world, nothing beats the Holderness family’s exact instructions challenge video).

What was really strange about the responses to Hightower’s tweet was how the early comments seemed to jump straight into “that’s waterfall.” Really? Any amount of thinking and examining the problem up-front means you have to have planned and documented every little thing that’s going to happen? What about if you’re working on some routine maintenance and not even building something as part of a larger project? Is it waterfall then? It’s entirely possible to do small bits of work, and think about those bits individually before sitting down to type anything. Generally speaking, competent developers will think about the problem before actually trying to code a solution, good developers will think about the problem in the context of the problem domain, great developers will think about it in the context of both the problem domain and (when applicable) systems outside of their team, and the best developers think about problems in the context the problem domain and how it fits into the business overall.

I’ve been the “just start coding” guy when I was younger. And then something would happen and something would get messed up or I’d end up coding around in circles getting nowhere, and would just delete everything and start over. However, when I started over – this time with more details and knowledge about the problem I’m trying to solve and the tools I have available (in other words, having had to think about the problem trying to debug the stuff I wrote without thinking) – the re-write was always faster, smaller, and cleaner. It was invariably when the initial “just code it” somehow worked that I’d look back and see code that was harder to understand, full of what seemed like extra steps just inviting bugs in, and in general just a larger pile of WTFs per minute. The problem is that you can get away with crap like that in school, because you rarely go back to old homework assignments and projects to change them, but in the working world, doing something like this quickly turns your code into a giant turd pile that people hate working in so much, they question your competence.

For the record, I’m not saying that for every piece of work you’re given, you should code something that works, then re-write the whole thing. That’s a waste of time. What I am saying is that the better you understand the problem first, the better the code you write to solve it. And no, nothing about trying to understand the problem means don’t write code. It means you need to know what you need to write and why you’re writing it before you start. Thinking first also doesn’t mean you need to design an entire end-to-end system before the first line of code gets typed into an editor. Nothing about Hightower’s tweet means you shouldn’t be writing code (if needed), just that you’re not going to write code that’s any good if you don’t understand the problem and existing code. You can still think first even when tackling large problems 1 smaller piece at a time, just stop and think about what you’re trying to do for each piece when the time comes to work on it. In my experience, the alternative generally leads to a lot of wheel-reinventing and yak shaving.

The biggest problem with the “stop and think first” mindset is that it’s easy to get yourself into an infinite loop of consideration. For me, the less code already exists, the easier it is to slip into eternal considerations of “I could {do this}…” The reality is it doesn’t take long to come up with a “good enough” route forward to get started (even if there’s still a part of the problem that I still need to solve, the Pareto Principle says I can still get most of the work done). It’s easy to imagine what you’re going to do, especially if you’re in a stage dedicated to “just thinking about the problem” – but just because writing code is 1 of the last steps doesn’t mean it’s not still a step that has to be completed. The key is to have thought about the problem enough to have enough information to get started, and understand the problem well enough to know the basic steps to solve the problem, even if some of the details still need to be worked out.

But what about The Primeagen’s tweet? If thinking first is such a key do successfully, then why was he claiming that doing the coding was far from the easiest part, especially considering he would have thought through the problem first? That’s because there is a limit to how far upfront thinking and planning can take you. You don’t hit edge cases until you’re running code against a variety of inputs and data, and you can’t do that without code. That doesn’t mean stopping to think things through is useless, but rather it’s benefits are mainly trying to understand what constitutes a valid solution, what you already have that you can work with, the high-level steps that need to happen to solve the problem, and what successfully solving the problem looks like (so you know how to test for success). Once you can fill in those 4 blanks, you’re ready to start testing your solution, by writing code and running it.

The reality of software development is that sometimes your initial understanding of the problem is just wrong, or there’s weird cases that you wouldn’t have thought of until you stumbled into it while running the code you just wrote. Sometimes the general solution you came up with was actually correct, but there’s a subtle logical flaw in how you implemented that solution that’s a pain to track down. Some things are far easier to describe on paper than they are to actually implement correctly (most encryption algorithms fall into this category). Just because coding is 1 of the last steps of the software development process doesn’t mean that it’s unimportant – just that it’s easy to overwhelm yourself if you jump straight in without some sort of concept what you’re going to do when you start.

It’s absolutely absurd to expect any given developer to have the full context of a large-scale software application in their head. There’s really only 2 ways anyone can reasonably do this: 1) they’ve basically lived in that application for 8+ hours a day, 40+ hours a week, for years or 2) they wrote the whole thing single-handedly. But you can absolutely hold a piece of the application in your head (at least well enough that you know where to jump to for digging into specifics) at a time, and an initial thinking step is instrumental for loading that information in your head, helping surface some questions that should be answered up-front, and having a general direction for writing code. This helps avoid wild code chases and duplicating a bunch of logic that already existed somewhere else in the code base.

 Posted by at 11:45 AM