5 ms·
The Rule of 3 is a great rule, except when it isn't. I had a colleague some time ago who wrote a couple of data importers for FAA airspace boundaries. There we
by Stratoscope 7y ago
The Rule of 3 is a great rule, except when it isn't.
I had a colleague some time ago who wrote a couple of data importers for FAA airspace boundaries. There were two data feeds we cared about, "class airspace" and "special use airspace". These airspace feeds have nearly identical formats, with altitudes, detailed boundary definitions, and such. There are a few minor differences between the two, for example different instructions for when a special use airspace may be active. But they are about 95% the same.
The developer wrote completely separate data definitions and code for the two. The data definitions mostly looked the same and used the same names for corresponding fields. And the code was also nearly the same between the two (in fact exactly the same for most of it).
It was clear that one importer was written first, and then the code and data structures were copied and pasted and updated in minor ways to create the second.
Because the data structures were unique for each (even if they looked very similar in the source code), this impacted all the downstream code that used this data. If you saw a field called FAA_AIRSPACE_MIN_ALTITUDE, you had be sure to not confuse the class airspace vs. special use airspace, because each of these had a field of the same name, the compiler wouldn't catch you if you used the wrong one, and you may have the wrong offset into the data structure.
I asked the developer and they told me, "I have this philosophy that says when you have only two of something, it's better to just copy and paste the code, but of course when you get to the third one you want to start to think about refactoring and combining them."
Yep, the Rule of 3.
In this case there were only ever going to be two. And the two were nearly the same with only minor differences.
But because of blind obedience to the Rule of 3, there were many thousands of lines of code duplicated, both in the importer and in its downstream clients.
I still like the Rule of 3 as a general principle, and I have espoused it myself. But I think it is best applied in cases where the similarities are less clear, where it seems that there may be something that could be refactored and combined, but it's not yet clear what the similarities are.
I think it is a very bad rule in a case like this, where it should be obvious from the beginning that there are many more similarities than differences.
- bcrosby95 7y agoThe rule of 3 usually is in reference to small scoped abstractions, not whole modules or subsystems. We're talking about extracting a short function, not significant and potentially thorny chunks of code. But I guess no one explicitly spells this out, so I could see where someone could become confused.
- Stratoscope 7y agoYes, exactly, and it is such a good rule to go by so much of the time. But like many rules, you need judgment to know when to apply it.
- hyperpallium 7y agoRules are a poor substitute for actual thought.
- thunderbong 7y ago> Rules are a poor substitute for actual thought. This should be the guiding principle of life!
- HomeDeLaPot 7y agoThe guiding principle... a rule, so to speak!
- NegativeLatency 7y agoEverything in moderation, even moderation itself.
- Stratoscope 7y agoReally? I'm sure you know that all generalities are false.
- antonvs 7y agoOnly a Sith speaks in absolutes
- speedplane 7y ago> Everything in moderation, even moderation itself. This was my guiding principle in life, and I thought I was pretty clever for coming up with it, but later found out that someone far more clever got a lock on the phrase in history: https://www.goodreads.com/quotes/22688-everything-in-moderation-including-moderation https://www.goodreads.com/quotes/22688-everything-in-moderat...
- yawaramin 7y ago> But because of blind obedience to the Rule of 3, there were many thousands of lines of code duplicated, both in the importer and in its downstream clients. Well sure, blind obedience to anything at all can cause problems.
- WalterBright 7y agoAny of these rules comes with "use your best judgement" and not drive off a cliff blindly following it.
- gameswithgo 7y agoEvery single rule or advice in programming is good until it isn't. OOP is good until it isn't, function programming is good until it isn't, premature optimization is the root of all evil until it is the root of all good. For some reasons humans have this deep need to try and boil things down to bulleted lists which in the domain of programming are just incredibly not useful.
- James-primitive 7y agoBulleted lists -- exactly
- TravHatesMe 7y agoI think that you have a point but I often find myself citing guidelines or rules when I am evaluating code decisions or questioning code design. Maybe it depends on your interpretation of the phrases, some sayings should be followed religiously but others applied with discretion.
- akoncius 7y agowell said. for a while I started treating everything as a soft rule, more like guideline. it gets easier then :)
- Smoosh 7y agoConsider them to be heuristics, not rules.
- bjornjajajaja 7y agoThis is because programming is an art. It has fundamental components (objects, functions, templates, iterators, etc) like in visual design (point, line, shape, value, etc). I think engineers should read the old and the new C++ books and master that language to know the evolution of all these paradigms and how to use them. There’s so much wisdom in the “Effective C++” series and Gang of Four and “C++ Templates: The complete guide“ to name a few. Problem is in this “start up culture” to bang things out and get them working the art is left behind. Just like many other arts.
- skrebbel 7y agoTo be honest I don't get it. What language was this in, that you couldn't duck type the data or make the data structures inherit from some shared interface that encapsulated the commonalities?
- Stratoscope 7y agoThat is a reasonable question. You may have noted that I left some details vague, so all I ask is that you trust me that the overall story is true. Of course if you don't trust me, that's cool too! ;-)
- skrebbel 7y agoHah! Totally do, I'm just nerd sniped by the details I guess :-)
- useful 7y agobut it was used in many places, so the rule of three applies. Personally, I would have kept the code that he wrote and made something that handled the special cases you are talking about. Everything should be replaceable, not extendable. Now if the special cases change, my code can be thrown away without changing the data feed code.
- konschubert 7y agoRules of thumb are just that. Developers still have to understand the underlying motivation (incidental vs inherent duplication).
- loopz 7y agoAre you sure merging code for different datafeeds would be better though? In such cases, what is identical and what is not, should be references to eachother in comments. But you don't know beforehand which approach would be better, unless you know the datafeeds will stay the same as now. The sad story here is that if you know the datafeeds will stay pretty static, there's little to gain making an advanced abstraction over them. Which is why you often find duplicated code that haven't been touched for years.. The original target was met with a naive approach, and no new changes lead to stale codebases.
- labawi 7y agoIf you have a 95% match on something nontrivial (and it likely won't diverge significantly), I'd go for merging even with 2 cases. At least merge most of the common parts. Reading a couple of ifs, and some not-quite duplicate procedures seems much better than having a complete 2-set in cross-refenenced files.
- dkersten 7y agoWith a 95% match, you really only have one use case, with some minor variation.
- scarface74 7y agoWhy are you reading a couple of ifs instead of having the two similar things represented by separate classes with shared functionality in a common class? Or even if you prefer composition to inheritance you could still make it work cleaner without a bunch of if statements.
- dkersten 7y ago> I asked the developer and they told me, "I have this philosophy that says when you have only two of something, it's better to just copy and paste the code, but of course when you get to the third one you want to start to think about refactoring and combining them." I would argue this is a good rule of thumb, but nothing should ever be a hard unbreakable rule. The usual refactoring rules should apply too: if 95% of the code is the same, you should probably go back and refactor even without a third use case because it sounds like the two use cases are really just one use case with slight variation.
- aj7 7y agoThere’s a safety issue here. I can conceive of these code blocks diverging more with time. Plus is there really a cost of duplication.
- scarface74 7y agoIf the compiler didn’t catch it, doesn’t that say it was modeled incorrectly? Why not have an IAirSpace interface or an abstract AirSpace class with two specializations? If there were processes that could handle either it should take an AirSpace class, one that could only handle one or the other took the specialization. If the steps were the same for handling both, have step1...step(n) defined in the concrete class and have a coordinating “service” that just calls the steps and takes in an IAirSpace.
- a1369209993 7y ago> The Rule of 3 is a great rule, except when it isn't. "Rules are for the guidance of wise men and the obedience of fools." It's unfortunate that you had to deal with a fool, but that's not a indictment of the particular rule that they picked to follow off a proverbial cliff. Edit: fixed ambiguous quote formatting.
- Stratoscope 7y agoThanks for your reply. I just want to note that what appears to be a quote in your comment ("Rules are for the guidance of wise men and the obedience of fools") is not something I wrote at all. If you still have time to edit the comment, I might suggest changing that so it doesn't look like you quoted me.