How can we tell whether one application is of higher quality than another?
We all stand by our work, of course. Some of the reasons are subjective, but others are entirely objective and indisputable.
Rather than proposing an overly academic definition, let’s be pragmatic. Software is high quality not only when it correctly delivers the required functionality and provides value to the customer who uses (and pays for) it. We also consider it higher quality when its maintenance cost is low and the difficulty of introducing changes (new requirements) is low or negligible. That last point is, in fact, the mantra of this entire book, so you’ll read it often until it’s imprinted on your developer DNA.
Easy to say, but somewhat difficult to achieve. As with many things, simple isn’t always easy. In my opinion, this is the most useful definition of software quality to keep in mind in a programmer’s day-to-day work at any company, because every company wants to get the greatest possible return from the effort it puts into creating a new product.
Many developers get stuck on the first aspect of quality mentioned above—delivering the functionality, full stop—because of inexperience, time pressure, or a lack of methodological discipline. We leave the implementation of feature X exactly as we first wrote and solved it, and it stays that way forever, sometimes becoming a real burden and accumulating technical debt that will eventually come back to haunt us or the colleagues who have to take over the project.
But we already know that in any business or project, with very few exceptions, our code will have to change. The agile software movement was essentially born from that reality (“all software will change”) as a better way to approach the code we write, giving rise to specific techniques, methodologies, testing practices, the concept of agile design, and more.
Get this firmly into your head: we must write code knowing that it will inevitably change. How well it accommodates those changes will determine the cost of maintaining it and may even determine the project’s success or failure. And you know what they say: if your work fails, you’ll be told to find another job.
Let’s recap: we don’t program only to meet the functionality the application requires “today”; we also do it to keep a business running (the one that ultimately pays for it). Few businesses are static and never need to change, optimize, or improve. That’s also at the heart of this century’s new economy, where new products appear overnight just as others evolve or disappear.
It’s true that we don’t have a crystal ball to tell us exactly which changes we’ll need to make. But we’ll see that we can write code in a way that makes it easier to add new functionality later without requiring extra effort. If, for example, you genuinely believe that writing tests “slows you down” or think that programming according to design principles is a drag (because you don’t yet understand their purpose—and I know many of them are subtle), then you’re working on a short-term basis and still don’t realize that these practices actually improve your productivity and the quality of your work.
In this respect, it’s easy to determine whether an application is high quality: we only need to look at the following aspects:
- The code is simple (easy to understand). A good programmer’s skill lies primarily in finding simple solutions to problems that aren’t so simple, and in breaking a large problem down into smaller, manageable ones through abstraction.
- The code is readable (easy to read and follow). Any team member should be able to read the code easily, and any new team member should be able to take it on without difficulty. I’d even go so far as to say you don’t need gurus to do things well.
- There are enough tests to provide sufficient coverage. A high percentage of the application’s code is covered by tests of different kinds, as we’ll see in the chapter dedicated to testing. It’s an exceptionally broad topic, and a competent tester has technical skills that differ from a developer’s. To be practical, our project should have, at the very least, a good suite of unit and integration tests.
- The design and code are consistent and coherent. We shouldn’t code in the first way that comes to mind when trying to solve a problem; that may be fine as a first attempt, but the design should be consistent throughout the solution and aligned with the rest of the application in style, use of external libraries, agreed-upon conventions, and so on. Nothing is worse than spotting a part of a project and recognizing a particular colleague’s hand in it.
- Developers spend most of their time adding new functionality, not fixing bugs. If we spend a lot of time finding or fixing bugs, then the application is moving away from the definition of quality given above, and there’s plenty of work left to improve it.
- Only minor defects, if any, reach production—never critical errors. Production is the final environment where the application runs to serve the customer. As mentioned at the beginning, thoroughly testing software of any significant size is extraordinarily expensive. It’s unprofessional to release a version of our project that has serious production errors preventing the business it supports from operating properly. To borrow an analogy from architecture: can you imagine a building starting to develop cracks the day after its opening?
- The most basic metrics show good values. There are many metrics for evaluating different aspects of code, but some are easy to obtain and can help us detect certain problems: lines per method, cyclomatic complexity, dead code, and so on.
In the following chapters, we’ll discuss all these aspects and describe them in greater technical detail.
Note: This is the complete chapter titled “What Is Software Quality?” from The Practical Agile Programmer.