
Simplicity Before Generality, Use Before Reuse - j4mie
https://medium.com/@kevlinhenney/simplicity-before-generality-use-before-reuse-722a8f967eb9
======
nnq
> People do not on the whole pay for — or need — generality: they tend to have
> a specific situation, and it is a solution to that specific situation that
> has value

Yeah, but otoh, most progress in science and tech has been made by _sort-of-
tricking_ people into _paying for stuff that solve a slightly more interesting
/general problem than the original boring problem they wanted solved._

So at least when it doesn't drown everyone in paralyzing complexity, we need
to remember to keep that "hacky rascality" alive and _slightly_ "screw" our
clients at the expense of developing some general and reusable solutions
(hopefully as open-source libraries :P) that will probably matter more than
whatever "important" problems they want solving ;)

~~~
jerf
"slightly more interesting/general problem"

I'd say there's no great sin in solving a _slightly_ more general problem than
what you currently face. In fact, that's often net simpler almost immediately;
it's not long before a function to format a URL is simpler than simply
manipulating a URL every time. (Even ignoring the simpler issues.) The problem
arises when you have something like "Let's see... I need a database... _let 's
write an ORM_." Whoops. (Speaking from harsh experience there.)

------
dgreensp
This article really hits home in terms of how people sometimes think about
React components, and how I’d like them to think about them.

I sometimes hear, from other engineers working on a complex React app, that
the point of React’s components is that every component is written to be
“reusable.” Every component. This is used as a justification for “going
general” immediately when writing a component. In reality, there is real work
to make a one-off component into a reusable component, and that work can only
be done in the context of having (or at least realistically imagining, not
dreaming up as an exercise) multiple use sites.

What React components actually provide are the same things that modules and
functions provide: encapsulation; limited dependencies; a calling convention.
When you write a function, you try to keep the arguments to a minimum; you
might say it makes the function more “reusable,” but there is still a big
difference between a library function that is _designed_ to be used from
anywhere, and a private helper function that is not. React components are
“reusable” the way functions and modules are reusable. A better word might be
“usable.” :) You can reason about the use, even if there is only one use.

Making something “reusable” means making it general, and we’ve all had the
experience that the article points to of taking some code that was intended to
be general, but in fact is used in only one place, and remaking it so that
it’s actually possible to use in a second place.

That said, I have many times in programming succeeded in boiling something
down to its essence in advance, and writing the general thing first, followed
by the specific thing. I suspect this is actually more about simplicity than
generality, however. I’m thinking of cases of “bottom-up” development where
you write the primitives and then the higher-level parts in terms of the
primitives. Actually, maybe it is about generality too, and when you are
testing out your primitives in different combinations, you are doing the work
required for good generality (seeing lots of different cases and basing your
decisions on that).

------
Derbasti
This is especially true for libraries. A minimal, simple library is much
better than a sprawling, "convenient", but more complex library.

~~~
makapuf
Or -God forbid- framework.

------
whack
Sorry for ranting. I've been reading articles like this one for many years
now, and I've certainly learnt a lot through them. Lately though, it feels as
though we as a profession are just going around in circles, not making any
real progress.

More and more, these articles remind me of schoolyard wisdom, the kind people
love to throw around because they sound wise. _" Winners never quit."_ "
_Quitters never win "_. " _Quit while you 're ahead"_. Taken individually,
they all sound perfectly reasonable and wise. Put them together, and clearly
there's something wrong.

This article strikes me in the same vein. " _YAGNI. Keep your code as simple
as possible. Focus on your code as it is right now, and not what 's going to
happen in the future"_. " _The only constant is change. Business requirements
are always in flux. The mark of good code is the ability to change its
behavior without overhauling everything "_. Taken individually, they both
sound perfectly reasonable, but put them together, and something is clearly
wrong.

Maybe it's time we accepted that there is no one right way. No one all-
important metric or rule that trumps everything else. That simplicity is
important, that conciseness is important, that DRY is important, that being
able to adapt to future changes is important. That good code finds a balance
between all of the above, without sacrificing one on the altar of another.
That instead of writing articles trying to elevate one over another by
attacking strawmen, we should instead present realistic code examples,
thorough which we can have a grounded discussion on how best to find the
balance between all the different qualities that comprise good code.

It's not going to be as easy or fun as simply declaring _" X considered
harmful"_, but it's going to help elevate the quality of our discussion, and
help our profession progress to the next level.

~~~
makapuf
Id say that this article is trying to be too general and give advice for a too
broad case. It would be better to focus on real world cases, solve them and
try to find some slightly generalised advice </meta>

------
tome
I'd like to take this opportunity to flesh out some ideas I've had regarding
the difference between the concepts "generic" and "general". "Oh, I think we
need the ability to do this kind of thing someday" falls under "general",
giving more and more functionality to a component. "Oh, there are several
things about this piece that we don't actually need to know" falls under
"generic", allowing the code to generally be simpler. In Haskell

    
    
        traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
    

is more _generic_ than

    
    
        traverse :: Int -> IO Float -> [Int] -> IO [Float]
    

and therefore strictly better (unless there are reasons the latter could be
optimized but not the former).

~~~
TheCoelacanth
They are definitely some downsides to the first definition. For instance, the
error messages will often be much harder to read. For the second one you might
get something straight forward like it expected `[Int]` but the actual type
was `Int`. For the first one you will probably get something about not being
able to deduce some long constraint for some long type. That might be possible
to improve with better tooling, but for now it is a very real downside.

I think that especially for application code (as opposed to library code), it
is a mistake to make your type signature far more generic than you need right
away, especially because it is usually easy to switch to the more generic type
declaration later without changing much existing code.

~~~
tome
This is a good point. I think there's some merit in doing

    
    
        traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
    
        mySpecialTraverse :: Int -> IO Float -> [Int] -> IO [Float]
        mySpecialTraverse = traverse
    

in some instances.

------
bootsz
This really hits home:

> _How much of what software engineers now do is still devoted to the
> accidental, as opposed to the essential?_

Far, far too much

------
curyous
So true. Premature generality is a big problem.

~~~
jandrese
IMHO this is one of those things that only experience can help with. Nobody
wants to be the guy adopting a single-use codebase to a somewhat different
problem and having to rewrite half of it, but overgeneralizing from the start
leads to complex and bloated programs that are hard to deploy anywhere--where
the effort to deploy it is about half as much as it would have required to
write a custom solution from scratch...

My personal philosophy is to keep the dependencies to a minimum and make
judgement calls on simplicity versus generality. I probably err on the side of
simplicity a little too often, but I also get asked to deploy solutions on
non-internet connected devices fairly often. Solutions that you can deploy out
of a tarball on a base CentOS 5.5 machine are good sometimes.

------
bullen
Great article! We need more "practical rational knowledge" infused arguments
in a sea of over-engineered "download and configure" nightmares!

