
Qualities of clean code - neokya
https://blog.goyello.com/2013/01/21/top-9-principles-clean-code/
======
ewjordan
Over the years I've seen many more unmaintainable atrocities caused by
diligent adherence to these principles than I ever have because of ignorance
of them.

All the horrid Java libraries that we hated working with 10 years ago were
created because of slavish devotion to the single responsibility principle,
short methods, DRY, and testability. The absolute worst codebases I've ever
seen sprung from an overaggressive concern for extensibility.

I do agree with the guiding principle, that when you're writing code, it
should be written for other people to read and work with. But sometimes that
actually _does_ mean that a class should represent an object that does more
than one thing, and you shouldn't fight it. Sometimes a gritty algorithm
really _does_ make the most sense if it's laid out as a 40 line function in
one place rather than spread across 5 different classes so each part is
independently swappable and testable. Sometimes you really _don 't_ need
extensibility, you know that up-front, and building it in just pisses off
everyone that ever has to touch your code because now they have to go through
five or six "Find implementing classes" dances to find the actual code that
runs when your interface methods (of which, of course, there's only a single
implementation) are called. Don't even get me started on abuses of dependency
injection when it's not necessary...

Part of me is glad that these concepts are so commonly discussed, because they
really are good things to consider, and can make code much tidier and easier
to work with in the best case. But it takes a lot of experience to know when
and where you should and shouldn't follow these "rules", and it tends to be
much easier to unwind a novice's spaghetti code and tidy it up than it is to
pick apart a poorly conceived tangle of abstraction and put it back together
in a way that makes sense.

~~~
highCs
So I have a theory that may explains things here. Here it is.

There is 3 levels of programmers.

Level 1 is the beginner. A level 1 programmer is barely able to make a
complete app or lib. His work simply rarely works. The code has no sense, is
full of bugs, it's a complete mess, and the user has a _very_ poor experience
(if an experience at all).

Then comes the level 2 programmer. The novice. The novice learnt about OOP,
design patterns, DRY, single responsability principle, importance of testing,
etc. The only problem with the level 2 programmer is that he overdoes
everything. He is victim of overengineering. He is able to make a complete app
with a decent UX and the quality of the code is dramatically better, but his
productivity is _low_. Really, really low. Everything takes month. Every
single change is a _project_. 10 actual lines of codes need to be changed?
That's 10 classes, 250 lines of unit tests, it requires 3 classes to be
refactored, and on and on.

Finally, there is the level 3 programmer. The level 3 programmer is a
programmer that does level 2 programming in a smarter way. His first principle
is _less code_. That doesn't mean the code must be complete shit, but the
level 3 engineer understand the real enemy and that all problems grow with it
exponentially: code. The level 3 programmer knows, understand and apply the
principles of the level 2 programmer, but he just does the right amount of
them. Not too much.

If he has to, the level 3 programmer gonna err on the side of the level 1 code
and will avoid as much as he can level 2 code. That's because level 1 code
cost less to fix than level 2 code.

Now here comes my point: a level 2 programmer read and write articles that is
about how to not be a level 1 programmer.

~~~
the_other
Please consider replacing all the "he"s with a word less gender-defining
("they", "s/he", "ve" etc). The entire gender spectrum can code if they want
to and they should feel included rather than excluded.

~~~
bobthechef
This is just silly. Besides, grammatically, "he" is a gender neutral pronoun
when referring to a person of unspecified gender (as in other languages).
"They" is plural. "Ve" is not a word. "S/he" is awkward and unnecessary (and
hey, we can argue that it is misandrous because you're capitalizing the "S"
and prioritizing "She" over "he"; if you argue that it doesn't matter, then
the same can be said about "he").

~~~
dragonwriter
> Besides, grammatically, "he" is a gender neutral pronoun when referring to a
> person of unspecified gender (as in other languages).

No, grammatically, "he" always has masculine gender [0]; its historically-
accepted (though increasingly-less-so) _semantics_ include use in reference to
a person of unspecified (socially-ascribed) gender (classically, use of
personal programs in English maps best to socially-ascribed gender, which has
not taken gender identity much into account -- recently, there's been a move
to align socially-ascribed gender with gender identity, but pronoun use
basically follows the former which just happens to have a growing norm of also
aligning with the latter.)

