I once saw two functions that looked alike and did what every good engineer is trained to do: I pulled them into one. DRY. Clean. I felt responsible.
For a while it was fine. Then the two cases started to drift, the way real requirements always do. One needed a special path. I added a flag. Then another. Soon my elegant shared function had a parameter list like a control panel and an if for every caller who’d ever touched it.
Every change to it became a small act of courage, because everything ran through that one body and I could never be sure which caller I’d just quietly broken.
What I’d missed was that the two functions weren’t the same thing. They just looked the same on the day I merged them. Duplication was telling the truth — these are two separate ideas — and I’d overruled it in the name of tidiness.
I’ve since learned to wait. Let code repeat two or three times. The right abstraction reveals itself once you’ve actually seen how the cases vary. Guess too early and you don’t get a shortcut — you get a knot you’ll be untying for years.
Duplication is cheap to fix later. The wrong abstraction moves in and refuses to leave.
– Serguey Asael Shinder
Leave a comment