Recent Posts

Tags

News

  • A blog about Microsoft Windows development, focused on kernel-mode driver development, the Windows DDK, WDK, and related tools.

    To elaborate on the copyright notice at the bottom: all content produced by me on this site is copyright and licensed as follows:

    <!-- Creative Commons License --> Creative Commons License
    This work is licensed under a Creative Commons License. <!-- /Creative Commons License --> <!-- <rdf:RDF xmlns="http://web.resource.org/cc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <Work rdf:about=""> <dc:type rdf:resource="http://purl.org/dc/dcmitype/Text" /> <license rdf:resource="http://creativecommons.org/licenses/by-nc/2.0/" /> </Work> <License rdf:about="http://creativecommons.org/licenses/by-nc/2.0/"> <permits rdf:resource="http://web.resource.org/cc/Reproduction" /> <permits rdf:resource="http://web.resource.org/cc/Distribution" /> <requires rdf:resource="http://web.resource.org/cc/Notice" /> <requires rdf:resource="http://web.resource.org/cc/Attribution" /> <prohibits rdf:resource="http://web.resource.org/cc/CommercialUse" /> <permits rdf:resource="http://web.resource.org/cc/DerivativeWorks" /> </License> </rdf:RDF> -->

    Although I work for Positive Networks, this work is my own and is not connected with my employer in any way.

    <!-- technorati again --> <script type="text/javascript" src="http://embed.technorati.com/embed/8xz8dihr.js"> </script>

Community

Email Notifications

Other Blogs

General

Technical Resources

About Me

Archives

Kernel Mustard

Reflections on Windows System Programming
Steve Dispensa, MVP - Windows DDK

The Twelve Programming Truths

The Twelve Programming Truths

Steve Dispensa
CTO, Positive Networks
MVP, Microsoft Windows DDK

August 1, 2004

The Twelve Networking Truths (also known as the Twelve Fundamental Truths) were first collected and presented in RFC 1925. These were meant to apply to the practices of network architecture and engineering, and they are fantastic in that context. As my career transitioned from network architecture to driver development, I kept coming back to the Twelve Truths every so often, and each time I noticed their remarkable value even as adapted to software development.

Like the author of RFC 1925, I have by no means discovered or invented these truths, but after having trained a fair number of new software developers, it has become oblivious that these principles, as applied to computer programming, are generally useful and valuable.

With that in mind, I will discuss each of the Twelve Truths as applied to the craft of software engineering. It is my hope that you find as much value in them as I have. I hope that I haven't destroyed their impact with my attempt to adapt them to another discipline, although I'm afraid something is always lost in the translation.

Two acknowledgments must be made: first, to Ross Callon, who first assembled the Truths into RFC 1925 on April 1, 1996, and to Dr. Deep Medhi, head of the computer networking department at the University of Missouri - Kansas City, who first introduced me to the RFC.


   (1)  It Has To Work.

You'd think this Truth would go without saying, but in fact, sometimes it seems like the last thing on the minds of a room full of software engineers during design of a program. Much (most?) software development is done for money. Clients, employers, and customers all expect the code to work. They don't usually even care how it works (sometimes to the chagrin of the developers!), but It Has To Work.

There are a couple of subcases of It Has To Work. The first case is understanding the definition of "work". This word, like "done", gets thousands of connotations and definitions in various contexts, and there is nobody like a programmer to confuse these definitions. Your client may not have any use for your word processor if the spell checker gets exponentially slower with every word typed. Yes, the check may complete eventually, but it doesn't "work" for the customer.

Another aspect of "working" is completeness. I'm not the first to quote this concept, but my empirical observations have led me to conclude that it takes 50% of the time to do the first 97% of the work, and 50% of the time to do the last 3%. Around my office, we have the "Last 3% Rule" when it comes to project planning. It may be that the encryption algorithm works perfectly, but until the user interface "works" - all the buttons do the right things, the appearance is polished and professional-looking, it doesn't suffer from state problems, etc. - it's not done.

At my office, we don't declare something Working until it has passed our Quality Assurance testing. The QA team is a separate group of folks who are responsible for thoroughly verifying that the software's functionality. It doesn't Work until they say it does.

There's a reason this is Truth #1. Nothing else matters if it doesn't Work, in every sense of the word. Above and beyond everything else, Working software yields happy customers.

   (2)  No matter how hard you push and no matter what the priority,
        you can't increase the speed of light.

        (2a) (corollary). No matter how hard you try, you can't make a
             baby in much less than 9 months. Trying to speed this up
             *might* make it slower, but it won't make it happen any
             quicker.

