4 ms·
> > The question you should ask yourself in these situations is, > > how similar will these widgets be in 3 months time? > > This is an awful approach. Yo
by lhnz 10y ago
> > The question you should ask yourself in these situations is,
> > how similar will these widgets be in 3 months time?
>
> This is an awful approach. You won't be able to predict how
> similar they will be and you will likely create expensive to
> maintain architecture for projected scenarios which don't
> exist and neglect the architecture for scenarios that will.
I'm not talking about writing code for features that don't exist. I'm talking about not doing extra work to generalise your code too early. DRY is generalisation. It is in a sense 'writing code for a projected scenario in which a feature is the same for all of its callees'.
Keeping things specialised towards a single purpose even if it is redundant in the short-term makes code easier to grok, test, and update.
Making a change across 5 redundant functions is not time consuming work and is not easy to mess up. However making a change to a singular function which needs differentiated behaviour for one of its callees is error-prone as (1) it might force an interface change and (2) there is a non-zero probability of a regression error on the other four callees.
I'm not attempting to predict the future. I'm attempting to talk from first principles about real cognitive and communicative costs.
- chipsy 10y agoIn agreement. I started using a rule of "use the language's primitives first" to guide my use of DRY. There are many ways in which you can "reduce repetition" by introducing a generalization abstraction, something that in turn couples lots of code to a master definition: Generics, high arity functions, and inheritance are three such temptations. Doing that, in turn, accidentally models the whole problem before the spec is mature. But if your discipline is "I'll use more built-in types and simple data structures, I'll keep more of the code inline, I'll let this class get bigger and I'll make some duplicated simple, fixed-function code paths," you don't get trapped by that. Sometimes I end up with the same simple math function duplicated across two different modules because I needed it for completely unrelated algorithms: That isn't pretty, but it's not a gross failure. The opportunities to make a really useful abstraction for a novel problem often take time to ripen, and until that point you might as well assume that the code necessarily involves a "spaghetti" mindset. You don't want to pay the cost of reworking the abstractions unless you have to - it presents a huge hazard to maintenance and it means the attempt to generify failed. But I think coders do get caught in a "clean code" mindset now where it needs to look both pretty and succinct, 100% of the time, and that leads to pushing the functionality out of the function body and into "ravioli" code. I think Go is a good language, of the hot ones today, for learning this kind of engineering-centric focus because it actively discourages trying to whittle down the code to the succinct one-liner. Unfortunately by the time I encountered it I already had this figured out(perhaps years later than I should have).
- c2352466 10y ago>Making a change across 5 redundant functions is not time consuming work and is not easy to mess up. You're ignoring the hardest part: Knowing that there are 5 redundant functions and finding all of them.
- lhnz 10y agoStore them all in the same place. Or if you wish to, make them use the same type signature or interface and search for this with your editor. e.g. widgets/ simple/ index test advanced/ index test weird-as-hell/ index test company-x-wanted-different-features/ index test tophats/ index test In the structure above, each of the widgets are tested separately, each are very easy to find, and each might have redundant code doing the same thing. This is way simpler and easier to work with than if people have created an `uber-widget` that has a configuration object and some dependency injection to handle all of its features because some overzealous engineer once decided that he was certain that all of the widgets would be able to be represented by a fully generalised `Widget`. I'm obviously not saying that all cases of DRY are like this, but I am saying that there are far too many people that end up doing this because they make decisions to generalise code too early.