
Code boilerplate: Is it always bad? - shurcooL
https://medium.com/@shazow/code-boilerplate-is-it-always-bad-934827efcfc7
======
grasleya
This is a hilariously bad take. If we follow the argument to its natural
conclusion then go is itself a poor choice of language. After all, go removes
memory management boilerplate in favor of an automated gc system. By the
author's argument go should force you to manage your memory yourself. In fact,
we really should all just code in assembly because it has the clearest
relationship between code and what is going on under the hood. True, coding in
assembly requires a lot of needless boilerplate but by this article's argument
that's actually a feature.

Go has so many truly bad design decisions (lack of generics being the most
damning) and it's sad to see its fanboys try to argue that they're actually
good.

------
majkinetor
I always write terse code, not only because it reduces the number of lines
(which is important to me) but also because:

\- You clearly state the atomicity, that this thing happening in 1 long line
is specific thought, intent, desire - it shouldn't really be on multiple lines
- its easier to remove/comment out intent (as it is always self contained),
replace it, optionalize it and reuse it.

\- Scrolling the code is damaging to the focus.

\- Once you learned the specific methods used to create terse code seeing it
repeated all over again you will become more proficient in reading the code
all around.

\- Its way more fun. Its like poetry in programming - why would we all write
dummy boring stories. Language is everything (obligatory babel 17 mention:
[https://en.wikipedia.org/wiki/Babel-17](https://en.wikipedia.org/wiki/Babel-17))

------
SomewhatLikely
_" By the time I’m done—adding more recovery scenarios, better logging,
augmenting errors with more context, handle more edge cases—most of my
boilerplate gets a lot more interesting. I did not realize that good
boilerplate could be fertile ground for iteration."_

This point makes sense, however I think I like to have my cake and eat it too.
Java 8's stream constructions can be relatively terse, but if you need to
switch back to a for loop, IntelliJ has nifty right-click menu items for doing
so. I think the argument made by TFA is at least related to John Carmack's
take on inlining vs. abstraction: [http://number-
none.com/blow/john_carmack_on_inlined_code.htm...](http://number-
none.com/blow/john_carmack_on_inlined_code.html)

~~~
buzzybee
I followed Carmack's suggestion and never looked back. I write simpler code on
average now, and I spend less time having to re-read it too because I'm
changing the same lines less often. What I lose in terseness in the short-
term, I regain in being able to spot key abstractions that are more crucial to
long-term program health. It plays well with debugging since there are fewer
places where state can act at a distance.

It's very unfashionable advice in some circles, though. It sounds just enough
like "go back to writing unstructured copy-paste spaghetti" to trigger some
people's kneejerk. But the ground rules of "don't take subroutines, don't jump
backwards" actually keep it from getting too far out of control, because they
align you towards code that only uses enough flexibility for the task at hand,
hence there's a lower likelihood of missing something. Very good practice for
greenfield code where you might reflexively add a generalization that you
don't use. I can still get better at it.

Edit: And one of the most important things it taught me, is that I can use
scope blocks and a comment instead of a function, and it'll be sufficient.

------
ovao
Interesting take. I think everyone can find _something_ in this with which to
agree, so I don't think it's a particularly polarizing way of thinking, but I
haven't seen terminology like this used to describe it before. I fall into the
same psychological trap in C++ that the author falls into with Python: the "if
this is over here instead it's more [elegant/right/concise/impressive/less
boilerplatey]". Unfortunately, I find it too tempting to value beauty and
elegance (and often masking real weight and complexity) over the in-your-face
'rawness' of simple loops, branches and so on. In Go this is harder to do in
general, so you only try for so long before you learn not to fight it. This
comment really spoke to me:

> _Maybe this is subjective but I posit that the code’s shape looks more like
> what it is._

Indentation isn't merely stylistic or syntactic, but it tells us useful things
about code at these lower levels. A more complex code shape should convey more
complex logic, and we should want that complexity apparent to us...at least
sometimes. It looks great to have a nice, terse one-liner, but visually
flattening possible control flow paths feels really nasty.

~~~
atilaneves
I didn't agree with anything. The Python version is leagues better.

------
maehwasu
"Man inadvertently discovers reason for monads and strong typing while writing
Medium post"

------
braveo
> Any time we write similar-looking code over and over, that’s considered
> boilerplate.

That's not what boilerplate code is, and DRY is not the way you get rid of
boilerplate (the article claims it is a part of the solution).

boilerplate is a repeated preamble that's necessary and unchanging in order to
get down to the business of actual logic.

An easy example of boilerplate would be include guards in a C/C++ header file,
or 'use strict' in perl.

For example, the author says this:

> When I write Go, I noticed that my tweaks almost always go exactly where
> they naturally belong. My boilerplate doesn’t stay untouched!

If you're "touching your boilerplate" while writing code that affects the
logic of your system, then it isn't boilerplate.

~~~
makecheck
Boilerplate can be common and still be tedious.

Those examples are just clear indications that languages sometimes have really
terrible default behaviors. Namely, Perl should have been strict by default
(with a "no strict" if this didn’t perform well enough), and C++ should have
at least added #import.

------
raz32dust
To take an analogy, it is similar to saying that we should use small words
when trying to communicate. But once you understand the meaning of the more
complex words, it is more fluent to communicate in terms of the longer words
which represent the abstract concept more concisely and correctly. Of course
it actually holds true to some extent in case of spoken languages, but that is
because your intended audience is not expected to understand all the words in
the dictionary. But the programmer who is reading the code is expected to
understand the whole dictionary of python, including all the keywords,
constructs and idioms.

It gave me some thought, but ultimately I think don't think it is a correct
argument.

~~~
oblio
To take your analogy to its conclusion, we don't want to communicate only
using small words, true, but we don't want to communicate only using big
words, either.

Ain't nobody (= most people) got time for Hegel :)

I suspect that's why some of the very "expressive", i.e. dense languages,
aren't widely adopted.

------
slaymaker1907
Yes. By using higher level constructs, it is much easier to see what is going
on in the code. If there are too many lines, it is much more difficult to
understand a piece of code as a whole.

------
meric
I iterate code like he does, then when it gets to where it's doing what I want
it to do, I refactor it to make it more readable even if it reduces
iterability. After the initial phase of writing the code, going back and
forth, etc, the code is going to be read many more times than edited, if in
the future it needs to be edited again, the future editor can refactor the
nice looking code into whatever shape, and the important thing was that future
editor understood my code in the first place, and I hope that future editor,
when done editing, will refactor the code into being easily read for the next
person, too.

------
olavk
It seems he have been writing code so clever and compact that he didn't dare
to change it when it came to refactoring time:

> I’d take any opportunity to write terse code. It feels good to be clever

> I noticed that when I wrote Python code, I would often go out of my way to
> avoid messing up a beautiful terse set of lines by moving logic to places
> other than where it might naturally belong.

I can see how a less expressive language might help with this problem, but I
think a better solution would be to rein in the cleverness and focus on
readability rather than shortness.

~~~
slavik81
I think it's related to the iron law of abstractions. It's easier to change
code that has no abstraction than the wrong abstraction. The right abstraction
is better than both those, of course, but is often difficult to identify
upfront.

~~~
mickronome
Succinctly put. I have observed a tangentially related correlation: The code
that needs least understanding to change will be the code changed, until it
isn't the code needing the least understanding any more.

In unison they sort of tells us why pragmatism is often good when we start
writing code, but how we must always try to push towards finding (good)
abstractions as the work progress.

------
flavio81
In the article:

I was expecting a discussion about boilerplate but the author writes about how
ugly/bad is code that does a lot of operations in one line.

Then he introduces the concept of "proportionality", and according to it,
assembler must be the best language since the program statements are directly
proportional to the operations to be performed...

Suddenly high level programming is no longer good stuff...

Meanwhile no real discussion about boilerplate!!

Whenever your code shows implicit patterns that get repeated but cannot be
abstracted away using code, you have boilerplate and boilerplate is almost
always a bad thing. Your choices are then:

1\. Pretend those patterns as a good thing.

2\. Switch to a more powerful language and destroy the boilerplate either
because it isn't necessary anymore, or because you can abstract it away with,
for example, a macro.

~~~
shurcooL

        > Then he introduces the concept of "proportionality", and
        > according to it, assembler must be the best language since
        > the program statements are directly proportional to the
        > operations to be performed...
        >
        > Suddenly high level programming is no longer good stuff...
    

Looks like you didn't understand what he meant in that section if you think he
implied Assembly is better than high level languages. It's not that 1 line of
code should do exactly 1 unit of work, but rather that if 1 line does 100
units, then 10 lines should do 1000.

Function calls are exempt from that, because everyone understands that a
function can do arbitrary amount of work, and one needs to know what it does
(by reading its name and documentation). The cost is considered to be a
"function call".

Yes, Assembly also has high proportionality, which is a benefit to it. But it
doesn't mean all other languages are bad. These are tradeoffs.

The point is that it's a beneficial property of a language when you have code
length that better corresponds to the amount of work it's doing.

~~~
flavio81
> but rather that if 1 line does 100 units, then 10 lines should do 1000.

Why they should? It has no reason to be that way.

If anything, 1 line should do 1000, if possible. Some lines will do 1, others
10, others 5, others 100. "Proportionality" adds no value and, quite the
opposite, discourages programming at the highest level possible.

~~~
shurcooL
> "Proportionality" adds no value

I think it helps improve readability of the code.

------
elgenie
The title is significantly more ambitious than the content. I think "Go: I
have found my Blub" would be more accurate.

------
xapata
Meaningful names are critical for understanding. Without good names, it's
incomprehensible, regardless of the complexity.

~~~
Ace17
Exactly.

If you abstract things but don't give meaningful enough names, you loose
everything (because your callers are still forced to lookup the definitions of
your abstractions, and now this definition can be hard to search for).

------
moomin
The proportionality argument is bunk, straw-man level bunk. Consider what goes
on every time go accesses an object, casts to an interface or writes to a
channel. Huge, complex code under the covers, way more than the Python
examples he's given. What this article really says is "There are abstractions
I'm not as comfortable processing as others, therefore those abstractions are
bad."

If having code be proportional to complexity was a good idea, we'd all be
writing in assembly and subroutines would be frowned on.

~~~
contravariant
I think having code be proportional to 'complexity' is good, but having it be
proportional to 'work' makes no sense. Personally I find things 'simple' when
they only require one level of abstraction, such as accessing an object, or
performing arithmetic operations on algebraic objects (e.g. multiplying
matrices). And complex when they require more (e.g. manipulating the
coefficients of two matrices to define their matrix product).

Boilerplate is when you requires more code than the complexity requires,
usually because the language doesn't provide a sufficient opportunity for
abstraction.

Note that, if we define a '->' operator that roughly expands to the following:

    
    
        (x, err) -> f :
            if (err != nil) {
                 return nil, err
            }
            return f(x);
    

Then the entire code he wrote as an example of boilerplate can be written as

    
    
        DoSomething -> SomethingElse -> MoreWork;
    

with some minor differences that could be optimized away in the right
language.

------
logn
You can write code like Hemingway (simple) or like Shakespeare (dense). Hard
to say whether one is better than the other without some attempt to quantify
and measure things. Seems more like just a preference.

In regard to boilerplate the author brings up an interesting point that with a
good language, the boilerplate can effectively disappear once all the code has
been written. If people are worried that boilerplate violates DRY, I think
they worry too much.

The best way I've found to determine whether a codebase is good or not is to
take someone who's never worked with the code before and ask them to fix a bug
or make an enhancement. If that's an excruciating task but would have been
easy in a greenfield project, the code sucks.

------
buttershakes
It's not true for all languages, but I actually like Erlang's boilerplate.
Almost every OTP project has the same structure, and a very close set of
primitives that drive the setup and structure of the application. It can be
annoying to setup and get right, but its a common set of structure and
patterns which make it much easier to dive into a large project.

So for me it is not always bad, just usually, with some specific exemptions
that I think are done well.

------
Ace17
[https://vimeo.com/9270320](https://vimeo.com/9270320) (Greg Wilson - What We
Actually Know About Software Development, and Why We Believe It’s True).

It seems we have scientific data showing that the number of code lines one can
review per day doesn't depend on the programming language. This means one
would spend a lot more time reviewing this C++ code if you looking at the
manually-rewritten-to-assembly version instead.

This is a very good argument supporting programming at the highest possible
level of abstraction, which is the exact opposite of boilerplate and
verbosity.

So please let's not rationalize/stockholm-syndrome over the limitations of any
programming language ; boilerplace and verbosity are always bad.

------
collyw
So expressiveness of a language is now seen as a bad thing?

~~~
rev_null
Among golang developers, yes.

------
chvid
More bad excuses for not having exceptions in your language ...

------
Tarean

        b = sum((j if j % 2 else 0) for (j, k) in results if k)
    

This definitely is borderline unreadable but that isn't necessarily because it
does a lot of things. It is because it does a lot of things without order and
with a lot of line noise. Compare it with

    
    
        sum [j | (j, True) <- results, odd j]
    

or

    
    
        filter snd >>> map fst >>> filter odd >>> sum
    

The bigger problem is that this carries a bool around. If it is only used for
filtering filter it beforehand.

The _Opportunity for correction_ is an interesting idea but the authors issue
mostly come from a lack of referential transparency. In a pure language
refactoring wouldn't be an issue so one could argue this is a symptom of too
little abstraction, not too much.

~~~
skrebbel
If you'd name your variables all three would be perfectly readable I bet.

~~~
Tarean
Technically the third has no variables...

Slightly less wise-ass, I couldn't come up with a use case for this code
that'd give sane variable names. Third attempt:

    
    
        sum $ do 
            (element, True) <- results
            guard (odd element)
            return element
    

That's pretty far from imperative languages, though, which I kind of tried to
avoid.

------
thescribe
I find the number of mistakes I made scales at least linearly with the number
of lines I write. Boilerplate is bad because it allows me to write more.

------
macscam
I assumed the title was about boilerplate in terms of some skeleton or
framework you download. Turns out this is different from what youre talking
about (verbosity). Its interesting that even in programming jargon we have two
uses for the same term. They are not totally separate though. The repetitive,
DRY violations eventually get abstracted into a framework, at which point the
boilerplate becomes ok. And if its still too verbose, than another boiler may
arose to encompass it.

~~~
braveo
The author doesn't understand what boilerplate means.

~~~
w-ll
>> Any time we write similar-looking code over and over, that’s considered
_copy-pasta_.

is what i'd call it.

------
janwillemb
When I see the error-handling examples, I think that the problem with this
kind of "boilerplate" is that it's easily forgotten. This results in errors
being eaten and that's bad. But I agree with the main point that
"proportionality" of code is a good thing.

------
phatoni
From the maintenance perspective I would prefer the more verbose code in most
cases, because it is easier to debug. Moving the verbose code block to a
method or function for reuse will fulfill the DRY principle as well. Then you
can decide during debugging to step over or step into the method. Doing to
much in one step would also contradict another rule: the single responsibility
principle.

~~~
grasleya
A counterpoint is that more declarative code can often remove the need for
tedious step by step debugging because the behavior of the code is more
transparent. I do most of my work in Haskell and find I don't typically need
to debug it like I would for a less declarative language because the meaning
of the code is much more clear.

~~~
phatoni
I come from a C# background and have little experience with Haskell, so YMMV.
C# got many new features over the years like LINQ or Lambda Expressions. My
experience is that you get most of them if used in a moderate way. Like
splitting complex LINQ into smaller parts and introduce intermediate results
instead of one big query. They are also easier to test this way. Another
important aspect of this topic is if you are maintaining your own code or code
others have written. I used to maintain code written by another team, which
only did feature development and barely any code maintenance. While doing only
code maintenance on code written by others for a longer period I gained a new
perspective on what's good code in the long term.

------
KirinDave
Call me old fashioned, but I remember when encapsulated was lauded as a good
thing because it insulated the programmer from whatever programming model was
considered popular this year was a virtue.

------
platz
I bet Fortran has an even lower 'proportional' variableness.

------
shazow
Hey HN, I wrote this post. I'm getting the sense that not a lot of commenters
are taking the time to read the contents before reacting to the title, so I'll
share a little spoiler/TL;DR:

Good boilerplate is boilerplate that usually gets changed over time (and in
different ways each time) as we iterate over the code.

It took me a while to come to this realization (and I explain the reasoning
and consequences in the post), so I hope it's helpful to other people who
might not have found this intuitively obvious.

Also perhaps "terse" or "clever" are not the best words to describe the
counterpoint—I welcome edit suggestions.

~~~
userbinator
_I 'm getting the sense that not a lot of commenters are taking the time to
read the contents before reacting to the title_

As evidenced by none of the discussion hitherto noticing that your Python and
Go examples actually perform a slightly different process? Was that
deliberate? ;-)

