Code is expensive

So, the other day, I heard the phrase that code is expensive. Indeed, with the current state of our software that I have been tasked with maintaining and writing, I would definitely call it expensive. All the code we've written so far, and much to my chagrin, is pretty much procedural code. It's difficult to maintain, the code base keeps growing ever larger and any changes are not at all guaranteed to work if any additional features are added. This makes it increasingly (probably exponentially) more difficult to maintain and/or add more features. So, yes, I would definitely say the code base we have today is a liability.
This past week, the powers that be arranged to have Uncle Bob come in and give a 3-day seminar on Clean Code. If you haven't read Clean Code, and you're a programmer, do yourself a favor and add it to your reading list. (You DO have a reading list, right??) While Uncle Bob has some contentious views on how code should be approached, he backs it up with some arguments that are difficult to disagree with. One of his stronger arguments in my opinion is the one for using the methodology of Test Driven Development, or TDD. The main concept is that you write all your tests before you write your first line of "real" code. What you end up with at first is a unit test that doesn't even compile. Then you switch over and write just enough "real" code to make that unit test pass. Then you go on to write your next unit test. From the outside, this approach seems completely insane and a total waste of time. Once you give yourself the opportunity to put it to work, however, I suspect you will not code any other way. Now, to be 100% by-the-book, TDD can be overly simplistic, so there are some concessions to make to find a happy medium. However, if you find yourself using the debugging tools at all, you've probably strayed a bit too far from the TDD path.
Well, this test driven development thing got me to thinking about the phrase earlier that code was a liability. To take the implied corollary a bit farther, then, the balance sheet of code would be heavily liability laden. We would need more assets to balance the balance sheet. That's precisely what the unit tests that you wrote BEFORE you wrote the first line of "liability" code is for. The unit tests are very small. They run very quickly. They can be run on a whim. What better time to run a suite of tests iteratively than when you're adding a new feature to an existing system? Or finding a bug that existing unit tests didn't find? If you have found that the existing unit tests were not sufficient to maintain that balance, you need to add additional unit tests to your suite. This way, the next person writing additional features (which very well could be you!) will feel the same zen unit tests impart while refactoring or adding additional features.
Another great aspect of unit tests is the self-documentation aspect. If you want to know what a particular piece of software does, look at the unit tests! Now, to make them useful, you will need to name your methods appropriately. A method name of "TestThis()" is meaningless, but a method name of "AnEmptyQueueHasASizeOfZero()" is pretty clear what it's checking for.
In the example we did during the 3-day seminar, we created a simple Queue class using the TDD approach. We had a constructor, and public Enqueue, Dequeue, IsEmpty and Size methods/properties, or five public members. We wrote 9 unit tests to verify the proper functionality of the Queue class. I have no idea if that's the right number of tests or not, but we felt pretty comfortable the class was tested sufficiently. But the number of tests really isn't quite as important as code coverage. The fact that they could run in less than 1 second made the point moot. I can't wait to start using this approach in the coming weeks/months/years. I think it will be taking my coding abilities to a whole new level.

Comments

Popular posts from this blog

Using Bogus to create Hierarchical Data Structures

Adventures in Getting a Site Hosted

Design Patterns: An Introduction