Maybe this was true when Programming Perl was written, but I see the opposite much more often now. I'm a big fan of WET - Write Everything Twice (stolen from comments here), then the third time think about maybe creating a new abstraction.
Maybe this was true when Programming Perl was written, but I see the opposite much more often now. I'm a big fan of WET - Write Everything Twice (stolen from comments here), then the third time think about maybe creating a new abstraction.
I've always heard this as the "Rule of three": https://en.wikipedia.org/wiki/Rule_of_three_(computer_progra...
So no, the end result can still be DRY.
Sandi Metz https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction
That doesn't people from trying though, platform creation is rife within big tech companies as a technical form of empire building and career-driven development. My rule of thumb in tech reviews is you can't have a platform til you have three proven use cases and shown that coupling them together is not a net negative due to the autonomy constraint a shared system imposes.
Use the wrong abstraction and you are constantly fighting the same exact bug(s) in the system. Good design makes entire classes of bugs impossible to represent.
I don't believe the trope that you need to make a bunch of bad designs before you can do good. Those lessons are definitely valuable, but not a requirement.
A great example is the evolution from a layered storage stack to a unified one like ZFS. Or compilers from multipass beasts to interactive query based compilers and dynamic jits.
The design and properties of the system was always the problem I loved solving, sometimes the low level coding puzzles are fun. Much of programming is a slog though, the flow state has been harder and harder to achieve. The super deep bug finding, sometimes, if you satisfactorily found it and fixed it. This is the part where you learn an incredible amount. Fixing shallow cross module bugs is hell.
Don't you have to be really seasoned to in good faith, attempt to couple two systems and say where that would be productive? You can't prove this negative. I would imagine a place like that would have to have a very strong culture of building towards the stated goals. Keeping politics and personalities out of it as much as possible.
A third time, with a new abstraction, is where you need to be careful. Fred Brooks ("Mythical Man Month") refers to it as the "second-system effect" where the confidence of having done something once (for real, not just prototype) may lead to an over-engineered and unnecessarily complex "version 2" as you are tempted to "make it better" by adding layers of abstractions and bells and whistles.
I'm really talking about manually writing code, but the same would apply for AI written code. Having a single place to update when something needs changing is always going to be less error prone.
The major concession I make to modularity when developing a prototype is often to put everything into a single source file to make it fast to iteratively refactor etc rather than split it up into modules.
But what happens when new requirements come in for just one of the things? If you left them separate, it's an easy change of a few lines. If you created an abstraction, now you either have to add a bunch of if statements, or spend time undoing the entire abstraction that you spent X amount of time creating.
If a bunch of other code has built up around that abstraction, undoing it can become a serious chore. I've worked on apps that had way too many premature abstractions, and we just had to live with it because it would be too risky and onerous to try to undo them.
In my experience, it's generally an order of magnitude easier to add an abstraction to a mature app when you get tired of making changes in multiple places, than to remove one when the app evolves and you realize these things aren't actually that similar. Also when you wait to abstract, you might see a better way to do it, or how to reduce the scope so that you're using composition to share a bunch of smaller pieces vs. sharing the entire page/object/interface/endpoint/etc.
Obviously, this isn't a blanket rule. There's an aspect of soothsaying to guess which things might diverge and which are likely to spawn a lot more similar copies.
I guess it could happen, but that depends on your mental model when coding - if you're just pattern matching similar chunks of code (which are not being used in a semantically identical way) then all bets are off, although that seems a very alien concept of how someone might code.
OTOH, if you have a higher level mental model of what you are doing then it's not a matter of "this looks like common code" but rather "i need to do the exact same operation" (same inputs/outputs/semantics) here. Maybe I'm expressing it poorly, but I can't recall ever having to fork a function because requirements at two call sites just diverged.