Recently a message was posted on COMP.LANG.EIFFEL that criticized Bertrand Meyer’s advice to project managers: “when hiring, beware of C hackers”. As one would expect a spirited discussion followed. Although parts of the debate went off in strange directions, it seemed to me that the main argument was driven by the age old tension between the low and high levels of software. It was a fight between the “bubble-and-arrow” crowd and the “bit-twiddlers” – design vs. code.
I am sure that you are familiar with the extreme positions of both camps. The disciples of the “design-is-king” school say, design is what software is all about. Once the design is complete the implementation is just a trivial conversion into some programming language. A good OO design will translate into a wonderful OO system, even if it is implemented in a non-OO language, like let’s say …(gasp!)… COBOL.
On the other hand, the “bit-twiddlers” and compulsive coders reply – you can’t make “bubble-and-arrow” pictures execute on the computer. The only thing that is important is the code, therefore there is no point wasting time drawing pretty charts, as that time could be better spent coding.
Clearly, both of these extreme positions are flawed. To start with I have two objections to the “design-is-king” view. First, I strongly object to the idea that the implementation language is unimportant. If the gap between the design concepts and the programming language is large, then the programmer must work harder to translate between the two. Moreover, such translation is a tedious, boring and an error prone process full of, what Fred Brooks calls, “incidental complexities”. In a word, it’s a process best left to computers.
My second objection is to some designer’s reckless disregard for the implementer’s task. I have yet to see a design for any sizable system that was easily translated into code and ran perfectly the first time. There are just too many issues that are not considered by the designers but must be faced by the implementors – machine limitations in memory and CPU speed can render the most beautiful design unimplementable, unexpected dependencies between subsystems can make debugging hellish, and sluggish response time can make the system useless.
At the other extreme, “bit-twiddlers” position is not without serious problems either. The low-level gods often spent too much time so close to the machine, that they miss the forest for the trees – they never see the big picture. What good is there in optimizing a routine that only gets called once, or in coding bubble sort in assembler? Low-level code maybe faster and it may use less memory, but it does no good if it solves the wrong problem. Also, we should not forget the poor soul who will have to fix the bits of low-level brilliance left by some wizard who has moved on to greener pastures.
The process of developing and maintaining software, that is software engineering, has many levels. Sometimes we have to work at a high level, as during analysis and design, and at other times at a lower level, as during coding and debugging. A true software engineer must be familiar with all these level, as ignoring one at the expense of another only leads to problems.
Moreover, note that the longest part of a successful project is the unglamorous “maintenance” phase. Many programs remain in use long after the paper on which their design was written has been shredded and recycled. To survive and flourish in the maintenance mainstream the software engineer must be prepared to work at any level of the software process: he must be able to extract the design from the source code and extend it to add new features, and he must be able to find and correct problems when the program crashes.
The analysis of failures is perhaps the most difficult task, to which the engineer needs to bring as much knowledge, understanding and as many tools as possible. This is not only true of software engineering, but also it is the at the heart of other engineering disciplines. By analyzing failures we can discover its causes and then we can re-design our software to avoid the same failure in the future. As Henry Pietrowski says in his book To Engineer is Human – ‘Success is foreseeing failure’.
And now back to Eiffel. Eiffel is a high-level languages that closes the gap between design and code. Translating an OO design into Eiffel is easy, especially if you used Eiffel as the design language. Surprisingly, Eiffel also provides some relief for the low-level concerns. Automatic garbage collection frees the programmer from tedious and tricky memory management, assertions provide a neat way to catch errors close to the source before they cause serious damage and strict type-checking eliminates an entire class of possible runtime errors. But don’t think that with Eiffel you will break free of the low-level problems – garbage collectors can get overworked by an program that creates too many objects, and debugging at the interface of Eiffel and C can at times be a hair raising experience.
So how are we to interpret Betrand Meyer’s comment about C hackers? I think his advice is to beware of individuals who focus on a single phase of the software process and ignore all others. After all, in the same book he also said not to hire any consultants how “leave after analysis and design are complete.”
Take care and see you on the net.
Richie Bielak (September 1995)