
Minimalism: Practical Guide to Writing Less Code (2002) [pdf] - mingodad
http://www.two-sdg.demon.co.uk/curbralan/papers/jaoo/Minimalism.pdf
======
christophilus
> Consider duplicate code to be a mistake

I partially disagree. Some of the worst code I’ve seen was an attempt to
reduce duplication.

It’s easy, especially in UI code, to mistakenly identify duplicate code, and
prematurely build a DRY “solution”. Premature DRY is the root of much evil.

I try to follow the rule of three before trying to identify duplication. Even
then, I’ve become much more cautious than in my younger days.

~~~
Aardappel
This "rule of three" seems to be fashionable these days, but I think you'd
want "the rule of 2" all the way to "the rule of 10" depending on the size of
the duplicated code, how hard it is to abstract, the complexity of the
resulting abstraction, the distance between uses and abstraction, etc.

I certainly wouldn't want to wait to 3 if I am copying a block of 10+ lines
that requires just a single int parameter to abstract into a function very
local to the 2 uses. And I may use "the rule of never" on a 2 liner that
requires a callback to abstract.

~~~
brianpan
It's a rule of thumb. You don't refactor if and only if three duplicates
exist.

------
Kaveren
I vehemently disagree with the idea that returning from a function often is
bad and that it should be used "sparingly". I find that code that returns as
early as possible is the best. The idea of a nearly mandatory single return
point is a very antiquated notion. I'm much more concerned when I see ten
levels of indentation for no good reason.

Likewise with continue and break. I think this advice is very bad. All the
other recommendations seemed fine.

~~~
userbinator
When I can't return early, I find that the && operator, because of its short-
circuit evaluation, is extremely useful for writing chains of "everything
before must successfully execute" without the labourious series of "if/else
ladders" or deep nesting that would otherwise be needed. Compare:

    
    
        success = doOneThing() 
               && doSecondThing()
               && doThirdThing()
               ...
               && doLastThing();
    

and the more common "traditional" way:

    
    
        success = doOneThing();
        if(success)
        {
            success = doSecondThing();
        }
        if(success)
        {
            success = doThirdThing();
        }
        ...
        if(success)
        {
            success = doLastThing();
        }

~~~
8note
promise chains do that pretty nicely

    
    
        Promise.start()
        .then(doOnething)
        .then(doSecondThing)
        .then(doThirdThing)
        .then(doLastThing)
        .catch(handlesomeerrors)

~~~
coldtea
That's because promises kind of Monad-ic.

------
Dowwie
"Recommendation 2: Include Only What You Need"

The slide refers to the inversion of control principle but can easily include
other important aspects of including only what is needed.

I would like to expand on this for discussion. Consider the challenges of not
over-engineering solutions to problems you and others don't fully understand,
yet. Unless you work for NASA, you won't fully know what you need until you
need it.

How do programmers here account for the unexpected?

~~~
scarygliders
> How do programmers here account for the unexpected?

You don't. At least, I don't.

When I'm writing code for an application, I'm doing the following...

"Hmmm, I need a function to perform <certain thing>"

Function is written... tested... debugged until <certain thing> is achieved.
Job done.

Whilst writing said code, I do not think to myself "Hmmm... y'know in the
future it might need to also do <some future thing>" and proceed to add
additional function parameters and write additional code which would only be
useful sometime in the future, just in case.

No. I write sufficient code for what is required of said function at that
moment in time. It is only if and when required would I then add any
additional parameters and code to that function. Or write new functions.

~~~
javajosh
There's a difference between not adding bells and whistles you might not need,
and shipping asymmetric, broken abstractions. Just because your app doesn't
need to support vectors in the negative plane, for example doesn't mean you
should ship a vector library that doesn't support negative numbers. In the
same way I see well-meaning devs "dribble" data into an API, forcing future
users to expand the API into a shape that could easily have been anticipated,
and made cohesive sense. This stinginess with API is particularly bad because
now developers learn to not respect API boundaries at all, because there
aren't any. (And additionally it contributes to a lot of unnecessary
indirection and typing because you have to define subset types at both ends of
the API.)

So, yeah, DON'T just add arbitrary stuff because you'll think it will be
useful to someone; but DO add stuff to maintain the symmetry and
understandable abstraction of what you've made.

~~~
kungtotte
I think you're agreeing with everyone else.

