Fitness for a Particular Purpose: a Design Methodology

By Terence J. Grant, February 13, 2005

Most licensed software you download states one thing right up front, something to the effect of “no guarantee of fitness for a particular purpose.” What this means in a nutshell is that although the software might, by all appearances designed to do one particular thing (and do it well), when it comes right down to it, if it fails to do that… Well, then that's just too bad. It’s a standard disclaimer, which is okay, because as we all know, “the best laid plans of mice and men often go awry.”

But still, “fitness for a particular purpose” is an interesting concept. What if you could design your software to be really “fit” for some “purpose,” or even better, fit for multiple purposes or even generic purposes? Given time, surely you would be producing some of the sturdiest, most robust and most useful software there is.

So, all that leaves is where to begin. Well, consider the application or library (further known as “the program”) you would like to write as a problem to solve. You can then simply take the same approach as the seven-step system I use to solve Calculus problems. Now let's take the procedure and tailor it to our needs a little better. Here is the seven-step process I suggest using for successfully designing software:

  1. State the program's purpose, completely.
  2. Identify libraries that you could use ahead of time.
  3. Break the program into pieces.
  4. For each piece, decide on a plan of attack, using principles and their limits.
  5. State the plan, referencing some principle, and the limits involved.
  6. Apply the principle, and if the principle proceeds to solve the piece, then decide on a plan of attack for the next piece.
  7. Make a general statement about the solution to the problem.

State the program's purpose, completely. This is the overall “purpose” statement for my “fitness for a particular purpose” methodology. At this point you should know what you want the program to do, without being overly specific. However, you do have to be specific enough to define the problem– that is, you have to know what the program needs to do, or else you'll have a difficult time trying to write it.

So, for instance, say we want to write a peer to peer (also called P2P) chat client. We could describe the program as, “A P2P chat client that supports multiple users, custom user pictures, and file transfers.” Although it helps to have ideas and inspiration early on, even something this simple is enough to start with for the first step.

Identify libraries that you could use ahead of time. Without a lot of thinking, try to imagine which libraries you will probably end up using– even if you've never used them before or seen their interfaces. This is the exact parallel of the “identities” step in my method of solving Calculus problems. In this case we're using the identities to rid ourselves of all of the extra work involved if we had broken the program into pieces too soon.

Keeping with our P2P chat client theme, if we're on Windows, we may want to use MFC for the user interface, WinSock 2 for the networking, DevIL for image loading support, and gzip for compressing files before transferring them. If its OSX, we might use Carbon for user interface, BSD sockets for networking and so on. Again I emphasize that not a lot of thought needs to go into the process early on.

Break the program into pieces. Only at this point should you consider splitting the program into its separate components. You still might want to gloss over the details, similar to step one and two, unless you feel that the piece can only be broken down into very simple steps. If the details at this point still tend to be too generic, you'll want to treat it as its own sub-program and start from step one.

Now we're at the point where we say our P2P program will need to be able to connect to another client, accept incoming connections, be able to transfer chat, picture, and file data.

For each piece, decide on a plan of attack, using principles and their limits. This is simply the decision stage. What I like to do is web search for interviews, papers, or anything that gives me further ideas or inspiration on how to go about the planning process from related programs. The important part is to try to make sure that you cover all of your bases before you proceed.

In the example, we'll only take on the user interface. We want to base the interface off of something we know– IRC for instance. IRC has a user list, chat area, and chat input, which we'll say is sufficient for our needs, though in addition we also want to display a custom picture per user, which for convenience, we'll limit to 32×32 pixels. Decide on the principle you'll be sticking to and its limits. Limits are the extreme cases in which the referenced principle may become invalid. The principle we'll use is that we know that MFC supports Text lists and Icon lists, as well as sizing either of those fields to any arbitrary width/height. Keep in mind this is just one principle; in practice there will be many principles for each piece, some interdependent. A good example of a principle's limits would be within the scope of a P2P application's file transfers. When reading and writing files, often you are limited to files that are 4 gigabytes in size or less. This is because most file I/O functions use signed or unsigned ints. The size of the largest unsigned int is 32-bits, which has a maximum unsigned value of 4294967295, which in bytes is exactly 4G. Anything bigger probably couldn't be transferred– but of course you know that someone will try. Considering this you may want to include as part of your plan of attack for file transfers that any file above 4G will, instead of trying to transfer such a file, pop up an error stating that it is too big to be transferred to begin with.

State the plan, referencing some principle, and the limits involved. It’s really very simple, but this is the part takes the most time as you're actually getting down to the solving stage. State it in words, design it on a whiteboard, then write it on paper. Always question yourself and analyze what principles you're using, making sure they're rock solid before you leave this piece. At this point we will want to be completely forthcoming with every piece of information and each step in solving our problem. The most important thing is to be as detailed as possible.

Since our P2P chat client is just an example, we won't actually go this far. However, at this point we'd evaluate, for instance, MFC's ability to do all that we hope it does for the GUI step; even referencing function names if necessary.

Apply the principle, and if the principle proceeds to solve the piece, then decide a plan of attack on the next piece. This is the point in time where we'll actually code and test the solution to the piece at hand. Once we are done, the important thing to remember is that we need to check that we have kept to, and not violated, any or all of the principles that make up our plan. The reason this is so important is that often you'll find some principles are either dependant or co-dependant on other principles, and since your program will only be as strong as the principle that is implemented the weakest, it is important to implement all principles as strong as possible.

Occasionally, the first time you write it, you're golden. Other times, you're going to forget things; don't worry– it happens. Don't give up. As with everything, practice allows you to become better and better until it becomes second nature.

Make a general statement about the solution to the problem. Write out the general solution, again, in words. Does it measure up to the original program's purpose? Are all of the principles involved rock solid? If so, then you're ready to test the entire program in the real world. At this point you should have no reason to doubt your success. Count on the principles to fulfill the purpose. It is something you do in math and it translates into programming (or anything else) very well.

In our P2P client, when we're finished writing and testing all the modules individually and know that they work, we'll just go ahead and assume that the entire program works, though this doesn't mean you wouldn't want to “beta test” it. If and when things break, you'll be able to identify which plans, principles, and their limits need improvement. However, over time you will have enough confidence that something will just work, because of the rock-solid principles involved. It truly is a thing of beauty.

You now have a method of implementing software that is “Fit for a Particular Purpose”– at least to yourself. I wouldn't suggest omitting the “no guarantee of fitness” in a license, but that's up to you. Keep in mind that with practice and time, you will get better. By using this seven-step process your programs will continue to become more stable and more robust. And, as an added bonus, you will find that you can produce more programs quicker with this method. If you find yourself getting frustrated at any point, step back and ask yourself some important questions, like, “did I spend enough time on each step?” Or, “did I break things up into small enough pieces?”

When all else fails, take some time off, forget about it, do something else and come back to it later. Inspiration usually doesn’t hit you all at once– but when it does, you'll want to be ready to channel it effectively.

Good luck and happy coding.