This past summer, I bought my dream home: a century-old Craftsman by the sea. When I bought the home, it had fallen into disrepair, crawling with ivy, with sagging porch steps. My partner and I quickly got to work, replacing a boarded-up window and patching numerous holes in the walls. The home had been used and disused for generations, and it showed. But the home itself, built from old-growth Redwood, has remained sturdy as ever — despite decades of dogs, kids, and even earthquakes.
This home has taught me a lot about lasting design. And as I continued to look into the history of the Craftsman home, I found key concepts that could, and should, be applied to the field of software engineering.
The Arts and Crafts movement originated in England in the 1860s. It arose, as did many movements at the time, in response to the advent of industrialization. Suddenly, furniture that used to be handcrafted was being mass-produced. Details that were tedious to do by hand could be cranked out by machine, and regard for quality of design and materials began to decrease in the bid for efficiency.
John Ruskin, a key player in the the Arts and Crafts movement, was alarmed by what he saw. He noted that the design of both furniture and architecture had been heavily influenced by the conditions in which they were produced. Where before, design was influenced by aesthetic concerns, materiality, and the knowledge of the craftsmen, now design was being largely dictated by the needs of automation.
Now we find ourselves, a century and a half later, in the field of software engineering — a discipline that would probably leave John Ruskin reeling. Still, I think many of these concepts apply. Often, we as engineers over-index on DRY and the pursuit of reuse, using a mass-production mentality. It’s naive to attempt to make a model, controller, or service that suits every need. Often, we need to start from scratch and create something new, even if that requires tedious-feeling duplication.
Often, I go back to Sandi Metz’s article The Wrong Abstraction. In it, she says that existing code exerts a powerful influence, which is a similar sentiment to Ruskin’s thoughts on automation influencing design. You may find yourself trying to abstract away shared code in order to accommodate several use cases that are, by nature, different. The same way a mass-produced chair aims to suit every user’s needs until, ultimately, it suits nobody’s needs.
At Empora Title, we’ve found this principle to be true time and time again. In some cases, we’ve successfully avoided “the wrong abstraction,” and in some cases we have not. One such case was a command called SaveDeal
. As a command, this class contained a single public execute method, which contained CUD operations related to a deal (the title agency concept which represents a real estate transaction).
In this SaveDeal
command, we had two branches; these branches handled logic for new deals and existing deals. We had included both scenarios in the same command, because there was a decent amount of shared logic. For both cases, we had to save several records associated with a deal. But, the bulk of the logic in the command lived in the conditional.
From the perspective of someone calling SaveDeal
, it may have been convenient to have this catch-all command that can be applied to any deal. But from a code maintenance perspective, the command was doing too much. Ultimately, we split this command into two separate commands: CreateDeal
and UpdateDeal
. We found it necessary to make this refactor before we bloated the scope of this command any more. Even though doing so required just a little bit of code duplication.
Of course, this is not to say that DRY principles cannot be selectively applied. DRY can be a very helpful rule of thumb. But I’ve often seen engineers consider DRY to be the goal, which causes design to become dictated by the pursuit of removing duplication. If we consider abstraction to be a means to reduce duplication, we’re thinking of abstraction wrong. Abstraction does not exist to make code reusable, but to encapsulate a concept, and often that concept needs to be highly specific. Ultimately, I think we need to approach the concept of reuse in software with more nuance. Reuse is only helpful when it increases clarity.
As the Craftsman movement migrated from Britain to the United States, the original anti-industrial message — which had condemned the use of machinery outright — became softened. The use of machines in constructing homes and furniture became acceptable, as long as the machines were not dictating the design. Artisans realized that a combination of manual and automated techniques could help them produce work efficiently, while not compromising their command of the design. As engineers, I think we should do the same. We can keep the DRY principle in mind, but the moment we allow it to dictate our design, we relinquish control and cease to be craftsmen of code.