Including/writing a fully featured Vector library _when you need a Vector
library_ is perfectly fine; but if you're writing some code to handle those
Vectors you should stop after it's done and not generalise it to also handle a
bunch of other types in case you might need it later (because you probably
won't).

~~~
javajosh
I wish I was agreeing! But sadly I am seeing more than a little but of
jingoism here, which I think is 80% right, but like anything you can take it
too far. "Every generalization is false," etc.

------
taeric
Main rule for writing less code: only write the code you need to solve the
actual problem you have.

It is fine to write towards future problems, but all to often trying to be
generic explodes the codebase and leads to bugs.

------
jeffdavis
Without understanding why these rules get broken, it's hard to avoid breaking
them.

There is a tension between dependency and redundancy. Avoiding redundancy
often introduces dependency as code becomes shared for several purposes.
Knowing which is preferable (dependency or redundancy) in a given case
requires a lot of judgement and changes as the code evolves.

Redundancy is bad when it introduces too much code and too many chances for
error or unnecessary divergence of implementation.

Dependency is bad when two things that start out closely related diverge in
purpose, straining an implementation to handle both cases.

------
growtofill
(2002)

Also, previous discussion:
[https://news.ycombinator.com/item?id=5024221](https://news.ycombinator.com/item?id=5024221)

------
brachi
For many real world examples of minimalism and simplicity, see projects such
as st or dwm from suckless.
[http://suckless.org/philosophy/](http://suckless.org/philosophy/)

~~~
s4vi0r
Suckless is at best satire and if we're being honest just plain idiotic.

Without fail every suckless person I've talked to either has no idea what
they're talking about, or in the off chance they do they're incredibly shitty
far right wing types who gripe on about women/minorities/CoCs ruining tech.

~~~
mhd
Yikes, that sounds like a horrible experience. Never got that from hanging
around at the Arch linux forum back in the days where their products were
moderately popular.

Having said that, my general impression was that there was an inordinate
amount of Plan9 cargo-culting (with some DJB fanboys mixed in). Young,
idealistic people without much experience but lots of admiration for the more
Bauhaus part of their elders.

------
mingodad
Looking around int the web [http://www.jbox.dk/](http://www.jbox.dk/) refered
on this submission
[https://news.ycombinator.com/item?id=18829779](https://news.ycombinator.com/item?id=18829779)
about Sanos operating system I found this pearl.

------
christophilus
> Prefer code to comments

I used to make this same argument, and then took it overboard by never
commenting. After reading some literate codebases, I’ve changed my mind.

Code is (almost?) always obvious to you as you are writing it. So, you never
recognize code that is in need of a comment until you come back to it and
struggle to understand its purpose and the context that gave it birth.

I now follow a rule: a meaningful comment at the top of each file. A comment
on every exported function / value. Often, in the process of writing the
comment, I realize I’ve poorly named something or that I’ve failed to handle
some case. Comments help guide the reader, but also the writer.

~~~
rdsubhas
Can confirm. This is literally what happened in a codebase in the past:

    
    
      // OLD CODE WITH COMMENTS:
      // check ...
      4 lines of code
      // drop ...
      4 lines of code
      // read ...
      4 lines of code
      // do xxx ...
      4 lines of code
    

In total, that method was like 16 lines of code, with some short commented
sections. And the sections used variables from before.

Situation: New coder comes in, sees comments. Says "comments are bad, they get
out of date, yadda yadda". So proceeds to change comment names into method
names:

    
    
      // PROPOSED: COMMENTS => METHODS
      check()
      drop()
      read()
      doxxx()
    

What coder forgot was the _variables and context_. As said before, the total
was 16 lines of code with simple variables used between it. Not a big deal.
But now when it has to be split into functions, it became like this:

    
    
      // ACTUAL 1: COMMENTS => METHODS
      a, b, c = check()
      d, e = drop(b)
      f, g = read(a, d)
      i = doxxx(e, g)
    

And this was a language that didn't support multiple return types. So the code
was actually:

    
    
      // ACTUAL 2: COMMENTS => METHODS
      class X { a, b, c}
      class Y { d, e }
      class Z { f, g }
      X x = check()
      Y y = drop(x.b)
      Z z = read(x.a, y.d)
      i = doxxx(y.e, z.g)
    

So now coder added 3 more classes, with more lines of boilerplate. Then the
coder decided, to _manage_ this problem better. So they created some more
interfaces and did some DI. The result was around a 250 line PR, which I
cannot ping here anymore. When we pointed out the same logic was now 250
lines, the answers were: "It's inherent complexity which we didn't know how to
manage. His/her solution scales better, and he/she has shown us the way".

All for what? Because comments were considered a smell. Go figure.

I guess this is why this other HN post trended – "Please do not simplify this
code":
[https://news.ycombinator.com/item?id=18772873](https://news.ycombinator.com/item?id=18772873)

~~~
nerdponx
It really depends. In my own software, I really appreciate when code is
"clean" like that. I find it easier to keep in my head when methods are short.

It also depends on logic reuse. If you are now able to use the `check` method
in other places, then I say it's overall a successful refactor.

~~~
zmmmmm
> I find it easier to keep in my head when methods are short

I think that's true if "short" is measured in terms of internal complexity. I
think each function should try to manage a small bite size amount of
complexity that represents a reasonable level of cognitive load for a brand
new person to reverse engineer. It could be quite a lot of lines of code if
they are very simple ones, or it could be a one liner if it's super complex /
obtuse.

~~~
nerdponx
Good point. Yes, I meant _logically_ short. Maybe _logically compact_ is more
apt.

------
hibbelig
I struggle to understand this; I guess the speaker added a lot of information
verbally.

~~~
yura
Indeed. Apparently the author didn't think that minimalism included simplicity
of language ("Baroquecratic", really?) and not adding tangentially related and
unnecessary quotations in every page.

------
mihau
Is there some version of this pdf with code examples ?

~~~
majewsky
What an ironic question.

~~~
mihau
Some recommendations in this pdf are sooo vague and lack proper context. For
example, what the hell does this mean: "Let the Code Make the Decisions: There
is no need to spell everything out in minute detail".

~~~
AlexCoventry
Majewsky was mocking the document, not you, I think. It's maybe taken
minimalism a little too far.

~~~
majewsky
I was not really mocking anyone. Just noting the irony of someone asking for
more code in a document that is about writing less code.

------
1penny42cents
Could someone explain these recommendations more in-depth?

(5) Manage Resources Symmetrically (10) Let the Code Make the Decisions

What does it look like when you do the opposite of these rules?

I think I'm unfamiliar with the problems these slides address, so I can't
grasp the insight in them.

