Fen's Law of Programming

My character arc as a programmer doesn’t seem very likely to result in any eponymous laws. But if it does, may Fen’s Law of Programming be as follows:

Copy-paste is free; abstractions are expensive.

My younger colleagues are probably tired of hearing me say this, but it’s a lesson I’ve learned and re-learned countless times over my career: too much abstraction causes great pain later, and the problems that come from too much copy-pasting are small and easy to fix.

Exegesis:

Say you have an interface - a module, class, API, whatever. And you find yourself in need a second such interface, whose internals are almost identical to the first one:

export function SubmitOrder(userId, orderId) {
// ... 50 lines of implementation
}

// todo: SubmitVendorOrder
// - takes userId, orderId, vendorId
// - mostly same implementation, only a few lines changed

In cases like this you have a choice:

  1. Copy-paste and make a few edits, so you’re left with two largely identical functions
  2. Add a new argument to the existing function, and update the body to handle both cases
  3. Refactor, so you have two exported functions but they share a common implementation

Of course rank beginner programmers choose (1), because they don’t realize there are other options. But in my experience intermediate and senior devs tend very strongly towards (2) or (3) - they’ve had “DRY” pounded into their brains, and seeing the same code in two functions feels like a code smell.

Further, I think most devs base this decision mainly on how much code would be duplicated. If it’s a few lines it’s probably fine - but if it’s 50 lines, then developer instinct screams that refactoring must be done.

And that instinct is what I claim we should reconsider. When you move two pieces of implementation behind a common interface, you are telling the world that they are the same “Concern” (in the sense of “separation of concerns”). You are promising yourself that future changes to one implementation will also affect the other - or if they don’t, you’re signing up to fix whatever needs to be fixed as a result.

So the practical application of Fen’s Law is this: when considering whether to duplicate or refactor code, pay no attention to the volume of code involved. Instead ask yourself:

When one of these implementations changes, how confident am I that the other will always need the same change?

  • If the two code paths seem almost guaranteed to stay in sync, then great! They’re the same Concern, as far as you know, and DRY applies.
  • If the two code paths look similar today, but they have different consumers or business owners, then sound the alarm! Abstract them at your peril.

So don’t be afraid to copy-paste. A year from now if you see that your two functions never diverged, you can easily merge them if you like. But if you merge them today into a shared implementation, and the needs of each consumer diverge over time, then a year later you’ll have a big hairy function filled with edge cases - and you won’t even remember that it once handled two separate interfaces. Unnecessary duplication is an annoyance, but unnecessary abstraction is among the toughest problems you’ll face as a developer.