Imagine a piece of software that works correctly and yet doesn't "feel" well written. How could you describe the code's quality in concrete terms? How do you convert the vague notion—"this is poor code"—into a plan for fixing it? Let us introduce a catalog of software design anti-patterns called code smells and show how to use them both to describe software quality in concrete terms and to plan remedial actions for improving code.
What Is Code Quality?
In recent years, agile methods in general and test-driven development (TDD) in particular have become significantly more popular. The microprocess of TDD tells us to work to the rhythm of red-green-refactor. Specifically, we write a test and see it fail; then, we write code in the most straightforward way possible to make it pass; then, we refactor to clean up any mess we made. But, in that third step, just what should we refactor and why? And if we need to refactor a pile of code that wasn't developed in a test-driven manner, how do we get a handle on where to begin?
Refactoring is defined as "improving software without changing what it does." The intent is to replace bad code with something better. The next questions must then be: What is bad code and what is good code? How can we recognize which is which? And, when we find bad code, what should we do to eliminate it?
The first step is to understand why code quality matters. The answer to that lies in our need to change the code in the future. If this code will never be read again and if it will never be changed, then it only has to be functionally correct. Such code can be messy and disorganized as long as it functions correctly. But, if in the future someone needs to fix a bug, add a new feature, or make a small tweak to the code's behavior, then we need good code. The moment we discover that the code is tortuous to navigate, difficult to understand, or hard to change is the moment we wish it were better written. Over and above functional correctness (which we take as a given in this article), we can define software quality as the ability to change the code easily: Good code supports change; bad code hampers change.
Exercise, Part 1
Before we go any further, look at the program in figure 1 (located at the end of the article). This is a small script written as a command line to-do list. The script is written in Ruby but should be fairly easy to understand even if you are not familiar with scripting languages. If you haven't seen much Ruby, these hints will get you through:
- The "case/when" block is similar to a "switch/case" statement in other languages (but with no fall-through from one case to the next).
- Arrays (the variables initialized to "") have a "<<" operator that appends an item to the end of the array.
- Regular expressions are bracketed by "//" and form a kind of pattern
The application is written as a big whileloop, reading input lines and interpreting them as commands. To use this tool, you add new tasks to the list via the "todo" command. See figure 2 for a sample session. Working the list in order, you can either mark the current task "done" or skip it. If you skip a task three times, it's deleted; you'll have to manually re-add it if you intend to do it. If you move past the end of the list, you go back to the