This looks familiar

William Woody has a post about developing Java code to implement the factorial function. More specifically, it’s about how not to. He starts with a very simple (and straightforward) implementation, then produces updated version after alternate version in order to handle various possible conditions that the code may need to support.

The operative word is “may.” He concludes the post with a discussion of appropriate pains to take in an implementation, as well as which requirements for a programming task are actual requirements. As he says,

So please, do us all a favor: if you have the urge to add complexity because “someday we’ll need it, I just know it!”, or because “it’s not sufficiently flexible enough” or “we need reusability in our code” or (God help us!) because it’s “cool”–just go home early.

I’ve not programmed in Java, but the points he makes are applicable no matter what the language. I believe that part of the problem is that there is a subgroup of programmers who aren’t happy unless they’re showing off how convoluted they can make their code. I remember a cartoon I saw, either in an issue of Forth Dimensions or in one of Leo Brodie’s books, Starting Forth or Thinking Forth. The cartoon shows two programmers in space looking at the Earth-Moon system. One, the Forth programmer, is saying, “No good – too complex.” The other, labeled, I believe, “Ada programmer,” is saying, “No good, too simple.”

I’ve worked with both types of programmer, and I’m currently maintaining a large piece of code written over a decade ago by one of the “no good, too simple” types. He came from a background in academia, and felt that his MSCS made him more qualified to define application requirements and design products than the combined 30 or so years experience in the field that my boss and I had at the time, because neither of us had a Master’s degree.

It’s pretty hard to figure out what any given piece of code in that application does, because nothing is simple and straightforward. It’s all convoluted, indirect, and involved. It puts me in mind of a button I bought at an SF convention a few decades ago, which reads, “Never make anything simple and efficient when you can make it complex and wonderful.” As an example, the program maintains a pair of ring buffers. Conceptually, these are fairly simple and straightforward. There are some boundary conditions you need to be careful with, which are discussed at the link, but other than that, they’re easy.

In the software I’m maintaining, they were implemented as state machines with 64 states. Talk about violating Occam’s Razor! The C source file containing the implementation is 15k in size, with almost 600 lines of actual code. What comments there are in the file mostly relate to the encoding and meaning of the state numbers – very little of the code has descriptive comments stating what the code is doing.

Years ago, I ran across the statement that the fastest-executing and most bug-free code in a system is the code that isn’t there. That made tremendous sense to me, and it’s why the ring-buffer implementation I just described offends my sensibilities so much. To me, it’s just intellectual masturbation. He wasn’t writing for efficiency, or for clarity, although I’ve no doubt he’d claim that that was exactly what he’d done. He was writing to show off, to himself if no-one else. There may have been some attempt at job-security-through-obfuscated-code going on, as well. If so, that didn’t work. After all, I’m maintaining his code, and have no idea where he is, nor where he’s been for years. Actually, although the comments in the file describe it as a “robust” ring-buffer implementation, I had to make a correction to it several years ago – there was a bug in his code (color me surprised) that caused a persistent error condition after a particular error occurred. The code, of course, couldn’t detect that it was in that particular error state. Eventually, though, a customer could.

I probably put more emphasis on clean and simple code than most programmers. It often ends up longer than other implementations, because I tend to ignore a number of advanced features of the language, unless the code is meant to demonstrate the use of that feature. This is because most of our customers aren’t programmers; they’re engineers who want to learn as little programming as necessary to get the job done, and the code I write has to serve a pedagogical purpose, as well as being useful. As a result, the code I write tends to be very simple with a lot of comments. Unfortunately, that doesn’t always mean it’s bug-free.

Coding style may be a matter of preference, but coding complexity, at least in this regard, is a matter of corporate culture. At a previous job developing dedicated word processors (that is, a computer system that could only do word processing), back in the early 1980s, I was at one point tasked with making an update to a particular piece of code. The code was designed to be incredibly efficient; it was written in an incredibly convoluted manner in order to minimize execution time. It took me three weeks to understand the code, after which the change took less than a day. Coincidentally, the original author had taken three weeks to write the code.

The convoluted code saved several milliseconds over what a naive implementation would have taken. It executed once during system startup. I suspect that you can guess my feelings about the efficiency of that module.

The author moved on to the “new product” project that the company started. Apart from a few supervisors, nobody in the “current product” group could move over to the new product group – they staffed it up with recent graduates who’d used Unix in school. That was their problem. Apart from the supervisors, several of whom were addicted to cleverness in their code, nobody working on the new product had ever delivered a product.

The current product line that was providing all the company’s income was based on 8086 hubs and 6800-based terminals (80×24 characters, IIRC, but they might have had larger displays). The new product under development was to be 68000-based, with a bit-mapped graphical display so that you could do WYSIWYG word processing. They gave us a product demonstration at one point. The system took somewhere between 30 and 90 seconds to come up (this was around 1983 when most personal computers were effectively instant-on, and even PC-XTs only took a few seconds to boot DOS), and the bug list was written ceiling-to-floor several times on butcher paper that covered one wall. One bug was that you couldn’t print – not a good thing for a word processor company’s upcoming flagship product.

This has been a long rant, but my point is largely the same as Mr. Woody’s: complexity for the sake of complexity, or for anticipated future needs, is a bad thing. My elaboration on that point is: make sure you’ve got someone who knows what needs to be done, and ruthlessly prevents unnecessary complexity.

Comments are closed.