Thursday, January 17, 2008

Good Engineering Practices

In my first two blog entries on engineering, I made some general comments about bad engineering, pseudo-innovation and the conflation of skills and qualifications. I thought it was time to say some things about what makes for good engineering practice. This is a topic I expect to return to from time to time. In this series, I will discuss one or two topics per article.

Here are a few things that I believe are common to good engineering practice. These are not necessarily the most important issues for engineering a product, but they are a good things to consider. More important, they can be applied in most (probably all) development situations.

Make solutions as general and flexible as possible

About 25 years ago, I consulted to one of Ray Kurzweil’s companies. Ray Kurzweil is a futurist and luminary in the computer industry. One of the things I remember him saying was that he was really good at pattern recognition and that all of the products he developed (at that time they included a reading machine for the blind, a music synthesizer and a speech recognition system) were all based on pattern recognition. I believe that Kurzweil’s approach to computing let him see many problems in the general terms of pattern recognition. His approaches are flexible enough that he was easily able to apply his expertise to a solution.

When I was starting my career in the software industry, I wanted to write an operating system. (A lot of us did in those days.) At some point, I took a job working on a compiler. One of the things my boss told me was that developing a system and developing a compiler were very similar. In the general sense, he was right. While there are differences, there are lots of similarities. Both parse commands. Both track information about their activities—systems control processes and hardware, compilers control code generation and optimization. Both process errors. And lots more.

If one were to look at a database system, there are also more similarities than differences to compilers and operating systems. Similarities would include things like language processing for the query language and differences would include things like more complex data storage algorithms.

If one were to look at a browser like Internet Explorer or Firefox, it’s possible to view the application in the same light. Browsers are very much like a compiler—it’s actually an interpreter. The languages being interpreted include HTML, JavaScript and CSS among others.

The point is that a good engineer develops the ability to see similarities across applications and to understand the flexibility of their solutions so they can apply those solutions to new problems and in new environments.

Generalization and flexibility are applicable in the non-engineering world too. Since my blog includes a section on cooking, I'll give an example here. About month ago, I made an oven barbecued brisket. I decided a few days ago that I could make the same recipe with salmon and teriyaki sauce. The similarities: season a hunk of meat/fish; slice it thinly; cover the meat/fish in sauce and finish cooking. The differences: the brisket cooks a few hours before getting sliced and the salmon is sliced before it cooks (because cooking makes it flake apart); the brisket gets a strongly flavored barbecue sauce and the fish gets a milder flavored sauce; the brisket cooks for 1 hour/pound and the fish cooks for about 25 minutes total. In terms of generality and flexibility, I would say that these recipes are essentially the same with a few differences.

Plan for the required efficiency of the application

How efficient does a piece of code have to be? I believe that depends on what it’s doing and how it’s being used.

Notice that I said “piece of code” and not program. That’s because some parts of a program may have to be more efficient than other parts. If a user is typing into a text field on a form, the code handling the interaction does not have to be any faster than the typist. On the other hand, if the user is editing a database record and saving it, the save operation should be fast enough that the user’s wait time is kept to a minimum.

When my father began programming in 1951, computers were very expensive relative to employees. The computers of the time were also not very powerful and had very little memory or mass storage (like disks). So it was important to write programs that were as small and fast as possible.

Over time, hardware became better and cheaper and employees became much more expensive than the computer systems they and their end users would utilize. So writing the most efficient programs became less important. There was also the notion that if a program was too slow that the next generation of hardware would solve the problem. That approach got the world past decades—with lots of notable failures when the next generation of hardware was not enough faster to make the software perform well. If you want a modern day example, think about whether your fancy new computer running Vista is really performing better than your old clunker running XP.

I think that one of the problems some programs face is that the software designers and developers don’t examine the question of efficiency closely enough…or at all…or perhaps come to the wrong conclusions. A few months ago, I spoke with a manager in a company that develops real-time applications (applications that have a time-critical aspect to them). He asked me how I would speed up a stubbornly slow piece of code. I told him that after looking at the algorithms and making sure that there weren’t unexpected code paths being followed, that in the old days we might rewrite some critical code in assembler (machine language) but that I did’t think anyone wrote in assembler any more. He laughed and said that they still did that sometimes and that they were about to do some rewriting.

Imagine a developer is building an application that examines transactions in a credit card database. There is an enormous amount of data being processed in that case. Now imagine that the code is optimized so that it is 10% faster. 10% over a million transactions is a significant of time and is very probably a worthwhile thing to do.

Imagine the user I mentioned above who is typing into the text field of a form. If the user doesn’t perceive any lack of responsiveness, then a 10% improvement in performance will not be noticed and won’t make any difference.

To summarize, I don’t believe there is a single answer to the question of how efficient an application needs to be.



I hope you find these thoughts interesting and worthwhile and that they give you something to consider as you develop engineer your solutions.

No comments: