
Please do not attempt to simplify this code - whalesalad
https://github.com/kubernetes/kubernetes/blob/ec2e767e59395376fa191d7c56a74f53936b7653/pkg/controller/volume/persistentvolume/pv_controller.go
======
Klathmon
I love this! It's the "jazz music" of software development. Something which
breaks all the "rules" but does so purposefully and explicitly so that it can
become better than the "rules" allow.

A naive look at this and my head is screaming that this file is way too big,
has way too many branches and nested if statements, has a lot of "pointless
comments" that just describe what the line or few lines around it is doing,
and has a lot of "logic" in the comments which could quickly become outdated
or wrong compared to the actual code.

Yet at the same time, it's probably a hell of a lot easier to maintain and
manage than splitting the logic up among tens or hundreds of files, it
contains a lot of the inherently complex work it's doing to this file, and it
is so well and heavily commented that it should be pretty easy to ensure that
any changes also keep the comments up to date (after all, any change without
changing the resulting comments should show up like a sore thumb, and will
most likely prompt the reviewer to look into it at the very least).

~~~
afarrell
> probably a hell of a lot easier to maintain and manage than splitting the
> logic up among tens or hundreds of files

I'm only halfway through John Ousterhout's book Philosophy of Software Design
but I think it agrees with you on this -- that smallness-of-file or smallness-
of-function is not a target to shoot for because it prevents the things you
build from being deep. That you should strive to build modules which have deep
functionality and small interfaces and should contain their complexity within
them so the users don't have to know that complexity.

~~~
TeMPOraL
I just finished his book yesterday; he has a lot to say about size and
comments. For size, your summary is spot-on. I'd only add that he notes
overeager splitting of methods and classes makes code involved in a particular
abstraction to be no longer in one place, leading developers to constantly
jump around files, which makes it more difficult to understand the code and
increases the chances of making bugs.

As for comments, this file is essentially Ousterhout taken to the extreme.
Still, I think he would have like it, given how critical this file is. In the
book, he encourages writing more comments than the current trends would
suggest, pointing out that you can't fully express abstractions in code, so
all the things the code doesn't contain - the high-level concepts, the
rationale, the caveats - should be documented in comments in appropriate
places.

Overall, I'm extremely impressed by the book, and its focus on reducing and
mitigating complexity.

~~~
lliamander
Martin Fowler of the Agile world, and Garret Smith of the Erlang community,
are both excellent programmers whom I respect, and they both take the approach
of breaking code into lots of extremely small functions.

Having tried that style, I notice that I don't particularly favor it, and for
the very reason you site: the code is no longer all in one place.

I've switched to moderately sized methods/functions with comments every few
lines. Some say that comments like this are a smell, and that you should
refactor the commented section of code into it's own function, but honestly
comments are easier to read than method names (and again, there's the benefit
of locality).

I'll have to take a look at the book.

~~~
BurningFrog
I assume everyone who splits code into smaller pieces use modern IDEs that
makes it trivial to navigate to functions by clicking them etc.

I say this because I'm always astonished by the number of "modern" programmers
who refuse to use IDEs.

~~~
TeMPOraL
> _I assume everyone who splits code into smaller pieces use modern IDEs that
> makes it trivial to navigate to functions by clicking them etc._

That's... not the point. Jumping around is. Imagine reading this comment
thread on a bizarro-HN, where you only get to see a short camelCased summary
like: debunk(this.previousComment), and have to click to open each comment in
a new tab. This is how jumping around small functions feel.

> _I say this because I 'm always astonished by the number of "modern"
> programmers who refuse to use IDEs._

There are reasons for it. Many languages don't have an IDE. Many are not
suitable for one (especially ones closer to Lisp on expressiveness spectrum).
IDEs are heavy and often mouse-oriented, and not efficient for reading and
editing text. Sometimes (read: Java) they are a crutch to work around the
expressive deficiencies of the language.

Mind you, I have nothing but good things to say about IntelliJ. I've spent a
lot of time in it even recently, and I pick it up any time I have to do
anything in Java. But for everything else, I launch Emacs, because it can
handle all other languages well, and has superior editing capabilities.

~~~
jonahx
> That's... not the point. Jumping around is.

There is a tradeoff: Small functions make high-level logic clearly visible and
easy to find, at the price of forcing you to jump around when you want to dive
into implementation details. Putting everything into one big function lets you
follow all the implementation details without jumping, at the price of making
you read everything to actually _understand what the code is doing_.

The latter is the biggest price you can possibly make me pay. Jumping around
is a minor inconvenience.

~~~
spockz
By choosing the right names for a function or class a lot of jumping around
can be prevented. Most IDEs also have a feature (including key combination) to
show the documentation of the method/function. The only reason that remains is
when you doubt the correctness of the method and need to look at its
definition.

~~~
AstralStorm
The only and _critical_ reason - skipping the check will net you tons of
debugging all the time because hidden assumptions are often not documented.
(or otherwise visible)

------
pdkl95
Ignoring the initial boilerplate (license, imports) and the request to
preserve the verbose ("space shuttle") style, the first line is:

    
    
        // Design:
        //
        // [... 4 paragraphs of English prose
        //      explaining goals and intent... ]
    

That's _exactly_ the type of comment that should be at the beginning of most
files!

~~~
jml7c5
As a novice programmer, I was absolutely stunned that this was not standard
practice. A typical source file provides zero context, background on the
subject, pointers to reference material/blog posts/books explaining the
concepts, information on how it fits into the program's 'bigger picture', or
(most importantly) the thought process that resulted in the file (i.e., why
the choice was made to do _this_ rather than _that_, challenges faced, trade-
offs, etc.).

It still baffles me. Every programmer has to start from scratch dealing with a
new codebase, and it makes improving any non-trivial program impossible unless
one is willing to spend hours of archaeological examination. To open-source
developers: if you want to get people contributing to a project (and make
everyone's effort much more enjoyable!), these sorts of comments are
essential. Not to mention they'll save everyone boatloads of time; it's a
shame that every programmer has to piece together knowledge from scratch,
rather than being 'tutored' by their peers' comments.

~~~
hibikir
There's plenty of good reasons to not write 95% of code with big walls of
explanation. The first is a matter of cost: Writing a good explanation around
everything is very expensive to do at first. A whole lot of the custom code
you find in random companies, from the shiny SV startup to the old enterprise,
is unimportant, cobbled together pieces. We have no idea of whether we are
writing code that will be thrown away in a week, a month, a year or whether it
will last two decades. Context can change fast enough that the comments become
worse than useless, as the terminology might have changed, or had been
misguided in the first place, turning the long comments into outright
unintended deception. This gets even worse as we do not evaluate all the
comments in all the files whenever there's a code change: It's crazy how a
large comment block in one place can become harmful after it's forgotten, and
someone else makes a correct, business critical change in another file. No
matter where I am working, it's rare for me to not find multiple examples
every year where the code and the comments provide very different impressions
of what is going on, and it's the code that is accurate.

This is not to say that there aren't reasons to write large comment blocks, or
architecture documents, but that they are often better written not while the
system is being first built, but later, in a maintenance cycle, when someone
already had wished for the comments, and has regained the knowledge the hard
way. By then, it's clear which part of the system are dangerous, suspicious
and unclear. Where there's more need for high quality error handling, and
where thousands of lines of error handling never get hit, because the failing
case that was originally considered doesn't really happen in this dimension
anymore.

Writing code so that someone, even a future you, can pick it back up and
improve it when it's needed, while still delivering the code at a good pace is
a kind of skill that many just don't learn, either because they are always in
greenfield teams that never pay for their mistakes, or have an approach to
maintenance that involves not becoming intimate familiar with a system, and
instead either hack or rewrite.

But nobody looks great in a resume by saying that they are a specialist in
software maintenance.

~~~
TeMPOraL
> _This is not to say that there aren 't reasons to write large comment
> blocks, or architecture documents, but that they are often better written
> not while the system is being first built, but later, in a maintenance
> cycle, when someone already had wished for the comments, and has regained
> the knowledge the hard way._

I don't think the "Lean Manufacturing" approach works here. By the time
someone "pulls" you for a comment, you've already lost the most important
knowledge that should go into the comments - _why_ the code is the way it is.
Maybe you'll recall it when asked, hopefully not missing anything crucial.
Meanwhile, comments are extremely cheap to write as you're writing the code,
and even before you're writing the code (you did spend the time thinking about
what you'll write, and aren't just "coding from the hip", right?).

~~~
vthriller
Commit logs are also cheap to write, and it's easier for people to realize
that whatever you read in "$vcs log" or "$vcs blame" might be severely
outdated.

------
nickharr
Having spent 25+ years writing, viewing, commenting on and reviewing code in a
multitude of languages, this is good stuff to see - regardless of the 'style'
of programming (or the language broadly-speaking).

Stepping back and whilst we can all overlook it, good code comments can make
an enormous difference in productivity - both for an individual, a team and
indeed a business. It aids repository knowledge (something that is easily lost
between current and prior teams/individuals), which shouldn't be mistaken for
the intelligence of someone looking at code...

I've spent far too much time personally and otherwise attempting to reverse-
engineer code written by someone with no comments or explanation. At times,
super experienced programmers/developers will take performance-enhancing
shortcuts that the less-experienced don't understand; They'll compress
routines and functions that are a result of their explicit knowledge of a
language and/or domain but without explanation...

On a basic level, comments should: inform, educate, outline and help others
understand the sometimes complex routines and functions that we all create and
often under an enormous amount of pressure.

There are those that believe good code shouldn't need explanation and to
_some_ degree that's true, but you can't apply that brush to every codebase.
Code can become complex, awkward, spaghetti-like and almost unfathomable at
times.

I've always strived to teach less experienced developers to comment well,
efficiently and with a little humor/humour (where possible); Something that
allows us to understand code quickly, appreciate the efforts of those before
us and smile/grin at the complexity of a challenge.

Personally, I don't really care about the code/comment ratio - It's a complete
red herring. At times, code comments can be worth more than the code itself.
At other times, they just help you get your job done; quickly, efficiently, no
fuss, just great code.

~~~
lmm
> There are those that believe good code shouldn't need explanation and to
> some degree that's true, but you can't apply that brush to every codebase.
> Code can become complex, awkward, spaghetti-like and almost unfathomable at
> times.

I have yet to find a codebase that couldn't be made clear as soon as a
programmer actually put some effort into doing so. Far too often adding a
comment is used as an excuse to give up on making the code readable before
you've even started.

~~~
xixixao
Here’s a great practice:

1\. Write some piece of code

2\. Now write a comment about it

3\. Is the comment adding more information, making the code more clear?

If Yes: Put that information into the code. Rename variables. Pull out code
into subroutines.

If No: Delete the comment.

You’ll be amazed at how often this practice works. Doing it all the time will
make your code more readable.

We have a second, enforced practice at work, thanks to code review. The
question in your head is simply: “Am I gonna get a comment about this at
review time?” If yes, you gotta make the code clearer/simpler/better. Because
you’ll have to answer and address the comment, and that’s just gonna slow you
down more than if you just fix the problem now.

~~~
pas
There are concerns and aspects of software engineering that are important to
document but don't sit well in code, but they become important when reading /
maintaining that code. Design decisions (implementation details),
specification details, other design or engineering constraints. And of course
business modeling fundamentals and constraints.

Sure, in theory, you can do everything in code, but I find that usually this
trade off is taken, as there's no time/budget to go that 80% extra time. (Or
however the Pareto curve looks for the actual problem. And usually it's not
less than 80, but more.)

~~~
nickharr
I would never suggest documentation in code alone... Your point is very much
noted, echoed and a hat-tip to you here. Yes, documentation is incredibly
important, and it should be used as an driver/accompaniment along with
comments.

------
dajonker
"it became clear that we needed to ensure that every single condition was
handled and accounted for in the code"

This is a feature of several (mostly functional) programming languages, e.g.
Haskell. Fun to see that often people figure out that these types of concepts
are a smart way to write your code. Too bad it usually means many people
reinvent the wheel instead of learning about computer science history and
other languages.

~~~
endymi0n
I know a business coach who regularly asks his audience "Who here makes better
burgers than McDonalds?". When half the audience raises their hand, he asks
them why they don't outsell this giant company.

Functional programming advocats, especially for the "pure" ones like Haskell,
always strike me as odd. It seems that all the beauty of those languages make
people obsess over that beauty and purity while keeping them from being
productive.

Meanwhile, people with simpler languages like Go just get stuff done that is
useful and makes people happy. Now if I _ever_ came across a useful Haskell
product, I'd be happy to test drive it, shouldn't be a problem by now with all
the container technology. But the closest I ever came to using a functionally
developed product was RabbitMQ (written in Erlang). That one was _such_ a pain
to use and operate — must have been the developers still dreaming in the
purity of its code instead of writing some installation docs. I moved on to
Kafka later and didn't regret it a minute.

Rant off.

~~~
iamwil
Selling a lot of burgers encompasses much more than making good burgers. By
the same token, good products entails much more than making a programming
language choice.

Functional programming, at its heart, is about using self-imposed constraints
to avoid certain classes of programming mistakes.

If your application domain doesn't have big consequences for these classes of
programming mistakes, then it can seem like functional purity can be a luxury
or frivolous. However, if your application domain suffers greatly from those
classes of programming mistakes, such as distributed systems, then it may be
worth it to consider what functional programming might buy you.

So yes, just because you use a functional programming language won't help you
sell your widgets or make a great product. And you can spend lots of time
fucking around with it for its own sake and still not sell widgets or make a
great product. However, if you understand what its constraints buys you, then
you can make your job of building these things in certain domains much easier.

~~~
bb88
> So yes, just because you use a functional programming language won't help
> you sell your widgets or make a great product. And you can spend lots of
> time fucking around with it for its own sake and still not sell widgets or
> make a great product.

It comes down to trust. You're either fucking with your code in a powerful
programming language that lets you do everything, or your fucking with the
language restrictions to get your code to compile in the first place.

You can either go eat at McD's which sells pre-cooked burgers from minimum
wage employees which kills the flavor and the taste of the meat being served
but is extremely safe, or you can go to an upscale burger joint where artisans
grind their meat in house and cook it to a perfect medium rare.

These days, I prefer the latter.

~~~
Tehnix
> It comes down to trust. You're either fucking with your code in a powerful
> programming language that lets you do everything, or your fucking with the
> language restrictions to get your code to compile in the first place.

> ...or your fucking with the language restrictions to get your code to
> compile in the first place

I think that's a wrong and outdated view on strong type systems.

A good type system is also an ergonomic one. What people start to experience
is that the compiler is actually a friend that helps you write code and keep
yourself true to your own promises. When new people start Elm, PureScript or
Haskell (or another lang with ADTs and Type Inference), they might be a bit
overwhelmed with the paradigm shift, if coming elsewhere, but if you are new
to programming, there's nothing inherently more difficult in Haskell than in
other languages—the cost of wrong code is just apparent earlier.

It doesn't come down to trust, it comes down to the realization that your mind
can keep track of less information than a computer, and that you, as a
programmer, are forgetful and make mistakes. The compiler is there to help you
when you stumble over your own feet.

NOTE: I'm only including strongly, statically typed languages with type
inference in the above. "FP langs" by itself is far too broad to be a useful
categorization.

~~~
weberc2
Even experiences FP folks complain about how difficult it is to program in
Haskell due to its purity. Further, the high barrier to entry you describe is
an even bigger deal to organizations than to individuals. It’s a cost that
needs to be paid for every employee. And it’s not just the language features,
but FP languages tend to have issues like poor documentation, poor editor
integrations, multiple standard libraries, multiple build tools, multiple
string types, home-grown project file syntax, multiple preludes, many
extensions, etc. All of these boost the learning curve in addition to the
issues with the language itself.

~~~
allemagne
In my limited experience with functional programming, I've come away with the
impression that the problems you're describing are why you don't see a lot of
companies that use FP exclusively.

But I think the advantages that OP is lauding are also there, and "space
shuttle code" might just be where it shines. Reading that comment in this post
made me immediately think of Haskell. The Clojure components at my company
fits the "space shuttle" description of importance, and its dependability is
striking compared to the rest of our code. Part of it may be that being
written in a different language allows its concerns to be separate from the
rest of the application too, but I do think the FP paradigm is simply good in
this domain.

~~~
weberc2
Sure, but I doubt all of Kubernetes is written in this style, so it’s probably
not worth writing everything in Haskell. Note also that there’s nothing about
FP that prohibits it from addressing the aforementioned practical problems.
Some Haskell-like could swoop in and totally steal Go’s lunch if they would
simply prioritize practicality over experimentation.

~~~
bb88
> Some Haskell-like could swoop in and totally steal Go’s lunch if they would
> simply prioritize practicality over experimentation.

I don't know. It seems like it would be easy enough to build an AST to make
sure there were no unknown conditions that didn't lead to a return statement.

~~~
weberc2
I don’t understand how your post relates to mine. I was saying that if a
static functional language focused more on simplicity, readability, good
tooling and documentation, etc then it would eat Go’s lunch _without trading
off any of Haskell’s important characteristics_ (I.e., robust type safety).

------
spullara
My take away from reading this code is that it is a huge mess that may be
impossible to clean up. At some point they failed to introduce abstractions
that would remove the need for all this complexity. They are probably right
that now that it works that it will be hard to refactor it without leaving out
some critical case. However, I pity anyone that works on this code base.

~~~
austincheney
Abstractions don't remove complexity. Abstractions instead hide the appearance
of complexity behind layers of ever increasing code.

~~~
fluffycat
I believe that premature abstraction is terrible. But in some cases good
abstractions may help with handling complexity and may make testing easier. I
have a feeling that because authors say that this class should not be changed
/ refactored, they failed to introduce a good abstraction. This also implies
that their tests are inadequate.

~~~
austincheney
I agree with the direction of your opinion, but would say it isn't a matter of
helpfulness. In some cases is it is a necessity.

It must be understood that complexity is an economic consideration and not
either a financial or technology consideration. It is always already present.
Complexity is not something that is created or destroy; but retained,
absorbed, or transferred.

Abstractions become necessary when they separate different functional layers
to perform different respective responsibilities. In this case complexity is
transferred both in and out of a system at a given layer, but you don't care
so long as you aren't doing the jobs of the other layers. That is referred to
as separation of concerns which results in the hardening of a system (risk
reduction) which is the side effect of reducing costs due to restricting
requirements available.

Many abstractions exist solely to provide a layer of convenience. In the case
where an abstraction does the same job as the code it abstracts risks increase
and costs increase. This is because the system continues to simultaneously
absorb and transfer complexity like described above, but the requirements
between the various layers isn't clearly separated. That results in fulfilling
requirements, the same requirements, simultaneously at various layers. This
has various names like _scope creep_ , _technical debt_ , and so forth. This
is bad because risks and costs increase directly to the correlation of
increased code and increased requirements. This is what makes the _law of
leaky abstractions_ valid.

It is easy to tell the difference between a necessary abstraction and a
wasteful abstraction by the forcefulness of separation. If you can perform the
same job in a lower level the abstraction isn't necessary and you are probably
better off without it. Most JavaScript frameworks are unnecessary
abstractions.

------
_ph_
As much as I like the style where the code logic is commented very thoroughly,
it also falls in the trap of comments no longer matching the current code, I
assume some variable renaming happened. In the function (line 320 of
[https://github.com/kubernetes/kubernetes/blob/ec2e767e593953...](https://github.com/kubernetes/kubernetes/blob/ec2e767e59395376fa191d7c56a74f53936b7653/pkg/controller/volume/persistentvolume/pv_controller.go))

    
    
       func (ctrl *PersistentVolumeController) syncUnboundClaim(claim *v1.PersistentVolumeClaim) error {
    

the variable "pvc" seems to have been renamed into "claim" and "pv" into
"volume", judging from the code/comment mismatch. Comments in the lines 339,
358, 360, 370, 380, 395, 411, 422, 427 point to the old names. Furthermore in
line 370 the comment reads:

    
    
       } else /* pvc.Spec.VolumeName != nil */ {
    

while the matching if is:

    
    
      if claim.Spec.VolumeName == "" {
    

So not only the variable name mismatches, but also the comment is wrong. The
VolumeName seems to be a string, so is never nil, the else comment should
specify that the VolumeName is non-empty.

The more verbose and detailled the comments are - the more work needs to be
spent in ensuring that they are correct.

~~~
kyberias
Yes and that actually PROVES why this ultra-commenting mentality is crazy.

------
boramalper
> Space shuttle style is meant to ensure that every branch and condition is
> considered and accounted for…

FTFY: …hopefully!

Only if they’ve used a language with Algebraic Data Types support, the
compiler would enforce that “every branch and condition is considered and
accounted for.” The only PL with ADT that I’ve used was Haskell, but I’ve
heard that Rust has them too “enums”.

People are arguing that “code is what computer executes, comments don’t ensure
anything!” and so on, but besides being executed, (this Go) code does not
ensure anything either. It’s human eyes that skim through all the cases, and
look for a matching branch for every one of them that “ensures.”

In my humble opinion, this so-called “space shuttle style” is just one of the
many workarounds to deal with Go’s by-design limitations (the most famous one
being lack of generics), a language that’s designed only 9 years ago.

~~~
jonahx
> It’s human eyes that skim through all the cases, and look for a matching
> branch for every one of them that “ensures.”

This a thousand times. The praise in this thread is disturbing.

The absurdity of this code is the logical conclusion of ignoring decades of PL
advances in favor of Go's "simplicity."

When you insist on "space shuttle" era language design, is it any surprise
when you're reduced to "space shuttle" era programming? I can't imagine
anything more fitting.

~~~
hellofunk
You are forgetting the tremendous advantages that Go offers for many projects
over alternative languages.

There are no perfect languages, that is certain.

------
hannofcart
I see a lot of comments mentioning various versions of the following:

\- "It's the "jazz music" of software development."

\- "...breaks all the "rules" but does so purposefully..."

\- "this is irreducibly complex, and cannot be split into multiple files"

\- "that smallness-of-file or smallness-of-function is not a target to shoot
for"

I am wondering: can't all the above statements be said in defence of _any_
poorly engineered, gargantuan single page code?

~~~
pknopf
Can everyone play jazz?

No.

~~~
fogetti
Can everyone claim that their terrible music is jazz music?

Yes.

~~~
pknopf
Apart from a comedy bit, can everyone easily spot the difference?

Yes.

~~~
kstenerud
No. Most jazz, including by the jazz greats, just sounds plain terrible. The
worst is jam sessions, where everyone steps over each other but it sounds not
abysmal at times because they're in the same key.

------
thothamon
This code reminds me of why ML-like languages with Maybe-style types and case
expressions that generate compiler errors for missed alternatives are good. I
bet rewriting this in OCaml or Haskell would lead to tighter code and might
even unearth a couple possible states that haven't been accounted for.

~~~
woolvalley
You don't really need functional languages to have those features, as swift,
kotlin and others show.

~~~
lmm
Those languages have all of the ML features; they are functional languages in
every reasonable sense, unless you consider "functional" to mean the absence
of something.

------
userbinator
If you want to see actual code that flew to the Moon:
[https://github.com/chrislgarry/Apollo-11](https://github.com/chrislgarry/Apollo-11)

~~~
luhn
My favorite part from that:
[https://github.com/chrislgarry/Apollo-11/blob/27e2acf88a6345...](https://github.com/chrislgarry/Apollo-11/blob/27e2acf88a6345e2b1064c8b006a154363937050/Luminary099/LUNAR_LANDING_GUIDANCE_EQUATIONS.agc#L179)

~~~
booleandilemma
I also like the “trashy little subroutines” comment.

------
cultofmetatron
This is basically the style that rust sort of forces you to write in with
allowing shorter forms where the compiler can automate checking that all error
states are accounted for.

~~~
nixpulvis
And with the beauty of pattern matching in Rust, it's usually very painless.

------
startupdiscuss
The comment:code ratio is higher than anything I write or that I’ve seen.

However, it does give me some comfort. When it’s not gamed, do other HNers
also feel that a high comment:code ratio probably indicates quality?

There are reasons why this may be the case. (More thought, more time and a
large team etc)

I don’t advocate using this measure to reward anyone because it would be gamed
immediately.

~~~
bdibs
I actually would say it’s almost the opposite, if you’re writing clean,
expressive code it shouldn’t need explaining.

And if your code is clean, you shouldn’t have a bunch of redundant comments
explaining the obvious.

~~~
geofft
I'm not sure there are many cases where there should be _long_ amounts of
expressive code.

If you're doing something obvious, you should generally be able to program it
concisely, in which case you have a high comment-to-code ratio because the
amount of code is low. Sometimes this will be because you're importing an
external library to do something, or because you're calling out to an internal
library. Sometimes this will be because you found a straightforward
implementation. If you're finding yourself writing hundreds of lines of code
to do a _single_ obvious task then chances are high you're implementing it
poorly (and, specifically, in a way where your defect rate is likely
proportional to the number of lines of code).

And if you're doing several obvious things, then the point of the code is not
to explain what the code is doing, but _why_ it's doing that. What is the
business purpose of the code? Which customer cares about this edge case that
you're handling, and under what circumstances can you stop handling it? Why
did you decide that the common library wouldn't actually work here? If you're
converting data from an awful legacy format, why are your ingesters / parsers
for the legacy format designed in this way? If you're micro-optimizing for
performance, why are the optimizations sound (i.e., why do they accomplish the
same thing as the unoptimized version), how do they work, and why did you
decide these spots need to be optimized? Each individual thing you do might be
obvious on its own, but the arrangement of the whole thing needs comments for
each step, which again gives you a high comment-to-code ratio.

~~~
bdibs
I might have misused the word expressive, I don't mean bloated code with more
than necessary logic.

I just meant simple to understand variable, function, and class names. That
combined with small classes and functions, makes following the logic of your
program extremely easy.

Following concepts like DRY (don't repeat yourself) and the single
responsibility principle ensure that you're making more easily testable code,
and I'm sure less overall LOC.

------
apo
One simplification that might be tempting is to replace the "if !condition"
with "if condition".

For example, line 463 shows:

    
    
      if !found {
        // handle missing
      } else {
        // handle found
      }
    

I would simplify this to:

    
    
      if found {
        // handle found
      } else {
        // handle not found
      }
    

Or even:

    
    
      if missing {
        // handle missing
      } else {
        // handle not missing
      }
    

The test-negative style is repeated throughout the file, but inconsistently.
Sometimes the negative is tested in the if branch, sometimes it's tested in
the else branch. Why?

~~~
gtrubetskoy
My guess is that this is in line with the Go tradition of first handling all
the odd cases leaving the essential part of what the function (or block of
code in this case) attempts to do as the last part. So "if !found" to me
suggests that this is more of an exception than the rule and this is why the
code is written so as to deal with it first.

------
yongjik
Obviously I'm not the intended audience, but I'm not sure it's wise to have
CloudVolumeCreatedForClaimNamespaceTag, CloudVolumeCreatedForClaimNameTag, and
CloudVolumeCreatedForVolumeNameTag in the same file.

This is almost the worst of both worlds: the trouble of wading through a pile
of words, together with the lack of clarity.

~~~
Etheryte
While I understand your sentiment, I don't think it's a real issue with pretty
much any modern IDE.

~~~
tonyedgecombe
I could imagine quickly scrolling and finding yourself editing code in the
wrong function.

------
unethical_ban
I can't say I've written overly branchy code, but I have become a fan of
leaning on the side of "verbose" regarding comments in code - specifically in
nested if statements. So many in the Ops area, "just" writing scripts for
themselves ( that the rest of the team starts depending on) will just pound
out a script that works, with no notes on its function, design or
requirements.

15 months later, they moved to a less stressful team, the app is not
functioning because the servers got upgraded, and you wonder how the hell this
thing works. And if it doesn't work, you have 60 hours of redevelopment to do.

------
Xyik
Love this thread. I see this a lot, where engineers blindly follow best
practices and have urges to re-factor code when its not necessary. Big files
are not necessarily bad and I love that a lot of the comments are with me on
this. Having to open several tabs and remembering where you are in the stack
can be hard once there are more than a couple of frames / function calls in.
There is a lot of benefit to keeping logic in 1 file or 1 function, and there
is a time and a place for writing really granular DRY code. As with all
engineering, there are always trade-offs to every decision and I think its
about time we put to rest some of the traditional rules of thumbs and 'code
smells' new engineers learn and adhere to like a bible.

~~~
jonahx
> As with all engineering, there are always trade-offs to every decision

This is the cliche that needs to be put to rest.

Yes, often there are tradeoffs. But just as often one thing is better than
another thing, and there is no tradeoff.

A worldview in which _everything_ has pros and cons and is ultimately
subjective is fertile ground for entrenched habits, because it means never
having to admit you're plain wrong, that there is a better way, or that other
approaches are simply that much better than yours.

~~~
Xyik
I believe code design is ultimately subjective. Unlike other metrics like
performance which can be easily and definitively measured, you can't easily
measure 'good' code. The definition of 'good' changes based on the context of
the code base and function the code is trying to achieve. In this case,
choices like having giant functions and files is definitely subjective.

------
germainelong
The comment claim that every branch is accounted for and yet few functions
below you can see this is certainly not the case. They should either have
fixed it first and then make such comment or shouldn't make such comment at
all. Otherwise this looks a bit cringey.

~~~
geofft
It says "(exception: simple error checks for a client API call)" and that
seems accurate to me.

In particular Go (like C) has no built-in exception "throwing" / unwinding
support, so for any function call where you want to pass an error onto the
caller, you need to do something like

    
    
        result, err := try_to_get_a_result()
        if err != nil {
            return nil, err
        }
    

See also [https://blog.golang.org/error-handling-and-
go](https://blog.golang.org/error-handling-and-go) . As far as I can tell, all
of the if-statements without else-clauses are doing just this.

(It would be nice in theory if there were better language support for making
this lexically obvious but they're in a language where that's not doable.)

~~~
germainelong
Is someone not familiar with the code competent enough to decide what is a
"simple error check" and not a bug? This is very weak as they suggest that
even the branches that would result in no-op are accounted for. So if someone
introduce a code with a branch that is unaccounted for that automatically
means the code is either faulty or is a "simple error check". With something
supposedly trying to be a space shuttle worthy code the lack of definition of
"simple error check" is very worrying. Would I want this code to fly me to the
moon? I'd be wary.

~~~
geofft
I _think_ you can syntactically state that anything where the check is on the
second return value (which is, by convention, the error return) is a "simple
error check", and their rule for if statements is always for things that come
from a first return value.

For instance, this would not be a simple error check:

    
    
        server, err := find_current_server()
        if server != nil {
            ...
        }
    

because if find_current_server() believes that it's a non-exceptional case
that there might be no server at all (i.e., it might return nil, nil instead
of nil and an error), then you absolutely want to handle that case.

~~~
germainelong
What if the second returned variable is not err, but the code using it assumes
it is? (the code breaks the convention) This will not be accounted for. This
means with that in mind a lot more discipline must be used to analyse the code
that is being used in that module.

~~~
geofft
I think that is highly unusual in Go (but someone who actively uses Go would
have to correct me).

Besides, this syntactic rule is not implemented by code but by the author and
reviewer, who should know what the function returns. (And shouldn't name it
"err", then.) They're doing the best they can in a language without syntactic
support for what they want, I think.

------
nradov
For background on how actual Space Shuttle flight control software was written
I recommend reading the "NASA Manager's Handbook for Software Development". It
contains some great guidelines for writing safety-critical software. AFAIK, no
Space Shuttle mission ever suffered a serious safety incident due to a
software defect.

[https://ntrs.nasa.gov/search.jsp?R=19910006460](https://ntrs.nasa.gov/search.jsp?R=19910006460)

~~~
dustycat
Thank you for the link. Could people who have studied it answer whether it is
similar to the code here?

------
emmanueloga_
This looks like an example of why I'm trying to learn more about model driven
development these days.

There's probably a lot of wisdom in this file embedded in a lot of noisy Go
code. If someone tries to implement this code in a different programming
language, a lot of this wisdom would have to be painfully extracted from the
Go code. Some sort of extracted decision table format (examples [1]) would be
objectively easier to read, could be rendered in different formats,
hyperlinked, etc. Maybe a small subsection of this code could be generated
from some structured data specification.

Mbeddr [2] is an example of a language that allows embedding decision tables
directly in C99. It can also embed state machines and other goodies directly
into the code. This is just one way to go... I'm sure some ppl would complain
about lock-in in a specific IDE.

It is not clear to me exactly which domain driven techniques could be applied
to this particular piece of Go code, but it could be worth it _to find that
out_.

From what I see out there model driven design seems to be applied to areas
were really convoluted, and some times nonsensical logic needs to be mapped to
code, like in insurance policy management, and stuff like that, or for other
complicated code like firmware, drivers, protocols, etc. Why can't we apply
these techniques to general programming problems?

1: [https://medium.com/@markusvoelter/the-evolution-of-
decision-...](https://medium.com/@markusvoelter/the-evolution-of-decision-
tables-80ce77bf984c)

2: [http://mbeddr.com/index.html](http://mbeddr.com/index.html)

------
maxander
Literate programming [0] is frequently re-invented, in various forms, because
it is simply the right way to do things- and each iteration of it is almost
immediately discarded, because we're all sinful and impatient creatures.

[0]
[https://en.wikipedia.org/wiki/Literate_programming](https://en.wikipedia.org/wiki/Literate_programming)

------
dmix
A code style with lots of verbose if statements? Go is the perfect language!

More seriously I’ve done this style unintentionally before in JavaScript for a
similar situation with plenty of branching logic. Stuff that easily could have
been have the lines but not as easy to verify by hand. I’m happy to see it
being formalized for certain scenarios.

------
Groxx

        if claim.Spec.VolumeName == "" {
            // ...
        } else /* pvc.Spec.VolumeName != nil */ {
    

maybe some cleanup actually would be a good thing.

------
vips7L
HN: this is amazing! Everyone should be writing code like this!

Also HN: c++ and java are awful! They're so verbose and I have to type so
much!

~~~
yawaworhtttt
What if I actually wanted the ability to get a thing going and quickly working
within a few lines, while _also_ being able to spell things out as needed when
the use case arises? Crazy I know.

------
boomlinde

        // 1.  Every 'if' statement has a matching 'else' (exception: simple error
        //     checks for a client API call)
    

Comment rot? The code is filled currently with else-less if statements, not
only for error checking.

~~~
fogetti
Obviously there are many people on HN that consider that to be a good thing. I
personally agree, this is the typical comment got. Only obviously blinded
people wouldn't see this.

~~~
boomlinde
I don't think that anyone here believes that comment rot is a good thing, or
that they are particularly "blinded". I just think that the fact that the
comments lie is a compelling argument against the prose comment-heavy style
used in this file. In this case it was not a big one because it's trivial to
determine that the comment is not true, but in general I think that no comment
is better than the wrong comment.

I have not cared to read the file much further so I can't comment on whether
comment rot is really prevalent.

Actual "space shuttle code" is AFAIK built to satisfy a specification, not to
serve as a specification in itself. When you find that the specification is
lacking or somehow faulty, you get the people responsible for it to revise it,
wait for the new revision and adjust the code to match. The key is that they
are two separate processes. Someone responsible for the specification doesn't
need to be concerned with the exact details of the implementation, and those
responsible for the implementation only need to be concerned with satisfying
the specification.

When the specification and implementation is all mixed up as prose and code in
the same file you don't get to enjoy the benefits of separating those
concerns. Changing the spec is the same process as changing the code, and the
order of change is indistinguishable. You can change some code, but then you
have to find all the references to the code you changed in the prose and
adjust that too. You can change the prose, but then you have to adjust the
code accordingly and any prose that assumed that the code worked as before.
You'll be relying on tests and reviews (reviewers suffering the same drawbacks
as the submitter) to enforce this, which IMO isn't not necessarily a bad
thing, but not exactly rigorous enough to give a "space shuttle" stamp.

------
tomphoolery
> Space shuttle style is meant to ensure that every branch and condition is
> considered and accounted for

I thought this was what you were supposed to do. Not that I do it every time,
because I'm lazy, but Zed Shaw used to say every `if` conditional should have
a corresponding `else` (which can also take the form of guard clauses). In
Ruby, this is really easy to do because every method has to return something,
so you build "returning nil or some String" as a concept into your program.
There's a whole discussion around whether _that_ is a good idea, but I
digress...going through the motions of enumerating every possible condition
for a given piece of logic is not a bad exercise, and can result in very
robust code at the expense of the code looking a bit more confusing than
necessary.

We have a very similarly-written piece of code to this in our eCommerce
platform. This code, written in Ruby, is used to calculate prices of discounts
with regards to discount compatibility. There's a big warning atop the method
stating something like "Please don't decorate/override this unless you
ABSOLUTELY need to, the consequences could be very difficult to debug!". Not
the easiest piece of code to look at, but it gets the job done in an efficient
way without having to rely on C extensions for performance. We were also
focused on correctness here, because calculating the wrong discount group
could result in zero or even negative order totals, which our clients would
_not_ be happy about. So the code is very verbosely written, isn't optimized
for legibility, and strictly specifies both if and else sides of each
conditional.

------
quotemstr
This kind of pattern indicates that the underlying language has inadequate
metaprogramming facilities. You should be able to _robustly_ verify certain
guarantees via the type system instead of relying on a specific code
formatting convention to do it.

------
thegabez
DRY is one of the most overly used of the software adages. It more often leads
to premature abstraction and overly complex code. My personal preference is
explicit code.

------
munk-a
This style of code with an emphasis on branch completeness is the biggest
virtue Go gets from the lack of exceptions.

I personally dislike that style since that level of detail seems extraneous
for normal tasks, but when you need to write code with absolute assurances
like this then preventing the stack from being unwound without an explicit
return (or panic, because some stuff is just terrible) is quite helpful to
inspect and insure you have full coverage.

~~~
Ericson2314
Rust's Result or Haskell's Either are wholely better for this purpose.

------
taeric
I've already seen a few places where this is compared to literate style
programming. I agree and can see the comparison. However, a main draw of
literate style, as used by most tools that support it, is precisely that it
lets you bend the code around a narrative. Instead, this is having to fit a
narrative to the code.

As an example of what I mean, look at some places where the if chunk is as big
as the else chunk. By the time you are at the else, you might have to scroll
back a ton to get to what it is you are in the else of.

Now, yes, to an extent, you could split this between functions. Such that you
could have:

    
    
        if foo {
          doSomething
        } else {
          doSomethingElse
        }
    

Literate style, though, would be more akin to

    
    
        if foo {
          <<since we have a foo, we can ...>>
        } else {
          <<Without a foo, we have to  ...>>
        }
    

This is somewhat subtle, but not having to define everything in terms of
functions with inputs and single outputs can free certain parts of your code
to just dealing with the core logic that you care to explain.

Not a panacea, but can be quite powerful.

------
zerr
Seems like a horrible spaghetti code to me - and the verbose commenting
doesn't help.

------
iainmerrick
This looks like pretty normal code. What is all the “space shuttle” stuff
about?

The only unusual things I see are a) 8-space indentation, b) lots of “if err {
return nil }”, and c) lots of comments.

Aren’t a) and b) standard for Go? How else is it possible to write Go code?

With less verbose error handling and 2- or 4- space indentation it wouldn’t
look especially “branchy”.

~~~
tcheard
It will actually be tabs, which is standard for Go (as per gofmt), GitHub's
default tab width is 8 spaces.

------
mrdoops
Pattern matching would be nice in this scenario. Would help with isolating
responsibility by breaking out the if statements into functions responsible
only for the case they know to handle. The additional opportunities to name
with more granularity could minimize the comments needed.

------
throw-far-away
It's a code smell whenever a function doesn't fit on a page or has more than 2
levels of indentation. Smaller functions are easier to test, easier to read
and easier to debug. Leviathan kitchen-sink functions are a sign of slap-dash
engineering.

------
dangom
Interesting to run a "blame" on this code, and see that almost every line has
been touched by a different commit, and that 41 contributors collaborated on
the file.

It seems that the "do not attempt to simplify this code" is necessary rather
because the cost of having so many people relearn what they already know is
too large, and not because the code itself cannot be written in a simplified
manner.

It's a beautiful example of the difference between real-life constraints of a
codebase developed by multiple collaborators, and the _elegance_ that we
strive for when coding alone.

There is no "one size fits all" coding style in software engineering.

------
carlsborg
This style is common in large enterprise C++ code bases and is definitely
useful when LOC exceeds a couple of 100k and the business logic complexity is
high, and the project is mission critical and expected to be around for many
years.

------
whoisthemachine
I would argue that if they had solid unit tests of the expected behavior for
their `pv_controller`, they wouldn't need this level of documentation - nor
the scary space shuttle warning - the code would self-document.

------
kannmig
Reminds me of the classic SICP quip:

"Programs must be written for people to read, and only incidentally for
machines to execute."

------
raverbashing
Some code do require this style.

If the code is complicated, sometimes it's better to make it extra explicit
than just relying on the reader's comprehension (including empty else blocks,
for example)

Because in a nested A/B/C condition it is very easy to not notice what happens
in the case where A v !B v C and/or why is it different from A v B v !C
especially when some of those can't happen together (but then the code does
"something" and "can't happen together" means "it will happen in that very
weird case")

------
yoz-y
Honestly, to me this looks like normal good code. It reads from top to bottom,
exits early, does not have conditions so long that they require lines to be
wrapped, it has function that are long or wide but not both... Does not seem
to superfluously refactor code into functions called only once.

Maybe there are too many comments, but for code that is critical and might be
harder to understand at a glance that is better.

~~~
hinkley
The longest functions don’t follow the same guard clause style as many of the
shorter functions and I think that’s a mistake. That half the file avoids else
clauses and half don’t tells me there’s an argument here that was never
adequately resolved. I think we’re missing about six functions and a couple
more refractors, then tone down the a couple parts of the lecture.

As it is it’s obvious that many different hands were in it (and if you look at
git blame, one of them doesn’t know how to revert changes properly and needs
to stay after school for extra lessons. If this file is so important it
shouldn’t have the most critical lines with a “revert changes” comment as the
commit message. You done goofed, son.)

------
portal_narlish
This seems psychotic. Why not put the same energy into rewriting this code
correctly in a statically typed functional language?

------
eigenloss
This is not compliant with the JPL/NASA Coding Standard: [https://lars-
lab.jpl.nasa.gov/JPL_Coding_Standard_C.pdf](https://lars-
lab.jpl.nasa.gov/JPL_Coding_Standard_C.pdf)

Calling this "Space Shuttle Style" is an appropriation of credibility and an
insult to the engineers of STS.

------
unnouinceput
If you ever programmed embedded software for big companies (automotive,
medical, etc) you're used to this style. Hell, at Siemens we were forbidden to
even use ternary operator, a rule said it was to be replaced by a complete
if/else statement no matter what.

------
jmartrican
Two points...

1) One would have to make sure all the branches are accounted for during
unit/integration tests.

2) The problem I am struggling with is the interviewers. So much of what I
code I keep thinking about interviewers questioning it. "Why so many branches?
Couldn't you have done this is in a more DRY manner? Couldn't you have done a
better job at naming functions and variables so that so much commenting is not
needed?".

This thing we do is truly an art form that can more easily be picked apart
than properly understood. Ultimately I think we need to be like MMA
fighters... able to properly pick the correct response for the situation with
no ideological preference... only results matter.

------
danielovichdk
The key thing about complexity is that, you can move it around, but you can't
make it go away.

The complexity has to be there, somehow. It's also highly subjective how one
sees and grasps complexity.

Breaking code up for the purpose of reducing complexity is more often a
symptom of clearing your mental space.

But IMO most often, complex code is much more well off being put in the same
place, and with extensive comments too.

I also believe it's a true flaw to believe that a lot of comments are
unnecessary when writing "important" code.

Comments are written in a human language, we interpret them as such. Computer
language is different and it takes, more often than not, more mental power to
understand and more important to change.

------
z3t4
Simplifying or not, having unit tests that cover _all_ branches will become
handy.

------
int_19h
Setting aside the discussion of when, exactly, this approach is appropriate, I
wish we had more languages that would make it easier and more organized. The
kinds of languages where e.g. code documentation is part of the syntax, where
there's a way to explicitly express various contracts (e.g. design-by-contract
in the language), and where, in general, the language design errs on the side
of readability over terseness.

Some examples (none of which are perfect) would be COBOL, Ada and Eiffel. But
they all have their own issues, and then of course there's a question of long
term support and maintenance.

------
alecco
For a whole codebase like this check out SQLite. The best one in the world.

------
franciscop
On a related note, please note that _most_ people have the opposite issue,
myself included. I know we are highly biased and tend to overcomplicate
things, so I follow the KISS rule by heart.

But when I _do have to_ write complex parts of software that cannot be
simplified, I will take a lot longer because I'll need to prove, first to
myself, that it really cannot be simplified. Then I'll still add a big
warning, either to others or to my future self (which is effectively a
different person).

------
viach
How about: code should be self-explainable, functions should not take more
than a screen, ensure behaviour with tests. Otherwise, to me it looks more
like Space Shuffle style.

------
AstralStorm
Readability is not a valid requirement as it does not specify a valid end
condition.

Likewise simplicity is not a valid requirement. Code coverage by tests, proofs
and comments is.

A valid requirement could be considered complexity, number of names and
entities and a lot of other linter constraints.

Heck, aggressive deduplication is not even always good.

Self similarity is not necessarily good either (the reuse of patterns) as you
could have problems trying to spot the differences and special cases as they
may no longer stand out.

------
jcoffland
The code referenced here is extreme. It does not consider diminishing returns
or acknowledge tradeoffs in readability. I would advise a more balanced
approach.

A more compact style allows more code to be viewed at once making it easier to
understand. Of course, taken to the other extreme, overly compact code becomes
hard to read.

It's called, "the art of computer programming", because blindly applying
programming principals rarely yields good results.

------
mindcrime
_1\. Every 'if' statement has a matching 'else' (exception: simple error_

I've always considered this a best practice. There's rarely a good reason not
to have a matching else, even if it's no more than a comment reading

// intentional NOP

or

logger.trace( "THING didn't happen, ignoring." );

Either one tells anybody reading the code that the else case was thought about
and handled the way it was for a reason.

------
ArchTypical
Of course this can be improved and simplified. Handling 12123123 err checks is
a problem of avoiding short circuit failures. That's fine, but you have
multiple return values, so there's no reason not to leverage that
properly...or even consistently. The whole file is littered with waiting
assignment until a comparison, except in some cases or where if and elseif
statements use the return values inline but not other places. It's just
random.

    
    
            //checkVolumeSatisfyClaim checks if the volume requested by the claim satisfies the requirements of the claim
            func checkVolumeSatisfyClaim(volume *v1.PersistentVolume, claim *v1.PersistentVolumeClaim) error {
    
                // check if PV's DeletionTimeStamp is set, if so, return error.
                if utilfeature.DefaultFeatureGate.Enabled(features.StorageObjectInUseProtection) {
                    if volume.ObjectMeta.DeletionTimestamp != nil {
                        return fmt.Errorf("the volume is marked for deletion")
                    }
                }
    
                volumeSize := volume.Spec.Capacity[v1.ResourceStorage].Value()
                requestedSize := claim.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)].Value()
    
                if volumeSize < requestedSize {
                    return fmt.Errorf("requested PV is too small")
                }elseif v1helper.GetPersistentVolumeClass(volume) != v1helper.GetPersistentVolumeClaimClass(claim) {
                    return fmt.Errorf("storageClassName does not match")
                }
    
                // function here, obviously
                err = volumeAccessModeChecks(volume, claim)
    
                return err
            }
            // Here's the function
            func volumeAccessModeChecks(volume *v1.PersistentVolume, claim *v1.PersistentVolumeClaim) error {
                // even the naming sucks. DISTINCT case error name
                isMismatch, volumeModeErr := checkVolumeModeMismatches(&claim.Spec, &volume.Spec)
    
                if err != nil {
                    return fmt.Errorf("error checking volumeMode: %v", volumeModeErr)
                }elseif isMismatch {
                    return fmt.Errorf("incompatible volumeMode")
                }elseif !checkAccessModes(claim, volume) {
                    return fmt.Errorf("incompatible accessMode")
                }
                return nil
            }
    
    

This whole function is already a separated function from the middle of the
file, no reason to stop there. No need for separate files when the function
calls are going to be distinct and inline beneath their usage.

------
TomMckenny
I imagine it reduces errors and I admit it's very tempting.

But doesn't this style add a considerable cognitive load such that thinking
several layers up becomes difficult? Perhaps there is simplified documentation
of lower or mid level components so that thinking on the higher levels is not
so difficult.

------
jonahx
_Good_ : High-level comments that provide context, history, and motivation for
the current design.

 _Also good_ : Line comments that explain cryptic details (eg, the semantics
behind bit manipulations) or link to things like protocol definitions.

 _Bad_ : Line comments that simply repeat what the code does.

~~~
RickS
As a fairly junior programmer, I'll play devil's advocate on the _Bad_ bit – I
find that kind of thing very helpful sometimes. Having plain english
demarcations for what a piece of logic does – or where in the loop something
is – can shave 80% off the time it takes to get reacclimatized when needing to
make a quick change. There are moments when I know the conceptual bit that
needs changing, and would prefer to hunt it in english rather than spend time
translating on the fly.

~~~
jonahx
Two relevant points here:

1\. My comment assumes the code itself is well-written and clear. If it is,
the plain English explanation will be superfluous, and you'll be able to
understand the code just as fast. If it's not, then the comment was the wrong
solution to a real problem. The correct solution is to rewrite the code.

2\. What counts as "well-written code you can understand instantly" is of
course experience-dependent. And not just "years of experience" dependent, but
primarily "familiarity with the paradigm and style of coding experience"
dependent.

Steve Yegge talks about over-commenting by junior developers here:

[http://steve-yegge.blogspot.com/2008/02/portrait-
of-n00b.htm...](http://steve-yegge.blogspot.com/2008/02/portrait-of-n00b.html)

I don't include that to be insulting, but because it tracks with my own
experience as well.

------
mlindner
Why are people praising this monstrosity? Crazy complex non-simplifiable code
is not good.

~~~
contingencies
Yeah, 500+ upvotes on HN apparently think otherwise, but I also dare to
disagree... it's not clear, despite claims to the contrary.

Comprehending this code relies on reading a long series of verbosely
referenced assumptions around an implicit state machine with numerous error
conditions and edge cases. The author's argument is that extreme verbosity in
control structures assists with ensuring no such conditions escape an explicit
code block - ie. they admit that they are (ab)using code to deal with their
personal challenge of achieving a rigorous and precise conception of the
problem. I would posit that approach is the wrong tool for the job, and
further that they have simply failed to clearly articulate the problem, and
are writing illegible code and documentation as a result. A clear symptom of
the resulting fragility is the title / opening plea to others not to change
their carefully constructed house of cards. Fragility does not good code make.

If I were to rewrite this, the implicit state machine and its valid
transitions would be documented first and foremost, eg. with a railroad
diagram or ABNF.

 _The benefit of using [a formal specification language] is that it teaches
you to think rigorously, to think precisely, and the important point is the
precise thinking. So what you need to avoid at all costs is any language that
's all syntax and no semantics._ \- Leslie Lamport ... via
[http://github.com/globalcitizen/taoup](http://github.com/globalcitizen/taoup)

------
j45
Space shuttle coding is like preparing code for your future self to quickly
make sense of.

A key part of the software lifecycle is being able to easily onboard newcomers
to be productive, or coming back to a part of a code base months, or even
years later.

------
amai
Isn't this code style just a sign that go is lacking proper exception
handling?

------
preordained
The hubris in some of these comments is staggering. No, you wouldn't have more
cleanly (and successfully) written this in $BETTER_LANGUAGE, even with your
super power of perfect hindsight.

------
rkagerer
Shuttle's missing a space after the comment symbol

 _/ /checkVolumeSatisfyClaim checks if the volume requested by the claim
satisfies the requirements of the claim_

------
hopler
People seem to be missing that "simplify" here is a droll insult to too-clever
hackers. The code is not too complex, it is well written and documented.

------
toastking
This is pretty smart given the nature of open source. It's a good safeguard
for someone messing with code that runs like half of cloud deployments now.

------
jackjeff
This code would probably a lot shorter if every function call was not followed
by a ‘if err != nil’ statement. This reminds me of Win32 programming.

~~~
Jach
You have to handle the errors somehow... though it's amusing that Go settled
around what is essentially Java Checked Exceptions everywhere with the
handicap of not being able to automatically propagate (or equivalently an
Either type, albeit you can make expressive functional pipelines with that) so
you always complect the attempt at doing work in the body, the ability to fail
in the type signature, and the requirement to notice and handle the error at
the immediate call site after the failure has wiped out any local state there
may have been in the called function before it returned. (Hey, sometimes it's
nice to have a way to force callers into something, but I want it as a design
choice.) The notion of being able to fully decomplect and separate error
signaling from error handling escapes both languages though, being that the
notion is only as new as the '60s with PL/I or the '80s with Zetalisp (or
modernly with Common Lisp)...

------
peter_retief
Way easier to maintain code that is verbose, I have never been a fan of
abstracting functionality with the purpose of having less code to read

~~~
mdpopescu
Yes, I too like to send my own electric signals to the parallel port instead
of using the print menu from Word. /s

~~~
peter_retief
Within reason

------
chvid
Porting to a language supporting exceptions would be a huge simplification.
But then maybe it would stop being a space shuttle ...

~~~
almost_usual
Better to make blindly throwing exceptions impossible.

~~~
geofft
A good alternative is language-level support for returning early from a
function with an error. Off the top of my head, both Rust and Haskell have
good support for this. (I'm sure there are others, these are just the ones I
know.)

Rust has a macro try!(x) that takes a Result<T, E> \- a two-variant type,
either a successful result of type T or an error of type E - and translates it
to, if x is the first variant, evaluate the output of try! to the value of
type T inside, otherwise _return_ an error with the value of type E (on the
assumption the calling function returns a Result<something, E>, too). So you
can do things like `let file = try!(open(...));` and operate on the file.
People liked it so much that a couple years back Rust added the question-mark
operator, so you can just do `let file = open(...)?`, and chain it to `let
data = open(...)?.read(...)?`. It's still visible that this is how you're
handling errors, and you can always leave off the question mark and write out
`match open(...) { Ok(file) => ..., Err(error) => ... }` instead, if you'd
like.

Haskell has its monads, which for the present purpose can be interpreted as
just a wrapper type. Given a wrapped object of type T, you can give it a
function that takes a T and returns a similarly-wrapped object of type U, and
have it apply the function to the data. If the wrapped object is in a failure
state, it can choose to "apply" the function by just not calling it at all and
instead returning the same failure state. Haskell has special notation with
the "do" keyword for calling several monadic functions repeatedly, where it
looks like you're writing regular, imperative code and assuming errors don't
happen. But again it's obvious when you're using it and you can always handle
the exceptional case specially as soon as you want.

~~~
steveklabnik
You missed the E in Result<T, E>, by the way.

~~~
geofft
Thanks, fixed - I was trying to gloss over it for the sake of readers for whom
X<Y> itself isn't familiar syntax but I think it makes more sense this way.
(I'm also glossing over the fact that try! will convert error types, I think?)

Or pretend I meant io::Result :)

------
discobean
I find I often write code in comments first w/ the business logic, and then
flesh out the comments with actual code.

------
revskill
Where's the test ? How people test this piece of code ? Test is what matters,
automatic or manual.

------
an-allen
I'd like to see Uncle Bob do a point-by-point critique of this... "piece of
work"

~~~
geofft
What is Uncle Bob's claim to fame? What has he written that is production-
ready and sturdy and has held up to change?

I'm sure he can have opinions - he is very experienced at having convincing-
sounding opinions on software development. But where is the test of whether
he's right?

------
gigatexal
This is awesome. I have learned a ton about the volume controller just from
the comments!

------
lifeisstillgood
Nice. At some point one rolls up your sleeves and says "right, this part is
important and needs to work and _I_ need to be sure I have got it right" At
that point space shuttle style works pretty well (usually for me I am writing
some security / control dispatch for the umpteenth time)

------
vectorEQ
this is how my code looks until it's done, where i take out all of the
nonsense and make it unmaintainable, shooting myself in the foot for any
development i need to do on it in the future. love it :D

------
joe_momma
yesss thought I was the only one that thought this style was a good idea ...
Makes my last company look foolish and explains why most of the devs weren't
as productive as they could have been.

~~~
andbberger
That sounds like some dangerous over-attribution

~~~
joe_momma
Dangerous is not delivering on time because you want the code to be fancier
than it should be or needs to be when all you had to do was write it out
plainly and leave obvious comments.

------
known
"premature optimization is the root of all evil" \--knuth

------
KoenDG
Ah, I love this so much.

------
arrty88
What's up with that tab size? Is that normal for go code?

~~~
dmlittle
Go uses tabs instead of spaces and Github displays them as 8 character width.
It can get annoying and there are several extensions to change this behavior.
Tab Size on Github [1] is the one I personally use.

[1] [https://github.com/sindresorhus/tab-size-on-
github](https://github.com/sindresorhus/tab-size-on-github)

------
kizer
I love it so much.

------
rue
God, Go is tedious to read.

Also, just use a code formatter that rejects any incorrectly styled code.

------
shriphani
someone please introduce idris to the k8s team.

------
Lapsa
emm... no, please. like the enthusiasm though

------
Dowwie
Kubernetes, eh? (strikes through its name on my list)

~~~
marcc
Why? Does this file/comment cause you to lose confidence in the project in
some way?

I'm genuinely curious to understand the reason for your comment.

~~~
bjourne
Code that "can't be refactored" is a huge smell. This code can be written in a
simpler and easier to follow fashion even if the authors of the code were
unable to do so. The proof of that is the thousands of free software projects
of equal level of complexity whose code bases are a joy to read.

------
aerovistae
this is how I wish all code looked.

------
tzjmetron
The amount of circlejerking in this thread is unbelievable. Go is a horrible
language, and if this code were posted anonymously, these same idiots calling
it "the jazz music of software development" and "space shuttle style" (copied
verbatim from the actual code comments to boot) would be deconstructing and
denigrating it to the last fine detail. Hilarious.

------
crimsonalucard
Sounds like a job for typed pattern matching.

See rusts, match operator. The operator won't even allow your code to compile
if all branches are not accounted for.

------
lincpa
I tend to wrap the if statement in the function as much as possible, so that
the code as a whole presents a sequential pipeline structure. Keep the code
concise and readable.

MyBlog: The Pure Function Pipeline Data Flow
[https://github.com/linpengcheng/PurefunctionPipelineDataflow](https://github.com/linpengcheng/PurefunctionPipelineDataflow)

------
edoo
I wonder if this arises there because people get moved and would otherwise
receive lots of clarification requests regarding the implementation from a new
maintainer. It is easier than maintaining a separate wiki page that might lose
track of changes.

------
dymk
ctrl+f 'err != nil', 88 matches

------
heyjudy
Violates title guidelines.

[https://news.ycombinator.com/newsguidelines.html](https://news.ycombinator.com/newsguidelines.html)

~~~
chairmanmow
Guidelines != Rules. This a direct quote, so I'd think it'd be disingenuous to
lowercase it. Probably good that guidelines aren't rules, even though people
confuse them more than I'd like.

edit: After I posted this I saw the title was lowercased and dumbed down.
Unfortunate that the term 'guideline' has been misapplied again, making things
worse, but at least there's some irony to get a chuckle at given the article's
subject matter.

------
bjourne
Is there any evidence that writing code in the demonstrated way produces
higher quality? No? None at all!? Then why should I believe it? I might as
well embrace healing and homeopathy too...

I'd rather write my code like its written in the Linux kernel. There is no
evidence that that style is better either, but at least Linus has provided
loads and loads of arguments in favor of it on a mailing list.

------
rossdavidh
While I can understand the basic philosophy, it seems an unfortunate example
to pick something that had 2 catastrophic failures out of 135 launches.
Although, I suppose, neither of them were due to software...

~~~
zrail
There were many other close calls, most having to do with the thermal system
on the external tank, which is what damaged Columbia. It was a known issue for
almost the entire program.

As you said, nothing having to do with the control system.

------
casper345
Although I think this is great, this reminds me of a talk Bryan Lunduke did
where he says CS/software development is not a profession yet. I love the
flexibility software devoplmentgives to every cs, programmer, developer but
when you think more about it, that is not the same for other jobs/professions.
Professions have standard guideline, procedures to follow. Everyone is doing
any language they want, design patterns they just read from a random article,
100s of tutorials on one subject. But are all languages/paradigms/architecture
equal? Should there be language _only_ used in some industries to keep
consistency? Doctors can't just try a new technique, neither do lawyers. Not
saying it is bad, but something I think more thought needs to be put into.

~~~
fogetti
I would say that "Everyone is doing any language they want, design patterns
they just read from a random article, 100s of tutorials on one subject" is far
from being true anyway.

That might be true if you live in a cave but when you are working on a team of
6-8 people with a team lead then basically none of that is true and you are
forced to follow others' decision and everything is decided for you (instead
of you to make decisions) no matter if you like it or not.

Of course some might say but then you have to make compelling arguments to
raise your concerns and being heard but that's beyond the point and that's
politics that most people (including me) despise. As a software engineer your
only choice to make those decisions you mentioned is that you are a manager of
some sort or you work completely alone.

And for some reason most developers accept this hierarchical relationship in
their daily professional life but still pretend that they are allowed to make
decisions by themselves instead of pointing out the implicit forces which rule
their daily lives.

And I like to call out this line of thinking because it is pushing this
liberal agenda that you are the sole reason for your luck or failure which is
not true at all. Not even close to reality.

Actually many thought leaders in our industry for example Uncle Bob anticipate
that our industry MUST and/or WILL be regulated (if by self-imposed regulation
or external one is again beyond the point) otherwise the whole industry would
lose credit. And I fully agree with that.

Not only because of credibility reasons but also because it makes the implicit
subordinate relationship that most developers are blissfully unaware of, more
explicit.

~~~
casper345
> That might be true if you live in a cave but when you are working on a team
> of 6-8 people with a team lead then basically none of that is true

Yes, I agree if you have an established team and there is a strict
hierarchical development cycle/roles then rules will be enforced. But how
about other team dynamics or lone developers that push their code into the
world. How many js libraries import explicitly or implicitly an addition
library (too many to count on top of my head but not enough to bother writing
a script for you to show). There is a lot of bad code out there that were
created by bad _untrained_ tech managers and lone coding wolves because some
standards have not yet been refined yet or agreed upon(standards like ssl, aes
encryption).

> liberal agenda?

You lost me there? And maybe I lost myself as well

> Uncle Bob When I said Bryan, i misspoke and was refering to Uncle Bob. But
> do you agree with self-imposed or external? They lead down very different
> paths