The original intent of this Truth is to illustrate that there are laws (some from Physics, some from The Reality Of Life) that cannot be broken. In networking, system throughput is eventually totally bound by the speed of light, and there's nothing that even the most talented engineer can do about it, other than work around these inherent limitations.

The obvious application to software engineering is that software development takes time. It usually takes more time than the customer would like, and often takes more time than the developer would like (in particular, the Last 3% is usually pretty boring work). However, failing to understand this fact can only lead to one of three outcomes:

  • The deadline arrives, and your software doesn't Work
  • Your software Works, but you've blown the deadline
  • (in some cases) The deadline arrives and the software Works, but you do not, due to chronic lack of sleep and food.

None of these are good outcomes, and all of them, in a bad enough case, can put a serious and permanent dent in your ability to earn money as a programmer. Also, that last case can only happen if the planning isn't off by more than the amount that you can make up by your superhuman effort.

Nobody is a perfect predictor of the time it takes to develop software, but you owe it to yourself as a programmer to give yourself a fighting chance of hitting your deadlines. This does *not* mean that you should plan on having to work tons of extra hours just to hit an un-hittable date. Rather, it means that you must work with customers, project managers, and anyone else who is interested, to make sure that expectations are set correctly up-front. There is no way out of this task, and all previous known attempts to get around expectation management have failed miserably.

   (3)  With sufficient thrust, pigs fly just fine. However, this is
        not necessarily a good idea. It is hard to be sure where they
        are going to land, and it could be dangerous sitting under them
        as they fly overhead.

I've heard a lot of bad ideas in my career. These ideas are usually the result of an overzealous engineer that doesn't quite understand the problem at hand, and doesn't really want to figure out the Right Way To Do It. In almost all cases, there is a right way and a large number of wrong ways to accomplish a given task. Wrong ways include everything from hooking Windows system calls in production code to hacking in a new feature in the 11th hour with insufficient design documentation.

Sometimes these tactics might work, for a while, but you will always suffer in the end.

   (4)  Some things in life can never be fully appreciated nor
        understood unless experienced firsthand. Some things in
        networking can never be fully understood by someone who neither
        builds commercial networking equipment nor runs an operational
        network.

Find an expert in any field of software engineering. There is virtually a 100% chance that he or she has written production code in that field. Code that has been sold to someone, or on which someone else depends in one way or another. If this seems obvious to you, consider the number of Monday morning quarterbacks. Software engineering has them too. Sometimes they are students, sometimes they are managers, sometimes customers, sometimes even professors. Whoever they are, if they haven't written production code they won't understand the practice of software engineering. If you are one of these people, recognize this fact and have some faith in your programmers.

There is a tangible difference between writing code for a class project, or even "just for fun", and writing production code. The best way by far to become a better programmer is to write more production code. Until you've gotten tech support calls, bug reports, feature requests, and (perhaps inevitably) patched vulnerabilities in your code, you're just practicing.

   (5)  It is always possible to aglutenate [sic] multiple separate problems
        into a single complex interdependent solution. In most cases
        this is a bad idea.

Good design is hard to find. It is a balance between generalization and perfection in the expression of a set of abstract ideas on the one hand, and being down-to-earth enough so as to avoid requiring the coding of lots of exception cases on the other. Good designs are possible with any design paradigm, and some are better suited to certain problems than others. Whatever your design paradigm, don't over-complicate the situation. As my grandfather used to say, "Don't make a Rembrandt!"

   (6)  It is easier to move a problem around (for example, by moving
        the problem to a different part of the overall network
        architecture) than it is to solve it.

        (6a) (corollary). It is always possible to add another level of
             indirection.

Rule 6a is one of the wisest things ever said. Many of the design problems I alluded to in Rule 5 above are really just this in disguise. Just remember - you have to pay the price for bad design eventually. If you find yourself encountering Rule 6, it's probably a pretty good sign that you need a re-design.

For added entertainment, notice that Rule 6a (about indirection) is itself a sub-rule. :-)

   (7)  It is always something

        (7a) (corollary). Good, Fast, Cheap: Pick any two (you can't
            have all three).

These Truths call to mind Rule #2 - there is no substitute for planning. Furthermore, there are problems that no amount of planning can solve. This is not really a re-formulation of Murphy's Law (although you should keep that in mind as well!), but merely a description of the peculiar art of software engineering. Things happen. Designs change. APIs don't work as documented. Oddball platforms don't support otherwise-universal interfaces. The list goes on, and by definition (and by Rule 7), you can't predict them. Better to keep this fact of life in mind during the planning stage of the project, and you'll have happier customers, spouses, partners, managers, etc.

