May 27, 2025 · 2 min read · Career Growth and Technical Leadership
Choosing Between Duplication and a Shared Abstraction
A solution can look better in isolation and still be harder for the team to maintain. The people who will change and support it are part of the decision too.
Two workflows in an enterprise system looked similar enough to share a generic implementation. The abstraction looked good. It removed duplication, created a clean class hierarchy and made the first pull request smaller. I usually don’t like duplicated code, so my first choice was the abstraction. It also required engineers to understand several extension points before changing one business rule. That was the trade-off we had to evaluate on a project at Softcraft.
Following the Abstraction
The two workflows shared validation, persistence, and part of their processing sequence. They differed in a few domain rules that were expected to keep evolving. We could put the common behavior in a reusable base and add extension points for the differences. Technically, it worked. But we also tried to imagine an engineer changing one business rule six months later. The cost appeared when we walked through an ordinary future change. To understand one workflow, an engineer would need to move between the base implementation, configuration, and several overrides. A rule that belonged to one business process would be spread across code designed to serve both. We’d save lines now and make context harder to find later.
Who Would Maintain It?
We sometimes discuss maintainability as if it only depends on the code. In practice, a team maintains the code. People join, move between projects, respond to incidents, and make changes without the person who designed the original abstraction beside them. That doesn’t mean every implementation must be basic. Teams should learn, and difficult problems sometimes need difficult designs. But the complexity needs to solve a problem that justifies the extra context every engineer will need later. In this case, the shared behavior was smaller than it first appeared. The differences were the parts most likely to change.
Keeping the Workflows Separate
We kept the workflows separate and extracted only the parts that already had the same stable meaning in both. The result had more lines and looked a little untidy at first, which made me uncomfortable because I normally try to remove duplication. But each business process could be read from beginning to end, and changing one rule didn’t require checking a base class, configuration and several hooks. If the workflows became more similar later, we could extract more with better information. On that project they continued changing in different directions, so the repeated lines ended up being easier for us than the cleaner abstraction we almost introduced.