I have been designing and programming computer systems for over a dozen
years now. At times I've loved it, and at times I've hated it.
Oh yes, I've done my share of grumbling about poor management, inadequate
requirements, indecisive customers, incorrect documentation and so on. But
even in the darkest depths, with blame flying thick and fast and the corporate
axe laying waste all about, I could always look back to the pure joy I discovered
when I first made a dumb machine do what I wanted.
In recent years I have begun to wonder; what made those early years so
exhilarating? Why does corporate programming often seem to be a hothouse for
frustration and stress? And more importantly, how can I make it more fun for me
and my colleagues?
So I examined the projects I've worked on which had a good (or even
great) team atmosphere, the ones that worked, the ones that came in
on time and under budget, and compared them with the others. Here is the
result so far; a few simple rules for stress-free programming!
Build in small pieces
Every one's heard of divide and conquer. Well in programming it's
vital. That 200 line function or that 80 method object may seem
straight-forward when you write it, but 6 months or a year down the line
when several conflicting customer requirements have been patched in by
different people it turns into a horrible lump. By then it's too late.
Everyone agrees it should be simplified, but nobody really understands
it or the way it's tangled into the system - the benefit of improvement is
outweighed by the risk.
This conflict of common desire against practicality is the first source of
programming stress. It's the one that causes team members to curse
management for their stubbornness and dream of pastures new. It's also the
one that causes management to distrust programmers. One or two overly-optimistic
attempts at improvement can crash and burn an entire team's reputation.
Test early. Test everything. Test often. Test automatically.
This may seem like a whole bunch of rules, but it all boils down to one thing:
testing is not an after-the-fact process to find faults with whole systems,
it's a vital tool for getting them right in the first place. As the second step
to removing my programming stress, I make sure that every function, every method,
every object, every subroutine has an associated automatic test harness which
tests normal, limit and error cases for every parameter, and makes sure it does
the right thing. This appears like overkill, and it's one of the hardest of
these rules to "sell" to team members. In my experience, though, this technique
gives the biggest single reduction in project duration (and therefore in cost)
of any tool or process I've ever encountered. Think of it like building a house;
Before you build the walls, you want to make pretty sure the foundations are
stable. If you find a fault with the footings after the walls are raised it's
going to cost a lot to get it fixed. If the roof's gone on and the furniture is
in place, that same fault might cost more than the house to put right.
A corollary of this is: Design for test. Provide test hooks, diagnostic
switches and so on wherever you can without compromising other constraints (such
as performance or program size). In a system designed for test, where a complete
set of automatic test harnesses have been built at the same time as the system,
it's much easier to consider rewriting even large sections. Just rerun the
automatic test pack after each of a series of small changes and you can be sure
that the final system is as robust and complete as the original.
This technique helps to remove the second source of programming stress, which
comes from not trusting the rest of the system. Locating a fault is easy if the
whole preceding system has already been completely tested. The problem
must be in the new bit. If you stick to my first rule, the new bit will
be small too - which makes it doubly easy. If you haven't tested all the bits
as you built them, it's like trying to find a rotten apple in a sack - by poking
the sack with a stick!
Do the simple bits first, you may never need the complex bits.
I've seen many projects snowball into chaos, and drive everyone working
on them to distraction, just from losing sight of the priorities. Some people
seem to live for "requirements capture" and "customer relations", but its
a fact of life that no project is ever fully specified before
design and coding starts. If you demand this you really will get stressed out!
So go with it, accept that things are going to change and make the best of it.
My survival technique for this is to always code the simple cases first. Not
only is it more likely that the requirements and specification process has got
the simple cases right (more people at more meetings can understand them), but
it's less hassle if you have to throw them and start again.
This also applies to adding complexity in the name of "future-proofing" or
"performance". Only do it once you know it's really needed. Many systems
I've encountered have actually become less flexible and even slower because
of ill conceived initial complexity. If you don't need it, don't code it.
The stress you can get if you don't use this sort of approach is the sort
which leads to despair at indecisive customers and a feeling of loss of control
of the project. As well as frustration and arguments as you have to change,
again, the code you've laboured over. This can never fully be eliminated,
but by doing the simple cases first you can at least feel you're making progress.
Communicate and share the workload
In the real world, the vast majority of programming is done in teams, but
all too often the teams are divided in the wrong way. Each team member becomes
an "expert" on some area of the system, and all changes, faults or support
requests for that area naturally go to them. If you have any influence on team
structure, it's almost always better to divide the team another way. The ideal
end result is that everyone in the team knows enough about the system to code
reasonable changes, find basic faults and handle normal support requests for
any part of the system. They should also know who to ask or where to look for
more detailed answers, but consider it an excercise in "learning from the master"
rather than "delegation" or "division of labour".
This approach naturally implies good communication between team members.
Working close together, frequent social and business meetings, a common
"house style" for code, comments and documentation all help. My top
recommendation, though, is to develop a "review culture". If everyone in the
team helps to review everyone else's work (and takes it seriously...) it
not only helps to catch potential problems and deviations from the house
style, but also helps everyone get up to speed on all parts of the project.
The type of stress this helps to prevent is well known, I'm sure. If
you're the only "expert" on one part of the system, then the pressure is on
you to solve all the problems. If a particular change or fault is mainly
in your component then you are the critical path for the work while others
seem to sit about doing nothing. On the other hand, if a problem or
question arises concerning someone else's area of expertise when they are
not available, you are put under pressure again.
Work to human timescales, and seek "closure".
This is a tip which doesn't just apply to programming stress, but it's just
as useful here as it is in any other field. It could also be phrased as
"don't bite off more than you can chew". If you work regular office hours, try
to divide your work into chunks you can complete in a day. Try to build and
test some sort of subsystem by the end of a week, and so on. It seems obvious,
but it's amazing how many people don't even try to do this.
This has double benefits in reducing stress. First it lets you free your mind
from clutching onto part-finished work. I've never been able to fully relax if
I have remember where I've got up to for tomorrow; well, not and do any useful
work the next day, anyway. The second benefit is the happy "kick" we all get
from "closure". I get a physical sense of release when I've finished and
tidied a piece of work; a feeling of "now I could do anything".
So there are my rules. They are not a complete recipe, you must still allow
sensible timescales; ensure you have the appropriate skills, tools and resources;
and develop an good and clear relationship with your customer(s). I'm not saying
that any project which implements these rules is guaranteed to succeed, but they can
certainly help. And they can make doing it a lot more fun.
If you have found any of this interesting or useful, please
let me know about it
A footnote to this. While cruising the web recently, I came across the
concept of Extreme Programming. While coming from a different direction
to the above, it has an amazing amount of overlap. For more information
about Extreme Programming see Kent Beck, Ron Jeffries
or the WikiWikiWeb.