> "They" is plural

"They" is grammatically plural and gender-neutral, but has a _very long_
history of accepted use with semantics of referring to an individual of
unspecified (socially-ascribed) gender. This acceptance was somewhat reduced
by the Victorian fad of Latin-inspired prescriptivism in English, but this
reductions is among those of that fads effects that have been fading over
recent decades.

[0] Note that grammatic gender is a distinct concept from either socially
ascribed gender of a person, gender identity of a person, or biological sex of
a person.

------
gravypod
I've often wanted to talk to people about things like these and I'm often
pushed back by people saying "This is simple, we all know this" but I don'
think that's the case. If anything I think we know very little about how to
keep software clean and simple. I've been trying to change that in my works by
having people comment on my code that I've recently written for homework but
people aren't really up for the idea.

I've had one discussion that stood out. We were talking about functional
programming and I said that the idea of functional programming was the
segregate state to the smallest unit of computation that must operate on it. I
was met with my "friend" just blindly telling me to "stop saying that". I
asked for a counter argument to that statement but they just said that I
should stop saying that.

We as a community are horrible at speaking about code quality , evaluating
each others work, and even just sifting out opinions from facts. It's crazy
and it's something that needs to change if we want to take our field to a
future where we can all be happy with the code we are writing.

I've got some suggestions I'm happy to talk to others about. My email is in my
about page on this website. Please comment here or email me and I'd like to
talk about this!

~~~
hyperdeficit
This is a real problem in our industry. We all like to think that we 'know'
how to write good code, after all we read 'Clean Code' once. That, combined
with either overconfidence in our abilities or the negative perception that
comes with not knowing the answer to every problem leads to a lot of software
that has applied 'good' coding practices and architecture to produce a mess of
awful code.

In reality it is probably a lot more like learning how to apply any other
concept, it takes practice, yet we don't spend enough time practicing our
craft. This is particularly a problem because in the course of developing a
product you will only get to implement a new solution to a problem a few times
at best, or once more likely. This means we don't get enough experience with
the different possible approaches to really internalize what a 'good' solution
looks like.

We spend much more time learning the software development approach du jour,
XP, Scrum, Kanban, Lean, etc. It doesn't matter what software development
approach you use if the output is an unmaintainable mess of code.

~~~
gravypod
Honestly the best way to go about this, in my mind, is to make criticism ok in
our industry. I've been trying my hardest to get someone I know to comment on
my implementation of a CS280 homework I've been working on. I want to get to
the point where I can properly write this code so it is readable to everyone.
It seems like every time I ask someone to do this I'm told "Why would you want
me to tell you how to code"

That's insane! I think that a goal of any programming course around the world
should instill the idea that getting your peers to modify and read your code
is a must in our industry. People need to be able to com along, see what
you've done, and understand it.

If anyone want's to comment on my code that I'm talking about in this example
it can be found here:

