C/C++ Users Journal March, 2005
I recently had occasion to review some old C code. This was stuff I'd written over 25 years ago. It doesn't exactly conform to the C Standardhardly surprising since it was written several years before work on that document even began. And yet it is easily understandable by a younger programmer who cut his/her teeth on Standard C. In fact, it took remarkably few changes to get the code to make it through our current batch of C compilers. And to execute properly, by the way.
I undertook this exercise for reasons other than nostalgia. Seems the code was useful enough that I've included it in my personal kit of tools for quite some time. But all I had was executables compiled for MS-DOS about 17 years ago. Remember .com files? They execute in a 64-KB address space using Intel x86 16-bit pointers to access all code and data. You might be surprised to learn that such critters still execute properly in a Command Prompt window on a Pentium running Windows XP. So the tools suffer from capacity problems, not to mention a pesky bug or two that I've lived with for years but would love to see disappear.
I feel the usual programmer's itch to rewrite all this stuff, of course, but I have to resist it. I can justify taking the time to update the code and fix the (small) problems, but not go off on a major tangent. Running a software company that has to generate a steady succession of new and enhanced products leaves little time for recreational programming. But please note that the urge to rewrite is driven far more by aesthetics than a need for revision. The code works and is obviously quite useful to meit ain't broke enough to need major fixing.
By aesthetics, I mean things like the choice of names and the use of comments and whitespace. I tend to change such conventions about once every 5 or 10 years, which puts this code several generations back for me. The changes mostly reflect evolving goals more than a rejection of past notions. These days, for example, I write code for millions of potential reviewers, not just a small cadre of maintainers. I write for multiple compilers, not just one that I can personally control. And, of course, I write for machines that are thousands of times larger and faster than those of the 1980s. Different constraints demand different conventions.
But the basic structure of the code differs little from the choices I would make today. By 1980, I had fully internalized the need for cleanly structured control flow (in the sense of structured programming) and carefully organized modularization (in the sense of structured design). Brian Kernighan and I had long since written our books on programming style and software tool building. I had not yet met object-oriented programming, but seemed to have an intuitive grasp of the need to keep tightly bound each data structure and the functions that manipulate it. The only modern influences not at all visible were template metaprogramming and generic programming, and I still use only a weak form of either of these techniques when writing C code. (C++ is quite another matter, of course.)
So what would I have done differently had I written this code last month instead of 25 years ago? I'd have put in more provision for testing components from the get go. And I would have developed the tests alongside the code, then kept the test code closer to the executable in the source tree. Maybe I would have added more commentary about any subtle interactions between components, but probably not. I prefer to avoid such subtle interactions in the first place. They're the source of many of the bugs I introduce during maintenance, and of most of the bugs that others introduce when they have to maintain my overly clever code.
I don't mean to suggest that all code should be usable for decades, by the way. Much of the stuff we write has a shorter target lifespan than that, and it would be uneconomical to engineer everything as though it will last forever. Still, I've often been shocked at how long code does endure, particularly if you're a tool builder by instinct. I remember my surprise 30 years ago to learn that Fortran programs I hadn't seen for a decade were still in regular use at labs I'd left behind. Luckily, they were good enough not to be a clear and present danger to those who still trusted them. That taught me to err on the side of longevity for any code I write.
Even so, I threw away my last Fortran card decks when we cleaned the attic last fall. They had served their purpose.
P.J. Plauger
Senior Contributing Editor
pjp@plauger.com