I’ve been struggling with working to a schedule lately. In my experience, schedules have been arbitrary constraints imposed by people that don’t understand the work to be done and have been tasked with reporting on progress and projecting when the work will be finished. This is due to the fact that in some domains that is an entirely reasonable way to work. Software is different for any number of reasons. That is the topic that I will explore in this post.
First and foremost, programming is a creative task. Even when you have a very specific problem to solve, there are any number of ways to solve it. Furthermore, a large number of those ways turn out to be good solutions. The difficulties start to mount up when you have a bunch of different people working on the same project. Each of the pieces contributed by individual programmers have to interface with some number of other pieces to solve the big picture.
There are a number of techniques that are used to address the issues arising when integrating software components designed and written by different developers. One popular method is to design the functional interfaces to the components first. Then, you write each one to do the function according to the description of their function. Then, it remains to put the pieces together into an implementation of the use-cases of the application.
Software is hard to define in a linear fashion. When you set out to write an application you have a vague idea of what you want it to do but almost no idea of how to get the application to do it. Typically this is addressed by defining one feature and writing a test for the feature. Initially the test will fail. Then you implement the feature. At this point the test will pass. Then, you define a second feature and write a test for it and then an implementation. At each step in the process, you run the complete test suite to make sure that a new feature hasn’t broken one of the already implemented features. You have to constantly refer back to the initial vision for the application to make sure that you aren’t going too far afield.
Sometimes, while practicing this incremental, iterative development approach, you will discover that you have painted yourself into a figurative corner. You know what the software does now and how it does it. You know what you want it to do in the next iteration, but you can’t see a direct way to get to where your going from where you are.
This leads to a practice called refactoring. In order to successfully refactor code you must first make sure you have a copy of the code in its present state in some sort of source code management system (SVN, and git come immediately to mind). Second, you must have a rigorous test suite for the code as it exists. Finally, you transform the existing code, misfeature by misfeature into the new code. For each feature that you transform you should run the test suite to make sure that you didn’t break anything. Once you have finished refactoring the code, it should be straightforward to pick up the rhythm of writing a failing test for the next feature and then implementing it such that it passes the test.
Refactoring is still more of an art than a science. There is a lot written about refactoring in various different computer languages online. Google will turn up plenty of references if you search for “refactoring” and the language of your choice.
Sweet dreams, don’t forget to tell the ones you love that you love them, and most important of all, be kind.