Implementation:
[https://git.gravypod.com/gravypod/school/tree/master/cs280/h...](https://git.gravypod.com/gravypod/school/tree/master/cs280/homework/program_1)

Assignment:
[https://web.njit.edu/~gwryan/CS280/CS280-Program-1-Fall-2016...](https://web.njit.edu/~gwryan/CS280/CS280-Program-1-Fall-2016-R1.pdf)

I've attempted to live up to what I think is "good code" but no one want's to
tell me if I'm right or wrong or even discuss this for fear of hurting my
feelings I presume. I always get "run valgrind or a linter on it" and I've
done that and come up with no ways for improvement. Everything is all opinion
and no fact in this business of code cleanliness although this should be a
cornerstone topic for the software development industry.

~~~
ThrustVectoring
That's a hard thing to get. A lot of people will play shitty status games and
make feedback to try to prove they're more clever than you. And since people
do that, genuine feedback can often get interpreted that way.

~~~
chiefalchemist
That's a good point. There's bad code. And then there's that's not how I would
do it code. Too often, in public the latter gets treated like the former. When
you're still open to learning this is confusing. That is, do I suck? Or is
that feedback coming from an asshole?

------
taneq
Something that lists like this always seem to miss is locality of reference.
You shouldn't have to jump around all over a file (much less through several
files) to follow the code that does a single thing.

The single responsibility principle should really be "exactly one" rather than
"not more than one". If a function is responsible for less than a whole thing,
it shouldn't be a function on its own.

~~~
morbidhawk
> locality of reference. You shouldn't have to jump around all over a file
> (much less through several files) to follow the code that does a single
> thing

This is something I was learning when I was working in Swift, in a WWDC video
they called it local reasoning; being able to reason about the code in one
particular function without having to worry about state changes elsewhere. Now
working in C# it seems to be a common thing to pass an object by reference to
a bunch of different functions to fill in it's values and map properties. If
you do too much of that it can become a maintenance and debugging nightmare
when a property didn't map up the way you wanted it to.

I also agree with what was said in a different comment about not breaking
functions up too much if it's not necessary and that would help eliminate this
need to modify state in so many places.

------
keithb-
>> Can be easily extended by any other developer

This is a tenet that will lead inexperienced developers astray. This "rule" is
just too ambiguous. Extensibility is a fascination for object-oriented
programmers but in my experience doesn't have a lot of successful examples.
Typically I have seen this manifest itself in either a Command+Composite style
where every meaningful class is a composite of other SRP classes, or in a
proliferation of interfaces that seldom have method definitions and are
instead used to enforce coding standards or dependencies.

KISS is incompatible with this rule and you should kill this rule with fire
because simple is not extensible. Perhaps when the goal is extensibility then
should you consider other developers, but if you are developing a "beige"
application then you should not consider extensibility. Instead, just assume
that release management will handle changes, i.e. another developer will
update rather than extend your class and that will be released in version 1.1.

Of course, to do this also means admitting that version 1.0 of your class was
pretty much garbage and that it needed to be "extended". Tough pill to swallow
for some.

~~~
js8
I somewhat disagree. Interestingly, one of the most extensible SW systems ever
is Unix shell, which is completely opposite to the OOP idea.

The idea in Unix is that data are the interface, in case of Unix the data are
unstructured text, but I think it can be generalized to systems with
structured data. So contrary to OOP, most extensible systems seem to be the
ones that (self)document their data structures as interface and leave it at
that.

~~~
hxegon
Clojure agrees with this philosophy IMO. Paraphrasing, but "100 functions on 1
data structure is better than 10 functions on 10 structures"

~~~
js8
True. I think in general it's a philosophy of functional programming, where
data structures are types, and type signature of functions are their APIs.

------
mosselman
While some of these points have something to do with 'clean' code or
maintainable code or whatever, this copy-pasted list of very very shallow
statements about code isn't really useful. I expected there would at least be
a few examples on how to implement these principles, perhaps in a 'before and
after'.

Maybe there should be a 'click-bait' button on HN with which we can report
things as such, along with posts such as 'Why I won't be using popular-
technology-x ever again' and '10 things I hate about SQL'

~~~
Can_Not
I was definitely looking for something more insightful and/or deeper to share
with my colleagues. Any recommendations would be appreciated.

------
pyrale
> 3\. It should not be redundant

Not sure I agree with this one. While abstractions are a great way to reduce
the length of code, sometimes they break readability. When you read code,
sometimes, you feel like you don't read a solution to your problem, but a way
to solve your problem masked behind abstractions far removed from the domain
concepts.

That's why, sometimes, redundancy is better than the wrong abstraction.

~~~
taneq
I've seen some very good arguments about 'redundancy' that's actually the same
lines of code performing a different function in different places. In this
case, naive de-duplication just adds unnecessary interdependencies between
otherwise unrelated parts of the codebase.

------
z5h
I believe an important quality is that code should TELL A STORY.

Consider a biography: you could simply collect facts about a person and write
them in an arbitrary order and call it a biography. It could be a complete and
accurate account, and still be impossible to read or follow.

Well written code is not only complete, but it also guides the reader through
the logic.

Consider the difference between:

    
    
      statuses = []
      reporter = Reporter.new
      jobs.each do |job|
        statuses << job.complete && !job.error
      end
    

and

    
    
      job_statuses = jobs.map do |job|
        job.complete && !job.error
      end
      job_status_reporter = Reporter.new
    

In the first case, we see statuses declared. Statuses of what? Not yet clear.
And the code that updates it is separated by unrelated code. Also, what will
reporter be reporting? In the second case, map and better naming are used
making it clear that we are getting a status for every job. Aha! I don't even
need to look at the implementation of the do block to understand what's
happening.

~~~
bruxis
Personally, I would rather see the first version with better variable names.

I always find that map() methods tend to obscure the purpose of the values
whereas a simple appears more explicit.

~~~
z5h
"map", "select", "reduce", "flat_map" and the like make your intention visible
upfront. "each" just tells me you're doing "something" with each item.

Not sure how giving less information is preferred over being explicit, but
hey, whatever works for you.

------
k2xl
Clean code isn't always the most efficient code, but I think that's fine. Many
times in my career I've found that you don't always need the most optimal,
efficient, and performant solution to get the job done, and that sometimes you
need to sacrifice those attributes for clear, readable, understandable code.
Good architecture usually supercedes speed of individual algorithms. Hence why
you see almost every major language under the sun has been used at scale.

I remember when looking into neural nets, the basic python code to get one
running was super easy for me to understand. However, I also realize that the
optimal, most efficient methods of neural network libraries are way more
complicated (and for good reason).

------
baron816
Most of all, clean code should be easy to reason about.

~~~
todd8
> Most of all, clean code should be easy to reason about.

This one sentence should be guiding principle for any set of recommendations.
Programmers should tape it to the top of their monitor. Programs difficult to
reason about are hard to get working and hard to debug and hard to maintain.

In my opinion, this is the key requirement of "good" or clean code. Likewise,
programming languages that facilitate this are "better" programming languages.

Depending upon the goals of a program there can be other important
requirements (for example, efficiency), but however the program is constructed
it should be as easy to reason about as possible given the constraints imposed
by these other requirements.

Recognizing this, I tend to pick programming languages that make my programs
easier to reason about and tend to program in a style that makes informal
reasoning easier. For kernel development, programming in C was appropriate
(back when I was doing it) because I needed to know _exactly_ what was going
on in the machine in response to the code. At the other end of the scale,
straightforward Python code often results in such short programs that they fit
entirely on one screen and are consequently easy to understand and reason
about.

This style of programming has led me to be very impatient with the "keep
debugging" until you think it works style of development. I tend to think that
each bug found is evidence of the presence of more bugs.

~~~
rimantas

        >> Programs difficult to reason about are hard to get
        >> working and hard to debug and hard to maintain.
    

Actually reasoning and maintaining often are at the opossing ends. The code
which is easy to reason about tends to be low on abstractions and tightly
coupled, so changes become much more difficult. More modular code is harder to
reason about but the changes can be introduced easily.

~~~
todd8
I believe that I agree with you. Good modularity and good abstractions allow
easier reasoning about the program. I like the word reasoning because it
covers thinking in a Dijkstra like way about tight pieces of code and Liskov
like ways about larger systems.

------
kasey_junk
Extensibility is important in some cases and a detriment in others. Most of
the rules about extensibility came from environments where the code was
running in many places at the same time (ie delivered to customers).

In a SaaS environment the code needn't be extensible as there is only 1 copy
of it. It is much more important for the code to be changeable, rather than
extensible and in many cases the things you do to make code extensible make it
harder to alter fundamentally.

Its important to understand that how you deliver your software is one of the
biggest guiding factors in how you design your software and take that into
account.

------
pif
If written by someone else, clean code is: all and only the code that I
personally won't have to maintain :-)

------
swalsh
Something I've noticed, in bad code if you need to learn something, or change
something... Simply "searching" the code isn't enough. Maybe there are
multiple places that do a similar thing, maybe the names are weird, maybe it's
so condensed as to be unreadable. Whatever the cause... The only way to find
the piece doing the thing is to step though it.

Good code, you can search it... even if the majority of it is unfamiliar. Find
a piece, and say "oh this is probably the spot" with out ever executing it.

------
pif
I recommend "What Could Possibly Be Worse Than Failure?" by Alex Papadimoulis
([http://thedailywtf.com/articles/What_Could_Possibly_Be_Worse...](http://thedailywtf.com/articles/What_Could_Possibly_Be_Worse_Than_Failure_0x3f_)).

An excerpt: _We are practically the only industry where completion and success
are synonymous. If the foundation of a one-year-old home is crumbling and its
roof is plagued with leaks, would anybody actually call that a success?
Despite being filmed and produced on budget, is there anyone who would not be
ashamed to have Gigli on their filmography? Of course not! So why are the
products we create – complex information systems that should last at least
fifteen years – be held to a different standard?

Now think back those projects of yours. How many would you say are
maintainable by someone with less business knowledge and a weaker grasp of the
system’s design? How many will not snowball into an unmaintainable mess? How
many do you truly believe could last fifteen years? I’ll bet that number is
quite a bit lower than all of them._

~~~
_pmf_
> How many would you say are maintainable by someone with less business
> knowledge and a weaker grasp of the system’s design? How many will not
> snowball into an unmaintainable mess? How many do you truly believe could
> last fifteen years? I’ll bet that number is quite a bit lower than all of
> them.

Coincidentally, the number of projects where the client offers to pay for tens
of man years is also quite low. This is something I'd recommend to be taken
into consideration by armchair philosophers portraying the state of the
software industry.

~~~
pif
I've seen this worshipping of "ship soon" many times. It always ended with a
buggy and messy software, an unhappy customer and a stressed development team.

~~~
nickpsecurity
It also turns people into millionaires and billionaires. It's how the top
hardware and software companies got their market share. So, one should always
assess on given project or company the First Mover & time-to-market for
feature sets advantages vs quality or user perception.

~~~
pif
Well, the article was about "clean code", not about "most efficient for
business" code :-)

------
erlich
Being able to simulate amnesia is my goal.

I write code that I come back to and don't understand, and realise its
terribly complex because I understood everything at the time, but could not
imagine what it would be like coming back after a month to make a change, or
for someone new to try to understand it.

Code is for people to understand.

Some people think its for being able to write good tests, eliminating all
side-effects and shared state, for the computer to be able to run quickly and
optimise, to be easy to read.

But its really just about being able to be understood. Most time will be spent
in maintenance.

When you modify and debug, you are diving into the middle. You are not walking
through the code base from the beginning and reading all the comments.

It needs to be understandable from all locations.

I'm a strong believer in micro-modules. Left pad et. al. I try to create small
modules which do one thing well that I can trust and not have to think about.

------
drakonka
If I'm remembering correctly this article seems to mostly paraphrase what the
various developers Robert C. Martin interviewed for this book ("Clean Code: A
Handbook of Agile Software Craftsmanship") said...in mostly the same order.

------
abhas9
Someone very experienced once told me: "For one change in requirement there
should me one change in your code. That's a clean code. If you can achieve it
without any change in code, it's even better."

This quote always sticks in my mind :)

------
antoaravinth
>> 5\. Can be easily extended by any other developer

I can remember a instance, when I started my carrier. I was a JS developer. I
wrote a module with Functors, Compose, Partial etc.

In code review my team told they didn't understand anything and reading such a
code is not pleasant for them. I was upset, I was thinking why my team is not
happy with it.

Today I can make lot of sense; stick to the pattern/design of your team. If
your team follows / loves functional programming in your project, stick to it.
If not try to advice them why functional programming would be better than
normal approach.

End of the day, all it matters is writing simple, elegant code which others
can understand.

------
textmode
With the exception of shell scripting which I'm writing every day, I remove
"code" more than I write it. C is the language I deal with.

The easier it is to remove code from someone else's program, the "cleaner" the
code.

That's my definition of "clean code".

For example, I just had to edit the code for a text-only browser to remove a
few "features". For example, the author recently decided it would be a good
idea to _non-interactively_ access no-content refs in a page such as
"prefetch" and other garbage.

~~~
mmarx
> The easier it is to remove code from someone else's program, the "cleaner"
> the code.

But then, contrary to intuition, a program becomes less clean by actually
removing code (since now there is less code to remove, hence it is more
difficult to remove code). A minimal (in the sense that no more code can be
removed) program would be maximally unclean, whereas, intuitively, should it
not be considered clean?

Also, adding code always makes a program cleaner, since the newly added code
can always be removed easily.

~~~
textmode
"... in the sense that mo more code can be removed"

Or else what happens?

"... should it not be considered clean?"

It should be considered finished.

"... since the newly added code can _always_ be removed easily."

Not true in my experience. Unfortunately.

Sometimes it's necessary to add some code, e.g., a new driver for a new item
of hardware. I have nothing against adding, per se.

~~~
mmarx
> Or else what happens?

In the extreme case (the empty program), there is no more code left to remove.
It suffices that any proper subset of the code is insufficient to provide the
needed functionality, though.

> It should be considered finished.

And yet you obtain a cleaner version of the finished program by adding
superfluous code to it. In fact, every unfinished version (a version where you
can still remove code) of the program is cleaner than a finished version
(where there is no code left to remove) of it.

> Not true in my experience. Unfortunately.

Removing the code is reverting back to the previous version. How is that not
easy to do?

> Sometimes it's necessary to add some code, e.g., a new driver for a new item
> of hardware. I have nothing against adding, per se.

But that changes the requirements, and thus changes which code can be removed.

However, the point was that you could obtain an arbitrarily clean version of a
program by adding redundant code to it—since it's easy to remove that code, it
is cleaner than the irredundant version of the program.

~~~
textmode
"How is that not easy to do?"

1\. The author does not provide access to his/her source code repository, only
compressed tarballs.

2\. The author's code is not clean.

You appear to have some issue with my "definition" of clean code. I do not
follow your points but maybe I can state it another way: Code that is easy to
edit is clean code. If that definition is still giving you trouble, you think
it's unreasonable, etc., let me know.

------
k__
The thing I learned from working with absolute beginners to mediocre
developers is: good modularization/isolation is the key to success.

Yes, most things in the article are nice to have, but you can't teach
everything in a month and people need to be prodcutive to be worth their
money.

------
partycoder
A piece of code should not know a lot about the system and should work under a
restricted set of assumptions and preconditions.

When this rule is not respected:

1) You end up with lot of coupling

2) Unit tests end up with a lot of overhead

------
caub
in the background image, the code would be better with arrow functions, or
object shorthand methods

~~~
kevindqc
I don't think it's an example of clean code (all the arguments are a and b for
example).

Also the article is from 2013, did these exist then?

------
JustSomeNobody
A "Top X" post on HN. And can there really be anything new said about
qualities of clean code now that hasn't already been said many ... many times
before? Given that it is a "Top X" post, I don't feel confident there will be.

I'm passing on this article.

~~~
noxToken
Mods, can you change the title of the article to _Stuff About Clean Code We
Already Know_? This way, we can let the new users and new programmers know
that everyone in the world has already seen this content? Thanks!

Just because you know it doesn't mean everyone else does.

~~~
JustSomeNobody
There a thousands(!) of articles on clean code. And at least one _really_ good
book on it. I would bet dollars to doughnuts that this articles has NOTHING
more to add on the subject based solely on the title.

~~~
noxToken
I know that. You that. I'm sure 99% of the commenters of the article[
_citation needed_ ] know that. But there are people who haven't read _Clean
Code_ or the bevy of of other articles on this topic.

The point of a forum (usually) is to promote discussion. I click on article
written about stuff I know all the time. Reading the article might not tell me
anything new, but the comment discussion can bring up new points or challenge
existing norms that make me reconsider why I do the things I do.

