The Rule of Three (2017)
andrewbrookins.com
andrewbrookins.com
Which brings us to today where our team had to maintain 2 different code bases, doing almost the same thing, in 2 different ways. Every single feature request we get, takes twice as long, because we have to make them independently in two different code bases. Similarly for bugs, testing, and ramp-up time. This is one of the biggest dev complaints that we have even today, and one of the factors that led to management deciding to "junk it all" and start over from scratch.
Code duplication is better than the wrong abstraction. And the right abstraction is better than code duplication. Finding the right abstraction is very hard, so I can see why people would just settle for the middle ground of duplicating code. But I still think that trying diligently to find the right abstractions is what we should be striving for
Starting over from scratch doesn't seem like it's going to help find the right abstraction[0]. All the nasty edge cases and such that make the existing code ugly are what you need to consider in order to know the "right abstraction", and they're usually what get ignored in the early design stages of a new rewrite.
You're at the point where you've learned pretty well what aspects are both shared and stable between the systems. Seems like a perfect opportunity to start to modularize the existing codebases instead - start to split out the shared functionality so it only has to change in one central place, while allowing the two different top-level projects to have their specific quirks on top of that.
[0]for most business processes, I think "finding the right abstraction" is actually a dangerous red-herring. Sometimes you have complicated processes. Sometimes clean readable well-organized code is what you need, not an abstraction over it.
At first code gen this way seems ideal, but the trouble is you'll start to realise one project needs a feature that the other doesn't and or it needs something implemented in a different way. The code gen in question doesn't support feature flags in a straight forward way, and adding extra features comes at a cost greater than just writing code to do it properly. Then you'll have to start maintaining not only the two projects, but a proprietary codegen framework.
It takes a human to look at the context and decide if duplicating or abstracting is the right answer.
The explanations of this statement that I’ve read make no sense to me. They amount to “using an abstraction for two things which just happen to be the same today” is bad. Well, obviously.
It’s like seeing that your tire pressure is 35 psi, and the speed limit is 35 mph, therefore #define speed pressure.
It’s really not difficult to see when A=B by definition or by chance. I no longer follow the Rule of Three, and I can’t recall any situation where I thought I’d wasted any time on an abstraction that didn’t end up working. I have spent a lot of time dealing with duplication when someone copy/pasted a second case (presumably a follower of the Ro3), even though it was obvious from the start that the two cases would always be identical.
Some times it is. On those times it's better not to abstract prematurely. Some other times it isn't hard to see the equality is by definition, but it's really hard to see what is or isn't covered by the definition.
On both cases, if you get more examples, your life gets easier. That's why people took this wisdom and compressed it into an easily digested pill inexperienced developers can use to treat all their duplication needs, either it helping or harming.
updateDescription(image) {
let description = window.prompt(
"Update description",
image.description
);
if (description !== null && description !== image.description) {
actions.doUpdate(image, { description });
}
},
updateCredit(image) {
let credit = window.prompt("Update credit", image.credit);
if (credit !== null && credit !== image.credit) {
actions.doUpdate(image, { credit });
}
},
I waffled on whether to deduplicate it or let it be. What was the Obvious Right Choice?What about adding a one line comment that this code can be refactored and improved, and how? But not doing anything immediately.
If you run into this annoying code repeatedly, then, it can be worth taking the time to refactor it.
Of course, from this distance it's impossible for someone to judge whether your or your management's judgment is warranted. But there is a risk in junking it, called the "Second System Effect". Don't over abstract this time.
Also, it's also worthwhile to document and explicitly write out a list of requirements. You can be more or less scientific about this, but the exercise will keep you focused on meeting requirements instead of ivory tower design.
> If you want to compare two things (e.g., "is my new code faster than my old code"), a very simple approach is to measure each three times. If the all three measurements of X are smaller than all three measurements of Y, you have X < Y with 95% confidence.
> This works because the probability of the ordering XXXYYY happening by random chance is 1/(6 choose 3) = 1/20 = 5%. It's quite a weak approach -- you can get more sensitivity if you know something about the measurements (e.g., that errors are normally distributed) -- but for a quick-and-dirty verification of "this should be a big win" I find that it's very convenient.
Less than three points sounds too simple; more three points too complicated
AHA is the trope of the rule of 3. Albeit this post was written some time after this post.
I remember it this way: avoid making things dry that they become brittle.