If the wording seems familiar, well, that’s intentional.
This is a repost from my old blog that is now lost to time and carelessness. I found some of my drafts and reedited a handful that might be interesting to read.
So you might have read it before but it's not like I have a massive following so you probably haven't ;)
I’ve recently been to yet another annual Railsclub conference in Moscow and was left uninspired to be honest. But one of the talks really got me thinking. It was ‘All the Little Things’ by Sandi Metz of course.
Sandi was as always clear and to the point. And most importantly for me, I had some of the same thoughts brewing in my little head for a while.
Repeat Yourself?
Having been brought up on Ruby and DRY principles I have a knee-jerk reaction to code duplication, I’m pretty sure you know how that goes. But last year or so taught me that sometimes repeating yourself may end up a lot easier and cheaper in the long term.
Here’s what we observed to happen when you find similar pieces of code and abstract them away. And I can’t deny it, at first all goes well for a while.
At some point the team receives new requirements, and there are two distinct possibilities.
- The abstraction holds. We congratulate ourselves and go on with our lives until the next set of requirements comes.
- The abstraction falls just slightly short.
Let’s talk about the latter.
Sunk cost
Your colleague (or even you in several months time) looks at the code and admires all the work that went into fighting code duplication. The abstraction doesn’t really fit the new requirements that well but he or she cannot just dismantle it and copy the code everywhere. So they add some extra logic and probably spice it up with new parameters and conditionals.
Our abstraction just isn’t that abstract anymore.
Several iterations of this and the code becomes extremely complex and difficult to make sense of. How did that happen?
Takeaways?
Just the fact that two pieces of code at this point in time do similar things is not enough grounds for abstracting it away. I reckon, we need a short and succint rule and I vote for ‘Prefer composition over inheritance’. Has a nice ring to it, doesn’t it? Aw, sorry, ‘Prefer duplication over abstraction’.
That doesn’t mean we should go and write lots of repetitive code, of course not. Just know that abstraction comes at a cost. That cost is measured in lowered readability, coupling and general fragility of our codebase. Paying this price or not is your call as an engineer. It just helps to keep in mind that if your guess was not entirely correct in the first place, you paid it all for nothing.
Going forward
Don’t be afraid to pick apart the abstractions that don’t work anymore. They may have been a good idea back then, but holding on to them will bring your team lots of pain.
New code? I for one quite like the ‘Call me next time it happens’ approach. If over time it’s become clear that a piece of code keeps popping up here and there around your codebase, you’ll probably be fine abstracting it away. Don’t rush.
2019 update
Since then Sandi had written a pretty cool post which goes deeper into why we should be wary of wrong abstraction much more than of code duplication. IMHO this one is a must read for every novice developer.