Taming The Destroyer Of Software – Part 1
Yesterday we discovered the real reason software usually ends up an unmaintainable mess after a few years—Unnecessary Complexity.
As I see it, we have three tools at our disposal for fighting this ‘Destroyer of Software’. We’ll examine one each day for the next three days.
Let’s get into it.
Unfortunately, Unnecessary Complexity often looks benign—especially when writing new code. Over time though, the mess usually grows, and we notice the problem. Before long, the code has become so fragile and difficult to understand that no developer wants to work with it.
Unnecessary Complexity—never mind how insignificant to start with—tends to grow. Complex code represents a more taxing cognitive load on programmers. It will take longer to understand where to make changes for a feature or fix a bug. Those modifications are likely to more complicated than they need have been with simpler code. And all this will lead to further increases in complexity.
What can we do?
We can change how we code.
As programmers, we often think we understand the logic we need to write. And so we start writing it in code. When we program in the traditional ‘Edit & Debug’ manner, we end up with a working algorithm after many false starts and much debugging. But we’re happy—our code looks tight and not at all unnecessarily complicated.
Enough abstract hyperbole. Let’s get concrete.
I hired a few programmers to write a simple ShoppingCart
implementation. This ShoppingCart
class had to have a Total
property that calculated and returned the cost for all the cart items.
Here is the code the developers produced for Total
:
Programmer 1:
public decimal Total { get { return this.Items.Sum(k => k.Quantity * k.Product.UnitPrice) ?? 0; } }
Programmer 2:
public decimal Total => _items.Count() == 0 ? 0 : (_items.Select(c => c.Quantity * c.Product.unitPrice).Sum());
Programmer 3:
public decimal Total => Items.Sum(x => x.Subtotal);
Programmer 4:
public decimal Total => items?.Sum(s => (s.Key?.UnitPrice ?? 0) * s.Value) ?? 0.0m;
Which of these is the easiest to understand?
How about Programmer 3’s effort? Doesn’t it almost read like English? “The Total is the sum of the Subtotals of the items.”
Programmer 3 is the only one who used Test-Driven Development (TDD)—the other programmers did not use TDD.
TDD is a technique where we use unit tests to drive the development of the code incrementally. Each test is only marginally more involved than the last. With TDD, we derive the functioning code using baby steps.
Each TDD unit test is a driver of desired business functionality. We only write the tests if they add value. TDD demands that when we have no test then we also write no code. In this manner, not only do we get verification of the required behaviour, specified through these tests, but we also have only the code we should have—no more and no less. No Unnecessary Complexity!
I have written extensively on TDD in the past. Get to my articles on TDD articles here.
TDD is excellent, but it is not sufficient to conquer Unnecessary Complexity. We need more tools in our toolbox.
We’ll look at another tool tomorrow.
Leave a Reply
Want to join the discussion?Feel free to contribute!