Of course there are many other places such as the mentioned C2: http://wiki.c2.com/?RuleOfThree , my point is just that the Erik authoritatively says he is postulating it, but it's already all around the internet.
Author here. It's slightly embarrassing that this turns out to be an old idea – it wasn't my intent to rip it off. I'm fairly sure I must have seen it a long time ago and then forgot about its origin. In retrospect I probably should have googled it.
The article now mentions this at the bottom. I think if he were intentionally trying to rip it off he wouldn't have called it "rule of three" because that makes it too obvious. I think he just forgot having seen it.
It's kind of fortunate that it worked out this way; I like seeing people comment on a well-established idea as if it's newly postulated.
I think the real solution here is just in learning more about your problem domain and teaching yourself to think ahead better. To borrow the example from the article, if you know that you're working with a small domain and auth will only differ in the parameter names and URLs, then it's ok to generalize like in the first example.
If you want to be able to build something that works with any bank ever, then you need to think from a higher level: you have a login operation that creates a session, and then a fetch-statement operation (that takes the session as a parameter), and so on, and those are your general building blocks that make up your interface.
No, you may not get it perfectly correct, and you may need to rethink and refactor at some point if you come across something different from what you could imagine. But that's not really a big deal, and you shouldn't be so afraid of refactoring that your go-to strategy is to always copy and paste similar code everywhere.
The awareness of this sort of thing perhaps isn't inherent; it comes with experience, but I don't think the take-away is to shy away from general functions. I've found that it makes things easier in the long run to think in terms of high-level operations and interfaces that you implement. Clean APIs aren't just for customers; you should architect your own "internal" code in the same way as if you intended to make it a public interface that random people could use. There's certainly a pitfall in trying to make things too generic, but after a while you develop an intuition around finding where the best balance lies.
More specific: being too generic can be solved by downcasting in consumers without affecting the module boundary; being too specific cannot be solved without changing inter-module interfaces.
Having a generic callback interface exec(Map(object,object)) is ugly, but better than changing 76 upstream and downstream modules' interfaces with code ownership and release cycles across different vendors/organizations.
Ah, interesting. I actually rather liked the AMP version on my desktop browser, with the full width and the giant pictures. Found it strangely compelling and I assumed is was a neat design touch.
While I kind of agree with the rule of 3, in the example given, I would just rename BaseScraper to ScraperWithFormLogin when encountering the 3rd instance and not derive the 3rd from anything (or create an abstract BaseScraper) - there's still a high likelyhood there will be another scarper with form login.
Why not just pull it out into a function, "loginWithForm(session, 'user', 'password')"? Why is it interesting on a class level that the scraper is using a form based authentication method? What if a page is switching between two authentication methods just for laughs?
> The problem is we’re overfitting massively to a pattern here
Seems like this will be true regardless of the number of units we're breaking down at. There can always be a new outlier later, and trying to predict future use cases is usually a losing battle. It's not like you can't refactor yet again later.
This just seems like a combination of choosing the wrong abstraction and applying the DRY principle in a way that doesn't actually lead to less or clearer code.
In this case almost none of the lines contain any superfluous information. At best you could try to simplify things slightly by writing it as follows:
Same goes double/triple/multiplier-of-your-choice in the UI layer. While back end code usually has process and rules behind it, so many things in the UI are:
- actually identical, but by coincidence
- perceptually identical, but not
- perceptually identical, but technically unrelated
We have this issue with our code quality tool telling me that I have identical html in some places. It's not at all helpful, but I want my jsx files included in the figures for quality.
Agree with respect to code organization but I can't help but feel the the number three is somewhat arbitrary and it nudges the reader in the wrong direction.
Instead of waiting until the third, I evaluate by asking myself if the duplication is accidental, how likely is it for the next instance to be different, and what will any extra arguments look like. If it doesn't feel right I leave the repetition.
The problem with this approach is, if there are 10 such different cases, which can be made a one-liner, the dev would instead make those 10 cases to 20, while waiting for 3rd to come in either of them. Instead of being afraid of future, do some abstraction. If a 3rd case is massively different, then based on the timeline either modify the abstraction, or create a new-functionality.
Code that is swift and easy to extract makes doing it a low-cost operation; just because you can do it quick and fast doesn't mean you should, is the point made in the article.
I've used a modified version of this. I run a process by hand. I run it a second time by hand. The third time I automate it. By then, I've seen enough to know what I'm actually trying to do.
My 3 rules of sotware engineering are: 1 - KISS (keep simple and stupid) 2 - DRY (do not repeat yourself except to avoid violating rule 1) 3 - there are only 2 rules
[+] [-] franciscop|8 years ago|reply
Of course there are many other places such as the mentioned C2: http://wiki.c2.com/?RuleOfThree , my point is just that the Erik authoritatively says he is postulating it, but it's already all around the internet.
[+] [-] erikbern|8 years ago|reply
[+] [-] brlewis|8 years ago|reply
It's kind of fortunate that it worked out this way; I like seeing people comment on a well-established idea as if it's newly postulated.
[+] [-] kelnos|8 years ago|reply
If you want to be able to build something that works with any bank ever, then you need to think from a higher level: you have a login operation that creates a session, and then a fetch-statement operation (that takes the session as a parameter), and so on, and those are your general building blocks that make up your interface.
No, you may not get it perfectly correct, and you may need to rethink and refactor at some point if you come across something different from what you could imagine. But that's not really a big deal, and you shouldn't be so afraid of refactoring that your go-to strategy is to always copy and paste similar code everywhere.
The awareness of this sort of thing perhaps isn't inherent; it comes with experience, but I don't think the take-away is to shy away from general functions. I've found that it makes things easier in the long run to think in terms of high-level operations and interfaces that you implement. Clean APIs aren't just for customers; you should architect your own "internal" code in the same way as if you intended to make it a public interface that random people could use. There's certainly a pitfall in trying to make things too generic, but after a while you develop an intuition around finding where the best balance lies.
[+] [-] _pmf_|8 years ago|reply
Having a generic callback interface exec(Map(object,object)) is ugly, but better than changing 76 upstream and downstream modules' interfaces with code ownership and release cycles across different vendors/organizations.
[+] [-] gilgoomesh|8 years ago|reply
https://erikbern.com/2017/08/29/the-software-engineering-rul...
to get site that's a little easier to read in a desktop browser.
[+] [-] nicky0|8 years ago|reply
[+] [-] jbb67|8 years ago|reply
It seems like nobody wants to build a function to do the thing thats needed, they have to build an abstraction or a framework.
[+] [-] magnushiie|8 years ago|reply
[+] [-] ptr|8 years ago|reply
[+] [-] asplake|8 years ago|reply
[+] [-] bpicolo|8 years ago|reply
Seems like this will be true regardless of the number of units we're breaking down at. There can always be a new outlier later, and trying to predict future use cases is usually a losing battle. It's not like you can't refactor yet again later.
[+] [-] JoachimS|8 years ago|reply
https://en.wikipedia.org/wiki/Second-system_effect
[+] [-] contravariant|8 years ago|reply
In this case almost none of the lines contain any superfluous information. At best you could try to simplify things slightly by writing it as follows:
[+] [-] unknown|8 years ago|reply
[deleted]
[+] [-] zinckiwi|8 years ago|reply
- actually identical, but by coincidence
- perceptually identical, but not
- perceptually identical, but technically unrelated
- arbitrarily different in special cases
[+] [-] wry_discontent|8 years ago|reply
[+] [-] aslakhellesoy|8 years ago|reply
[+] [-] blago|8 years ago|reply
Instead of waiting until the third, I evaluate by asking myself if the duplication is accidental, how likely is it for the next instance to be different, and what will any extra arguments look like. If it doesn't feel right I leave the repetition.
[+] [-] sidmkp96|8 years ago|reply
[+] [-] gfiorav|8 years ago|reply
#1 – It should be swift and easy to extract code, if not that's a different problem.
#2 – You need to be patient and research ahead. You don't really have an option if you're in a big project. You get used to it.
[+] [-] Cthulhu_|8 years ago|reply
[+] [-] AnimalMuppet|8 years ago|reply
[+] [-] reacweb|8 years ago|reply
[+] [-] tashian|8 years ago|reply
[+] [-] lwhalen|8 years ago|reply
[+] [-] unknown|8 years ago|reply
[deleted]
[+] [-] rjcc|8 years ago|reply