by danparsonson | 2025-08-07 03:12:12+00:00
> The wisdom in WET is that it prevents the accidental complexity created by premature abstractions

Yes, but...

> while not preventing the introduction of abstractions later.

...not necessarily.

A facetious example to illustrate the point:

I am required to implement DogWidgets and CatWidgets and I have two choices - separate the Widgetness into some kind of base class, or copy-paste.

I take the latter approach. The code is initially easier to read and maintain due, as you say, to reduced cognitive load. Fast forward two years; I'm no longer on the project. Now Joe Junior Developer comes along and over a period of several months, is asked to make a series of changes only to CatWidgets; some of these pertain to Widgetness, but Joe is not experienced or familiar enough with the code to understand that, and anyway has not been told to change DogWidget, so we eventually end up with two divergent implementations of Widgetness. Then somewhat later, Jolene Developer joins the project and is asked to fix an intermittent bug in CatWidget. The bug turns out to be partly due to the original Widget implementation, and partly due to Joe's changes - now Jolene not only has to fix CatWidget, but also examine DogWidget carefully to determine if a) the bug affects DogWidget too, and how, and b) whether or not to fix it, and how (which may be different from the CatWidget fix).

Somewhat later still, the business expands into the BirdWidget market. Rolling CatWidget's and DogWidget's Widgetness back together into a base class is now a very difficult refactoring job that is determined to take twice as long as just copy-pasting DogWidget and making Bird changes; doing the right thing by the code doesn't make economic sense. Now we have three problems.

If I chose instead to implement Widget, and derive DogWidget and CatWidget, then yes, I am imposing some additional burden on future developers to learn how the code is structured. However, now every time a change is made to CatWidget, if the design is good then it should be fairly obvious whether it's a change to Widgetness, or a change to Catness. Bug fixes and improvements to Widget now automatically filter down to both (and all future) Widgets; if Widgets need a colour, then even if we only want to give it to CatWidgets at the moment, we can stub it out or provide a default value for DogWidget, and that thought process also forces us to examine our design instead of ignoring DogWidget altogether, which helps to keep the design current and relevant. Designs should evolve over time.

If one day we get sick of this code structure and wish we had taken the other route, we can quickly copy-paste and completely fork CatWidget with basically no risk of regression, and minimal effort. BirdWidget is obviously trivial to implement. New Widget safety legislation can be implemented once cleanly, instead of three times in three slightly different ways.

> Naive interpretations of DRY are renowned to needlessly cause problems due to those mistakes.

Naive anything is problematic. DRY and WET are tools to be deployed when it is appropriate, not rules to be followed dogmatically; as I said there's a line to walk. As to your initial question:

> How can you tell if what you're task to do is to duplicate a class?

As far as I know there is again no hard and fast rule - it's a heuristic that we develop over the course of our careers: "is this likely to change in the future, and in what ways?" With experience, one can learn to foresee that the business might branch out into the BirdWidget market, and with care, develop code that supports that possible future without being too tied up in abstraction right now. Of course one can go too far, although as I outlined above, I personally feel that 'a bit too far' is overall better than 'not quite far enough', at least in the long run. I've personally spent countless hours trying to untangle legacy WET code compared with much less time necessary to understand DRY code.