I have written a short article for the next QBExpress.
I have included it in this email. If you would like it
in another format, let me know and I can send it to
you in the desired format.

--begin--
Managing Complexity
By Richard D. Clark

One of the continuing problems of software design is
managing complexity in large programs. The problem is
easily defined: as a program grows, the number of
interactions between code objects within a program
grows at an ever-increasing rate, creating a
maintenance nightmare. While defining the problem is
easy, solving the problem has proven to be extremely
difficult. There have been numerous white papers
devoted to the subject, new programming languages
created, C++ for example, notations developed and
guidelines created. All have a particular take on the
problem, and all address certain aspects of the
problem, but as of yet, there is no all-inclusive
solution to the problem, and one may not exist.

I dont pretend to have the answer to this problem,
but I have discovered techniques that help to mitigate
the problem and ease the burden of tracking down bugs
and minimizing the effect of code changes. When I made
my living as a programmer, I had to write large,
enterprise level programs, and I learned that taking
the time up-front to organize code in an orderly
fashion helped in managing the complexity that is
bound to creep into any large-scale program. Here are
some of the lessons I learned, most the hard way.

Define the Problem

Before you start to code, think about what problem you
are trying to solve. All computer programs should
solve one or more problems. For example, an electronic
address book may solve the problem of storing names
and addresses that can be quickly retrieved. Strange
as it sounds, even games should be designed to solve a
problem. For example, in Unreal Tournament 2004, you
have a game type called Capture the Flag. The problem
definition is actually simple: move to the opposing
team base, grab their flag and take it back to your
base. In a chess game, the problem to be solved is to
capture the enemy King. Once you clearly define the
problem, you can then create a set of action steps to
solve the problem.

Have a Plan

The problem to be solved is the goal of your program.
In the Capture the Flag (CTF) problem, once you are
able to grab a flag and take it back to your base, you
have achieved the goal of the program. The task is to
define how that goal is achieved. One thing I like to
do is to work backwards from the problem, breaking
down the program into ever-smaller components and then
tackle those components individually.

Lets look at the CTF problem. There are basically 3
pairs of components: two different flags, two
different bases and two different teams. The flags can
be broken down into red flag and blue flag. The bases
can be broken down into red base and blue base. The
teams can be broken down into red team and blue team.
Since CTF is a symmetrical game, we really only need
to define one side of the equation, a flag, a base and
a team, and that definition could be used for both the
red and blue sides of the game.

Lets define a flag. A flag is an object that resides
in a base and is manipulated by a team member. Using
this definition we can write down some preliminary
action steps that need to be addressed when coding a
flag. 1) A flag is an object, so we need to have a
model of the flag. 2) A flag resides in a base, so we
need some place to put the flag. 3) A flag can be
manipulated so a flag must have some properties. This
is a good start, but it is too general, so we need to
refine the action steps.
Looking at 1 above, a flag is a model so we will need
to have a base image of the flag. Since there are two
flags, red and blue, we will need to have two texture
sets for the flag. So for action step 1, we will need
to create an untextured model of the flag and create
two texture sets for the flag. For the red flag, we
will paint the red texture set on the model, for the
blue flag, we will paint the blue texture set.

At this point we have enough information to implement
in code, a flag object. We have no place to put it and
cant interact with it, but we can create the object.
Notice the progression of definitions: we defined the
problem in general, then restated the problem in
specific problem components, and then redefined those
components into even smaller, solvable problems. Going
through the same process with each aspect of the CTF
game will yield a list of small action items that can
be directly translated into code.

Smart Translation

The next step is to create the code, and many times
this is where the programmer fails. It isnt simply a
matter of jumping in and coding the action steps, the
coding has to be done intelligently. Look at the
action steps and find the commonality. For example,
displaying a flag and displaying a team member,
involve similar techniques. They both have a model and
texture set. It doesnt make sense to have two
routines, one to load a flag and one to load a team
member. It would better to have one routine that does
both.

The rule is, if you do have to do something twice,
write the code once. Look through all the action steps
and group the common functionality into general
routines that can be called from different action
steps. In other words, you write one routine to load a
model, regardless if the model is a flag, player
character or weapon. By using one routine instead of
three, you have reduced the complexity of the program.
If there is a bug in the model loading code, then you
only have to fix one routine, not three. And, more
importantly, if you need to make a change, you only
have to change one routine, automatically reducing the
risk of introducing a bug into the system. One routine
to fix, one place to look for bugs.

Smart Organization

The next step is to organize the code in an
intelligent manner. Dont put everything in one huge
source file; break up the source file into logical
groups. Routines that manage the models should be
placed into a single file. Routines that manage
textures should be placed into another file.
Non-specific routines, a random number generator, for
example, should be in yet another file. The same
principle that we used to define a problem should also
be used to organize the source code. Group the code
into small, logical groups so that if something
breaks, you know where to look and if something needs
to be changed, you can do it in one place.

Document the Code

For small programs, documentation usually isnt
necessary. For large programs, it is vital. Lets say
your program works for six months, and suddenly one
user does something that no one has ever done before
and your program quits working. When you go back to
the code after not looking at it for six months, are
you sure that you can remember what you were really
doing? Some people can, I for one cannot. Taking a few
moments to write a note about the purpose of a
routine, the input and output expected and the
expected results can save hours when it comes time to
debug that long-forgotten routine.
As I said, this isnt a grand solution to the problem
of program complexity. However, I found that following
these steps greatly reduces the time and effort needed
to produce and maintain quality software, especially
if that software project grows beyond a simple
one-page program. The important point is to have a
method, and then stick to that method. We will never
solve the problem of complexity, but we can at least
get a handle on it and make it somewhat more
manageable.
--end--


Rick Clark