(I rarely use Python and never Go; yet I noticed quite easily that the Python
code is summing the selected _odd_ elements, while the Go code is summing the
selected _even_ elements.)

~~~
qb45
> As evidenced by none of the discussion hitherto noticing that your Python
> and Go examples actually perform a slightly different process? Was that
> deliberate? ;-)

Good catch. I like abstract, declarative and structured code because if
nothing else, it requires some level of engagement to write which has a side
effect of reducing silly mistakes.

------
addicted
I have no idea what the Go code does, but the python code is immediately
clear. It reads like a line of English.

I can't see how anyone could prefer the Go version.

And I don't know if python allows this (it may not due to the significance of
whitespace), but your proportionality problem can be easily resolved by
placing each different clause on a different line, like this:

    
    
      sum((j if j % 2 else 0) 
        for (j, k) 
        in results 
        if k)

~~~
wayn3
this is what happens when people who "dont need math to program" write code.

if you want to sum over even numbers, just do:

sum([j*2 for j in range(0, end_range/2)])

and yes, brackets supercede pythons "whitespace is syntax". if you need
linebreaks where, ordinarily, linebreaks are not allowed, just write brackets.

~~~
elgenie
What the code in the article is doing is:

Given an iterable of pairs, sum the first elements of the pairs for which the
first element of the pair is odd and the second element is truthy.

What the code you're suggesting does is, to be polite, unrelated.

~~~
wayn3
and no sane person would write it that way. whatever it is that its trying to
do.

i agree that i misinterpreted the codes intention.

------
gravypod
I think most of the complaints about the python version go away when you do
something like this....

    
    
        is_even = lambda n: n % 2 == 0
        score_result = lambda j, k: j
        is_interesting_result = lambda j, k: is_even(j) and k
     
        a = some_variable + 42
        b = sum(map(score_result, filter(is_interesting_result, results)))
    
    

or

    
    
        b = sum(j for (j, k) in results if is_even(j) and k)
    

List comprehensions only become difficult when you start doing conditional
execution within the result side. Use if for filtering or turn your complex
operation into a meaningfully named function/lambda expression.

~~~
Spiritus
The first one is horrible and super un-pythonic (it even violates PEP8[1]).
But perhaps even more important, it doesn't even work since a map-function can
only take one argument (not two).

Using functional programming paradigms like this often advised against in
Python.

[1] [https://www.python.org/dev/peps/pep-0008/#programming-
recomm...](https://www.python.org/dev/peps/pep-0008/#programming-
recommendations) (search for "Always use a def statement")

~~~
gravypod
> The first one is horrible and super un-pythonic (it even violates PEP8[1]).

I'm not really concerned with PEP. I'm concerned with if the idea being
expressed is cleaner then the original. My first iteration was cleaner then
the original comprehension (despite the comprehension being PEP8). It also let
me see what I was really doing and I was able to turn my map and filter into a
much nicer list comprehension.

Also, pointing out lambdas instead of defs is silly. It was done for an
example. Most of the time you'll be operating on data structures with built in
functions or supporting functions you will have written making it cleaner to
use the map and filter paradigms.

    
    
        money = sum(user.get_payed_balance() for user in users if
                                               user.has_paid_bills() and 
                                               user.is_still_subscribed())
    

bs

    
    
        clients = sum(map(User.get_payed_balance,
                      filter(User.is_still_subscribed,
                          filter(User.has_paied_bills, users))))
    

I much prefer the filters to the list comprehension in this case. I'll think
"User's money for user in users if user has paid their bill and subscribed".
I'm going to think "Sum the get_payed_balance for every user who
is_still_subscribed and who has_paid_bills" for the functional implementation.
If I had complex tuple arraignments then (like the OP's post) then I agree LCs
will be the better way forward. If you're dealing with objects or functional-
fitting problems I prefer map and filter.

> But perhaps even more important, it doesn't even work since a map-function
> can only take one argument (not two).

The concept still stands. Just use `from itertools import starmap`. Starmap is
like map that calls the * operator on arguments.

    
    
        >>> a = lambda a, b: 10
        >>> list(map(a, ((1, 2), (3, 4))))
        Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
        TypeError: <lambda>() missing 1 required positional argument: 'b'
        >>> list(starmap(a, ((1, 2), (3, 4))))
        [10, 10]
    

> Using functional programming paradigms like this often advised against in
> Python.

I'm not concerned with if it's "Advised against". I'm concerned with a) will
someone be able to understand this better then the implementation that
currently exists) and b) will I be able to come back in 10 years and tell what
this is doing if need be.

