What's "good" code and does it matter?

Wednesday, October 14, 2020

I take pride in my work and in writing good code, and it's important sometimes to take a step back and ask: what does that even mean? And does it matter?

At a high level, "good code" is code that is suitable for its purpose and achieves its goals. That definition is pretty lacking, though, I think. You can write some very very hacky prototypes that achieve their goals—proving out an idea—while also being pretty objectively bad code. But objectively, by what measure?

This is where we get back into what it means for code to really be suitable for its purpose. Code is a living thing. It is written, edited, read, and used, in order of increasing frequency: most code will be used far more often than it is edited, read far more often than it is edited, and edited far more often than it's newly written. This means that for code to be good, it has to support these activities. It has to do its job well when it's used. It has to be able to be read. It has to be able to be updated.

A great deal has been written already on how code can do its job well. The short summary here is that it has to do what is expected (per the spec, if you are fortunate enough to have one) and have few defects. On the non-functional side, it has to also do what's expected in a reasonable amount of time, reliably. It doesn't matter how free of bugs your program is if it literally never terminates. And sometimes a "reasonable amount of time" might actually be a floor on the time for things like bcrypt, which we want to make reasonably slow.

Supporting reading and editing go hand-in-hand, because they are core parts of maintaining a codebase. You cannot really edit a codebase confidently if you cannot read it and understand what it's doing, and you cannot fix bugs or add features if you cannot edit it confidently. While tests are a big portion of this, they are distinct from the quality of the code under test. In an ideal world, they add assurance, but the code itself should have a clear design that presents itself. It should be designed from the outset to be extensible.

But does that really capture what we're doing day-to-day? I'm a software engineer, not a computer programmer. While I take pride in writing good code, my job is not to produce good code but to effectively solve problems, usually using code. In practice, engineering means you have to make tradeoffs.

When you're trying to solve a problem, but you're not sure exactly how to solve it, you reach for prototypes and proofs of concept. These will be sufficient to test an idea and validate the approach, but you can cut a lot of corners on them. The code doesn't look good, it's almost certainly not maintainable long-term. But is this a good engineering decision? In a lot of cases, yes! It's the right tradeoff to make.

Similarly, you can write the absolute best code you have ever created for that shiny new feature, but... realistically, you're probably working on it in a business, and realistically, improving that quality to make it super readable and super extensible won't deliver value to the business. It really depends on how much the code will be extended and read, and it's also a tradeoff between time now (for a startup burning cash, time right now is in very short supply!) and time later (once you get profitable or take another infusion of cash from rich suckers venture capitalists, you can afford to rewrite things).

This comes back in a lot of decisions you have to make as a software engineer. If you design a super-scalable system that can handle all the traffic you will need three years from now... well, that growth will probably never materialize, because you did not spend that time developing your product right now. It's often a better decision to write something that works okay for now, and refactor/rewrite later when you need to scale up.

So, does writing good code matter?

It does, to an extent. Your code has to be good enough to do its job, which is usually to deliver value and to optimize more for the here and now than down the road, scale that might never materialize. If your code does its job well enough and it can be (maybe somewhat painfully) maintained and updated for a couple of years, well, by the time those two years are gone you may well have rewritten it anyway! If you'd spent twice as long at the outset writing your magnus opus, that time would have been wasted.

Your code can't be a dumpster fire. It probably shouldn't be the Mona Lisa, either. Striking a balance is an important facet of engineering for all things, ranging from the quality of your code (good, but not too good!) to how much scale to handle (enough, but don't over-engineer it!) to how much coffee to drink (just kidding, never too much coffee).