Rule 7a needs no elaboration, other than to say that I have found it to be true over and over again. As a general principle, you cannot optimize Rule 7a away.

   (8)  It is more complicated than you think.

Much like Rule 7, this rule reminds us that human nature is to assume the straight-ahead case, minimizing the possibility that things could Go Wrong. People are just wired that way; there's no way around it. As a software engineer, you must be vigilant against the feeling that you completely understand your problem.

It was often said about the Theory of Relativity that only a couple of people in the entire world understood it during Einstein's time, despite the belief on the part of many that they had their brains wrapped around it. The bummer about this Truth is that it is usually not empirically obvious until after the painful process of learning has occurred.

One specific point about Rule 8 relates to the area of software security. Here, it takes more than best practices to write secure code. You must always maintain an attitude of extreme caution. It's extremely difficult to know if your code is secure as written. Lots of code that is secure in one environment turns out to have big holes in it in other environments. While there is no guarantee that this will prevent vulnerabilities, defensive coding practices and a general attitude of suspicion about everything you write will go a long way. I know I'm suspicious of everything I write. :-)

   (9)  For all resources, whatever it is, you need more.

       (9a) (corollary) Every networking problem always takes longer to
            solve than it seems like it should.

While Rule #9 has its obvious applications to software engineering, there is one particular uniqueness about software: often it is impossible to just "add resources" to a programming project. One reason is that the ramp-up time is often very long, even with an experienced coder. Another is that the complexity of adding a developer to a project tends to be O(n) with n = the number of programmers on the project. Furthermore, that implies that the "total complexity" of the project is O(n^2) [1]. Both of these numbers can be improved upon in practical cases with good design and organization of the project, but there's no doubt that small teams have an easier time being highly efficient.

   (10) One size never fits all.

This is another admonition against over-ambitious design. The natural tendency of the human (programmer's) brain is to generalize. Triumphs of generalization are everywhere around us, particularly in the realm of physical science. Other things, such as advances in hardware engineering, would be impossible without the art of generalization.

But, like anything, too much of a good thing ceases to be a good thing. One database developer I know is fond of saying that "the design cannot be so abstract that the database doesn't hold anything!" Often, inexperienced software architects over-generalize a design in their eagerness to arrive at the "perfect" solution. However, this is seldom possible. Software engineering is a real-world activity, where imperfections in design are a fact of life.

Sometimes it's okay to have one-off solutions. A skilled software architect develops a sense for how to minimize exception cases while maximizing the elegance of a design.

   (11) Every old idea will be proposed again with a different name and
        a different presentation, regardless of whether it works.

        (11a) (corollary). See rule 6a.

As the rule itself states, this is just another way to say Rule 6a. Be on guard against getting drug down the same old path again and again. It is a waste of time and money.

   (12) In protocol design, perfection has been reached not when there
        is nothing left to add, but when there is nothing left to take
        away.

Possibly the most valuable axiom ever contributed to the world's body of engineering knowledge. The same goes for software design - there is no need to overcomplicate software, and there is much need to keep it simple. Good software must be maintainable, and the less of it there is, the easier the task of maintenance.

Again, this rule runs against human nature - we like to build things - elaborate things - but nothing could be worse for software engineering. More than anywhere, software engineering requires the KISS principle.


Summary

I have long relied on the Twelve Truths, first in network engineering, and now in software engineering. There are few documents with as high of a concentration of practical wisdom as this one, and new engineers would do well to read and understand RFC 1925.


[1] "Proof" of the complexities of adding developers to a project:

Every coder has a certain amount of undocumented "state" in his or her head. This state is often a requirement for writing successful code, or at least is required to do it efficiently. It is this state that makes a group software development project complex.

Call the "amount" of this state X. If all n developers have X state in their heads, there is nX amount of undocumented state in the project. Each additional developer brings X more state to the project.

Each developer will eventually have to know the state in the heads of all of the other developers on the project (n-1)(X), requiring a total amount of "learning" of state of n(n-1)(X).

Therefore, adding a developer to the project (who starts with no state, but develops it over time) requires that developer to acquire (n-1)(X) amount of state, or O(n). The total amount of complexity in the project then rises to n(n-1)(X), as that developer adds his state to the project, which is O(n^2).

Comments

TrackBack said:

# August 22, 2004 10:47 PM
Leave a Comment

(required) 

(required) 

(optional)

(required)