As part of The Practical Agile Programmer, I’ve included a short chapter on a subject I’ve thought about a great deal over the years. Sometimes things aren’t what they seem. And, above all, software development requires a lot of education and explanation for project managers who haven’t done enough programming, so they can understand some of the subtle aspects inherent in writing code.
At first, the points I’m about to make may seem counterintuitive when it comes to our work as software developers. Many of the mistakes we make professionally while programming and designing applications come from failing to understand certain subtleties involved in producing high-quality code.
Counterintuitive idea #1
It’s hard to make software simple.
We shouldn’t fall into the trap of thinking that the more complex something is, the harder it must be—or, worse, that its author must have greater skills. In software, true skill and excellence lie in finding a simple solution to a complex problem. I’ve always admired the way people find understandable solutions that solve complex problems. Nothing is worse than unnecessary overdesign or adding complexity because we didn’t take a few minutes to look for a simpler solution. That’s why we should keep simplicity in mind whenever we program.
Counterintuitive idea #2
A solution’s design and architecture evolve and grow too.
In agile development (writing software so our applications can accommodate change more easily), people often say that design emerges as new requirements are implemented, and that’s true. The alternative is to establish a design at the beginning of the application and try to keep it rigid at all costs. That’s absolutely not the way to go: the design needs to change to fit the new functionality being added. We continuously improve both the quality of the code and its design.
Counterintuitive idea #3
Writing tests increases productivity.
How many times have I heard the same thing: “I don’t have time to write tests.” Saying this isn’t professional, because backing our code with tests isn’t separate from software development; it’s inherent to it. Contrary to what it may seem, writing tests reduces the time spent finding and fixing bugs, as well as making the application more robust.
Counterintuitive idea #4
Writing tests is a way of documenting our project.
An excess of comments throughout the code is a sign that the code itself is poor. Comments are occasionally necessary, but overusing them reveals that the code isn’t simple or easy enough to understand. As for tests, how can we learn to use a class, library, module, and so on? Exactly: by looking at the tests that support it. There’s no better way to learn how something is used than to read code that tests it.
Counterintuitive idea #5
Writing tests forces us, to some extent, to produce a better design.
Imagine a monolithic application that does anything you can think of. You’ll almost certainly end up with classes that have very long, tightly coupled, rigid methods, even if the application works and fulfills its purpose… In this book, we’re trying to learn how to program so our applications can accommodate change more easily and remain maintainable, right?
That monolithic application can only be tested manually, and not much beyond that. If you try to add tests to it, you’ll soon realize that its structure and the relationships between its components (its design) need to change—they need to improve. Writing tests forces you, in a way, to create a better design.
Counterintuitive idea #6
Code optimization is an ongoing task.
Optimizing code so it runs faster is a common task, especially for the parts of an application that run most often. But leaving that work until the end is a mistake. Just as we make writing tests to support our work part of our regular development practices, we should also include small optimizations that improve performance—as long as we don’t sacrifice simplicity or readability.
Just as we improve code quality and design through small steps, the same is true of optimization.
Counterintuitive idea #7
An application’s performance comes not only from more hardware, but also from better design.
The quick fix for performance problems is to add more hardware to the production system running the application: more memory for the database engine, more SSDs, load balancing across servers, and so on. That may be necessary, but sometimes the cost only covers up a poor or inefficient architecture, design, or solution. I’ve seen thousands of euros literally burned on hardware for a solution with an incorrect, poorly conceived architecture.
Counterintuitive idea #8
Database design, like any other asset in a software project, also needs continuous improvement.
Every part of an application can be improved, from CSS and HTML files and front-end JavaScript scripts to higher-level solutions in C#. Absolutely everything. Databases and their design are no exception; they too need continuous improvement and evolution. If we keep the initial design rigid, or insist on the way the database was originally planned, we’ll end up trying to force new required functionality into it.
Counterintuitive idea #9
Big improvements always come from micro-improvements.
Software has a cumulative effect when we continuously make small changes: simple refactorings (like those described in this book), tiny improvements to code readability using clean-code techniques, design improvements, minor changes to the project’s organization, and so on. Over time, hundreds of small changes accumulate, genuinely improving the application’s quality and maintainability. Many of these improvements are also mini-tasks that take just a few minutes: improving variable names, simplifying a method, extracting duplicated code, adding a couple of unit tests, and so on.
Note: This article is a summary and part of the work-in-progress project The Practical Agile Programmer, scheduled for release in July 2018.