You should check out this talk [1] before going around trying to use PEP8's
inconsequential clauses to call something "horrible" code.

[1] - [https://www.youtube.com/watch?v=wf-
BqAjZb8M](https://www.youtube.com/watch?v=wf-BqAjZb8M)

------
hacker_9
Yes but it is avoidable if you use a Lisp. Every part of the code can be
compacted into a smaller form. I know when I write in Clojure it's always the
most concise code I've ever written, with minimal effort too.

~~~
_asummers
This is true in Elixir as well, which has hygienic macros very much like a
Lisp. There's a balance, of course, but it really is quite nice to be able to
just rewrite the AST as you need. And one must always remember the adage data
> functions > macros.

------
combatentropy
All of the examples are far better than the code I normally deal with.

That being said, the most tiresome code is this kind of step-by-step error
checking.

    
    
      if (! is_numeric($id)) {
        exit('The ID is not a number.');
      }
    
      $db = open_database();
      if (! $db) {
         exit('There was an error connecting to the database.');
      }
    
      $query = db_query($db, 'select * from widgets where id = $1', $id);
      if (! $query) {
         $db_error = get_db_error_msg($db);
         exit('There was something wrong with the query: ' . $db_error);
      }
    

Zzzzzzz. It triples the code size. One third is the normal code path. Two
thirds is all the error checking. I'm sorry, I just don't write it:

    
    
      $db = open_database();
      $query = db_query($db, 'select * from widgets where id = $1', $id);
    

I let errors bubble up to some general error handler, which stops the script
in its tracks and gives the user a vague and unhelpful error message. It
doesn't matter. Even if I gave them a very specific error message, all that
they would do is log a ticket, and I would still go into the server log to
uncover the line number the error was on, debug, and fix it.

Don't get me wrong. I write JavaScript to constrain user input, with friendly
directions and error messages. And I set tight types in the database and add
database constraints where I can. But I don't write all this step-by-step
checking in the middle layer (PHP, Python, Go, what have you). So if the user
somehow bypasses the JavaScript and tries to insert text into an integer
column, the database will simply refuse, and the resulting web page will be a
very ugly error message, which is what they deserve.

If the database is down, it's going to be ugly, regardless.

I admit that this strategy works okay for non-life-threatening, SQL-backed web
apps and such. You may need to do something tighter in your work. But I wonder
if there would still be something more elegant than the every-other-line error
checking shown in the article.

~~~
userbinator
_But I wonder if there would still be something more elegant than the every-
other-line error checking shown in the article._

Factor out the error checking into a function:

    
    
        onerrexit(is_numeric($id), 'The ID is not a number.');
        onerrexit($db = open_database(), 'There was an error connecting to the database.');
        ...
    

I admit you still have to explicitly state you're checking, but that's not
much overhead, makes the code somewhat clearer, and gets rid of all the
if(...). If the error-action is more involved, like your "third paragraph",
then you can make an errcheck that takes a function reference for the error-
action instead (not sure if that's possible --- haven't used PHP in a long
time --- but there's probably a similar method otherwise.)

~~~
qb45
Sometimes when I'm being very lazy writing C, I define a macro of this kind
which doesn't even take any error message argument but just prints line
number, textual representation of the code which failed and errno. Huge
debugging aid at negligible cost.

~~~
combatentropy
Oh, well, Apache and PHP give me this out of the box. So even though there is
not a great deal of custom error checking in my code as I said before, I
always have enough information in Apache's logs to track down the problem. I
guess with C and Go you have to roll your own.

------
tigershark
It's always bad. The second line makes you understand with a glance what it
does because it is declarative and explains _what_ is doing instead of _how_.
The go code is awful (personal opinion, although I _hope_ it is widely
accepted), much more verbose and it opens you up to a whole series of errors
that are just impossible in the python version. Boilerplate code by definition
is just duplication and it is always bad because opens you up to bugs in a
specific part of the boilerplate while you can't have any bug at all in any
boilerplate if you don't have any of it. Honestly I can't really see how
anyone can prefer code that is more verbose and bug-prone to code that is more
concise, clear and with less bugs.

~~~
userbinator
_Honestly I can 't really see how anyone can prefer code that is more verbose
and bug-prone to code that is more concise, clear and with less bugs._

I agree with you, but seeing the number of proponents of Enterprise Java there
are, it's clear that there are a significant number of programmers who prefer
verbosity and extra abstractions --- although IMHO not necessarily because
it's actually better.

In fact a trend I'm seeing with a lot of programming languages is more
verbosity and unnecessary abstraction, leading me to wonder whether
programmers actually know the languages they're using or are barely scraping
by with the very minimal basics (and in the case of some languages, a bit of
OOP cargo-cult dogmatism.)

Although I'm not really familiar with Python, and the order of the clauses its
conditional expression syntax is unusual and a little surprising, I think that
second line probably took longer to write than it did for me to read.

~~~
coldtea
> _I agree with you, but seeing the number of proponents of Enterprise Java
> there are, it 's clear that there are a significant number of programmers
> who prefer verbosity and extra abstractions --- although IMHO not
> necessarily because it's actually better._

Even Enterprise Java wasn't verbose for the sake of it (I say "wasn't" because
modern enterprise Java can be more like modern functional style than Go).

It was more verbose because it added tons of options and flexibility while
keeping with the OO style, so everything was pluggable and configurable and
factorable. But that's something else than Go style verbosity.

------
coldtea
> _In Python (and many other languages), it’s very easy to unintentionally
> hide tons of work in a single line of built-in syntax. Some lines do almost
> no work, other lines do a moderate amount of work, other lines do what I’d
> describe is tens of lines worth of work—a lot of work. It varies a lot, even
> in beginner code. In Go, I’ve found that this variability is much more
> diminished._

I call BS. Consider these 2 Go lines:

    
    
      heck_if_I_know_what_this_does()
      heck_if_I_know_what_this_does_either()
    

Just because Go has less powerful primitives, it doesn't mean that you don't
use opaque code, libs, etc. all the time. Sure, you can look into what each
function does -- but then again, you can also immediately tell that a list
comprehension in python is doing at least O(N) work compared to a simple
addition, without even having to open any other source file.

> _Is this good? I believe it is. I believe this code is more proportional to
> the complexity of the work that it’s doing._

We invented programming languages on top of machine code so that our code can
be LESS proportional to the "complexity of the work that it’s doing".

> _When reading proportional code, it’s easier to notice where the interesting
> bits are._

Not really. It's the inverse: the terse code only keeps the interesting bits.
The "proportional code" gives you all the non-interesting, and getting in the
way, mechanics.

> _When a friend was reading through the source code for ssh-chat, he was
> surprised that I implemented my own Set type. I explained that while Go
> doesn’t have a built-in Set, it’s just a few lines of boilerplate to make
> your own on top of a map. In fact, I noticed that my version of Set evolved
> to be fairly specific to how it was being used. In retrospect, I’m glad that
> I was tweaking my own implementation iteratively rather than spending time
> working around whichever limitations a generic library might have had._

Pray tell, what "limitations" a generic Set would have?

And why does those kinds of post remind one of Stockholm Syndrome?

~~~
taneq
> Just because Go has less powerful primitives, it doesn't mean that you don't
> use opaque code, libs, etc. all the time.

True, but that's not what the article's arguing.

> We invented programming languages on top of machine code so that our code
> can be LESS proportional to the "complexity of the work that it’s doing".

No, we invented programming languages so that our code can be smaller and less
arcane, not less proportional. The actual point of the article, which I
believe you missed, is that if we focus on 'cleverly' compressing large
amounts of logic into dense nuggets of code, it interferes with our ability to
appropriately factor the code so that it produces the desired behaviour in the
simplest and most readable way.

The article also states (and I agree) that if your code is wildly variable in
the amount of semantic content per line of text, it's much harder to properly
grok and (more importantly) to modify what's going on.

~~~
coldtea
> _No, we invented programming languages so that our code can be smaller and
> less arcane, not less proportional_

Smaller also means less proportional to the work that it's doing, since the
abstraction, sugar and techniques we use to make code smaller do not yield the
same compression ratio from the underlying instructions in all cases.

So, smaller = less proportional -- unless you suggest we should also strive to
artificially constrain how succinct we make our code so that there's a
constant ratio everywhere.

That is, so that 10 lines of code in a language always are (or at least,
always strive to be) 10×n times of machine code. I don't see how that's
feasible -- some abstraction could yield a 1/100 less code than machine code
in one line, while another can yield a mere 1/5 or 1/10 less code.

But even if we could achieve that, I don't see it being even useful. Having
higher level code strive to be more proportional to machine instructions is
not necessarily telling us much about the cost of such code. It might tell us
that it's complex and slower than another, but that doesn't mean much. It only
matters what role it plays in the overall runtime of our program. Just because
something A 5 times bigger than something else B, doesn't mean A needs more
optimizing. A could be just fine as it is, e.g. if A is called rarely, and B
is in some tight loop.

Gauging code by "code proportionality" (the only semi-legitimate use I could
think of such a feature) is bogus. We should always profile.

> _The article also states (and I agree) that if your code is wildly variable
> in the amount of semantic content per line of text, it 's much harder to
> properly grok and (more importantly) to modify what's going on._

On the contrary. The part of "What's going on" that matters when it comes for
modification is the high level stuff that goes on -- which a more terse code
shows better, as it doesn't hide it with boilerplate.

In the end, that point seems both trivial and non-sensical: yes, boilerplate
is easier to modify. That's because it doesn't matter much -- it's the stuff
that you want to do that's hidden inside the boilerplate that matters.

e.g. I can write a loop to filter 100 numbers in tons of ways. And I can
always modify it from one to one of the other ways.

But something like:

    
    
      value = filter(my_array, (val) => val > 5)
    

Just does what it says is does, and since it encapsulates the intention 100%
there's no much room for wiggling.

~~~
taneq
In general, we're less concerned about "the work that it's doing" in the sense
of raw processor instructions, and more concerned about the semantic
complexity for the programmer. So if a line says "set A to true if B or C are
true", that's simple. If it says "filter array A to only include elements
where this inlined lambda function returns 5 or more, and then return the
results with an even index" it's a bit harder to figure out the intent behind
it.

~~~
coldtea
> _If it says "filter array A to only include elements where this inlined
> lambda function returns 5 or more, and then return the results with an even
> index" it's a bit harder to figure out the intent behind it._

I'd argue the contrary though.

That the "semantic complexity" is less when you only keep intent-code, than
when you spread it over 20 lines of boilerplate which you have to visually
parse to get to the point.

Your examples are not analogous, because they do different things.

Let's make them implement the same example, Go/C/etc style and "terse" style:

    
    
      array_2 := make(int64, 0)
      over_five_index = 0
      for i, val := range(my_array) {
        if val >= 5 {
          over_five_index += 1
          if over_five_index % 2 == 0 {
            append(array_2, val) 
          }
        }
      }
    

vs:

    
    
      array_2 = my_array
                  .filter((val) => val >= 5)
                  .filter((_, i) => i % 2 == 0)
    

I don't see how there's less "semantic complexity" in the former for the
programmer to parse. It's just less _per line_ , but worse overall -- in that
he has to now reconstruct the full intention from all the individual lines.

As for the first example, it would be:

    
    
      a = b || c
    

in both approaches.

