I'm reading Robert Martin's new book, Clean Architecture, and a recurring thought keeps bubbling up in the back of my head. Anybody who has worked or trained with me has heard it plenty, and it goes like this.
Every design decision comes with benefits and drawbacks. Mastering design skills is largely developing the experience and judgement to weigh them.
The design principle hanging with me now, as it often has in my career, is DRY. Don't Repeat Yourself (DRY), as the Pragmatic Programmer phrased it back in 2000, is widely considered to be a fundamental design imperative. According to my assertion above, though, it has benefits and drawbacks.
As part of his Extreme Programming process, Kent Beck defined Simple Design as a short sequence of priorities that goes:
The system (code and tests together) must communicate everything you want to communicate.
The system must contain no duplicate code. (1 and 2 together constitute the Once and Only Once rule).
The system should have the fewest possible classes.
The system should have the fewest possible methods.
These rules have been revised many times since then, but it also caused a large and recurring argument (not really by Beck's design). The argument went like this. If we value code expressiveness higher, sometimes we duplicate code to be more expressive. If we value eliminating duplication, it might cost us expressiveness. that said it was better to duplicate code if it made the code more expressive. I participated in this argument plenty, and my experience lead to a purely personal conclusion. If code is duplicated, there is at least one idea hiding in there that wants its own abstraction. Those abstractions will communicate better, and therefore there is no real conflict. However, I still regularly talk to craftsmen I respect who disagree.
Reading Clean Architecture (See Chapter 7), I found a different example of when to violate DRY in favor of SRP and I wonder if the conflict I see is similarly nonexistent. The proposed challenge was to have one piece of logic whose definition spans multiple stakeholders (actors by his definition). In this example, the logic was defining how overtime hours were calculated. Different divisions in the company started with the same definition but over time diverged. DRY would say that logic should exist in one place until the divergence happens. Bob, if I understood correctly, stated that it should be duplicated because each stakeholder as a different reason that the code could change, and therefore, sharing logic violated the Single Responsibility Principle (SRP). SRP states that a unit of code should have exactly one reason to change.
So, professionally, I'm a little scarred from years I spent, weekends and holidays lost, and fires survived cleaning up piles of fragile, unmaintainable duplication in code. However, I give Uncle Bob's experience a lot of weight as well, so I should take a step back and see what I can learn.
Before the logic diverges and stakeholders force separate calculations, what are the pros and cons of DRY vs SRP?
Advantages of SRP:
It can only be broken in one place
If it works one place, it works everywhere
If the rules change (new overtime legislation/policy?), they update everywhere.
Easier to isolate time calculations from other business logic.
Time calculations are usually consistent to time domain (ie region, governing laws, etc) rather than specific to business domains (ie operations vs sales). That means we can very possibly be wrong about what kind of change would call it to sheer apart in what direction.
Advantages of Duplication for SRP:
If one stakeholder calls for a calculation change, the other's features is safe from getting broken. Would acceptance test automation catch such a break?
Expected use can be made more explicit
At first glance, the winning point for me in this is the crystal ball problem. The SRP design has me assuming that I know what the shape of future change forces will look like. I find programmers (myself included) to be supremely confident in their predictions on such things, and almost always wrong. That means I clearly lean toward DRY, again. However, it is best to hold opinions lightly if we are to grow, so questions that I might ask include:
What other advantages and disadvantages are there?
Which set of problems is more likely?
Which set of problems is more painful? and therefore
Which set of problems is more maintainable/sustainable?
Is the example just a less than ideal example of the principle? (like I said, good examples are HARD)
What do you think?