I used to think there was a right way and a wrong way to write a program. I found myself on a quest for the elusive right way to implement whatever program I was attempting to write. I wrote and rewrote the code. I read articles and books on techniques and disciplines. Nothing seemed to make programming any easier.
Slowly I began to suspect that maybe there was a continuum from good to bad and most programs fell somewhere in the middle. I hadn’t given up on the idea that there was an absolutely right way to do things and an absolutely wrong way. I just had plenty of evidence at hand that there were lots of ways that were neither absolutely right or absolutely wrong.
I don’t know why it took me so long but I have finally figured out that there are no absolutes. There are only criteria against which any given solution can be judged. The key fact that I had been missing was that there is almost never a single criteria by which a program can be judged.
I have become a dyed in the wool pragmatist. If a program achieves its intended purpose, it is good. If you discover there is another attribute that you would like it to exhibit, you have added a criteria. Now the program may fall short of adequately achieving its purpose. This will require some thought and perhaps a rewrite of the original program.
This activity of rewriting became so common that it has been given a name. It is called refactoring. Refactoring is done by first implementing a test suite that exercises the features that are implemented in the program as it is. It is also good to check the program in to a source code management system such as Git or Mercurial or even Subversion. There are plenty of others but these three are popular right now. With your test suite and backup copy of your original program you can now confidently start refactoring your program.
There are a number of ways that a program can be refactored. Each one solves a particular problem with the code as it is at present. For instance, you may have output to a particular type of output device hard coded in your program. In order to continue to support your current favorite device while setting the application up for supporting different kinds of output devices you might refactor your program so that it calls a print method on an output abstraction class. Initially you would concentrate on implementing an output abstraction class that supports outputting to you current favorite device. You aren’t going to want to quit using it.
When you have written that code and tested it against your existing test suite to ensure that you haven’t broken something in the process of changing the way it works, it is now time to implement the code to write to the new device. You will want to make sure that the current test suite still passes and that the code that supports the new device works properly. After that you need to write tests specific to your new code and ensure that it passes those new tests. When they work, check the new code and the new tests into your source code management system. You’ve implemented your new feature and you are ready to start on the next one.
Oh and don’t worry. Someone will want the program to do something more or differently before you know it. You have job security for as long as you want it.
Sweet dreams, don’t forget to tell the ones you love that you love them, and most important of all, be kind.