Sometimes DRY is not the Answer

2022-05-03

graph nodes image

One of the first lessons we learn as software developer is "don't repeat yourself," or DRY for short. However, when developing user interfaces, this rule often bites us the most later.

I saw this phenomenon develop most often when we made the transition from page based architectures to the component architectures promoted by frameworks like react. In the page based world, every page had it's own bespoke design and javascript file.

Each view was naturally decoupled from the other views. Designers got used to the idea of every "page" being a unit and we didn't think too much about the re-use of components but the re-use of whole layouts.

In retrospect this was a very cumbersome way to develop and didn't make very good use of DRY principles at all. We repeated ourselves all the time or we used a template language like handlebars to make a few partials when needed. Not totally DRY, but not exactly wet either.

React comes along and really changes the game. We see individual components become the unit of construction. Developers embraced this re-use first as they began breaking designs down by the component rather than the page.

The problem was designers were not yet on board. They frequently had small alterations between components from page to page. Sure these two carousels look almost the same, but there would be a few subtle differences. No problem, let's introduce a few props to differentiate.

But then a few weeks later, another slider appears, with yet another subtle difference. So we add another flag, and another, and another. Over time we begin to have a single component being pulled in n directions.

Today designers are very much on board with component architectures. However, the same problem seems to occur over and over again. The real question is at what point does an accumulation of differences become a whole new component?

In biology, we have a clear definition of this. Two creatures can evolve away from one another, but as long as they still can mate and produce offspring, they are the same species. Figuring out when speciation occurs in a UI component is far trickier.

We can look at the DNA, or what I would say is the data that goes into the component as the measure. If two components look the same, but take different types of data then they are not the same component. This doesn't make much sense though.

A series of UI components for pie and line graphs for instance can take many types of data. It would feel incredibly cumbersome to need to produce a new pie graph component every time the data or props going in were different.

What about visually? If two pie graphs look different maybe that should be the point of differentiation. Again, this doesn't make much sense because if a pie graph simply has different colored slices should we then produce a whole new component? I think not.

Let's get a better example that incorporates a little of the new with a little of the old. Let's envision a UI with an area for metrics and analytics that lives on one path. On another path we have a dashboard with a large hero section that will showcase some impressive metrics.

Both sections utilize graphs, but one's purpose is to wow returning customers and encourage them to return to the dashboard. The other is meant to provide more utility by letting them drill down into different metrics in detail.

I find this use case to be fairly common. Pages or views tend to reference these overarching user stories and contexts. The components drill down into the various use cases needed to serve that story.

So now we have two pie graphs, they take the same data, but one looks visually more flashy and has a few animations on it for when the user lands. The other is a bit more utilitarian, and has animations removed because it appears along with many other graphs.

At the point the design is made they look almost exactly the same. This is our MVP after all and the designer is trying to make the most re-use of components possible at this stage. We'd need at most one or two props to represent the differences between the two.

However, they serve two different user stories. In my experience, these are exactly the type of components that become problematic over time. The design team requires that they be free of one another and have the ability to diverge over time. They just didn't tell you that, yet.

And this brings me to another rule that I think trumps DRY but doesn't get spoken about nearly as often. Coupling of components is worse than repeating yourself.

The reality is it's far easier to DRY up some code later than it is to uncouple a highly coupled component or set of components. Often times this happens in an evolutionary process over many sprints, such that it is hard for future developers to uncouple them.

It's more like the frog boiling in water analogy. The point to differentiate the components becomes hard to spot and it's all to easy to kick the can down the road and just add some more props and if statements.

So when creating component based architectures, we shouldn't forget our roots, the page context. The page that a component appears on is the secret flag that we might want to consider repeating ourselves in order to avoid coupling components that will diverge in the future.