One of the things that annoys me the most about programming is bad code, especially if I'm the one that wrote it. And I know I'm not alone, because I work with other programmers, good programmers, and I hear them say, "Who wrote that crap?" knowing good and well they wrote it themselves.
It happens to all of us. If we write a lot of code, sooner or later, we're going to write code that embarrasses us because we know better than to do what we just did. And I don't believe it's simply a matter of being rushed, or being tired, or just having a bad day. I think there's a fundamental problem that makes bad code difficult to avoid unless and until we address it.
Simply put, the problem is complexity. The mind can only juggle a relatively small number of different topics simultaneously, which means that each new principle we need to remember competes for brainspace against all the other principles we need to remember. When we are working with a framework that has multiple moving parts that all have to be managed in concert, or when we're working in a language that requires a lot of boilerplate or conventions or rules that are not directly related to the tasks we're trying to accomplish, all of those different requirements are competing against other principles that we also need to remember, like the difference between "=" and "==" in if statements.
If we try to juggle too many competing ideas at once, some of them are liable to fall out of our working brainspace. It's not exactly that we forget basic principles like checking that an object is defined before calling a method on it. It's just that there's no working space left to process that rule, and therefore it just doesn't happen. The result is code that is embarrassingly bad, in the middle of code that is pretty complicated stuff that you wouldn't give to a bad engineer.
From here we could go off and talk about how this contributes to "imposter syndrome" and the frequent suspicion that we're not as good as other people think we are. After all, could we really be that good when we check in code that bad? And the answer is, yes, I think we can, we're just experiencing the consequences of a fundamental problem that we still need to address.
We probably all have standard work-arounds that we've picked up over the years. For example, we probably go back and re-read our code looking for stupid bugs (and maybe not-so-stupid ones). This helps, because we can unload a lot of the earlier concerns that were filling up our brainspace, and reload the broader, more fundamental concerns, like checking for common errors and bad code. The problem is that this takes time, and it's fairly common for deadlines and other pressures to take that time away from us.
Pair programming is another good remedy, because now you have the advantage of two brainspaces, effectively giving you up to double the number of concerns you can actively be paying attention to. It's actually less than double, because you'll probably have some number of concerns that both programmers are going to be thinking about, but still, it's an improvement, provided you actually do it.
But the real solution is not to work around the complexity, but rather to eliminate it, or at least as much of it as you can. That means first of all understanding the difference between essential complexity and incidental complexity. If a project is complicated, how much of that is due to the actual business process being complex, and how much of it is due to the mechanical constraints of the programming language and/or framework we're coding in? In fact, how much of the complexity is due to failing to understand the business requirements clearly and accurately in the first place?
Ideally, every programming project should start with a clear, simplified, minimalistic understanding of the problem to be solved, and by that I mean breaking things down into appropriate, self-contained, and self-sufficient domains. Granted, this attempt will almost certainly fail at first, for any non-trivial project, but it needs to be a first step in every iteration that's going to generate a new code release. Turn off the computers, grab some pencils and 3x5's, and spend some time coming up with the simplest and most consistent description you can for the entities and processes you're going to have to represent in the code. Eliminate complications due to ambiguities and garbled requirements. Reduce the tasks down to their most straightforward theoretical implementation, and only then turn the computers back on.
Of course, there's a lot to be said for taking that problem and working on it using languages and tools that minimize incidental complexity. For that, you can't beat functional languages, and the more functional, the better. Object-oriented design has its uses, but incidental complexity is built into objects because they conflate data and function. Pure functions and pure data are simpler, and therefore better, because each of them is only one thing, and therefore requires less brainspace to work with.
But I don't want to go off on a tangent here. The main point is that the remedy for bad code---and especially bad code coming from good coders---is to reduce complexity to a workable level. Brainspace is too valuable to waste on empty rituals that exist only to satisfy the demands of some generic framework or language requirement. When we can focus our attention on the actual job that needs to be done, free from distractions, we will write better code. Guaranteed.
No comments:
Post a Comment