
Cognitive Biases in Software Development - _sJiff
http://smyachenkov.com/posts/cognitive-biases-software-development/
======
W0lf
My personal observation (and view of course) is that most people simply cannot
code. And I am not talking about that reverse-a-binary-tree or traverse-a-
linked-list type of coding, but more profoundly they just cannot wrap their
head around a problem and are able to create a _sufficient_ solution with
respect to the context of the code and its environment at the same time. Good
software development is strongly connected with ones ability to think clearly
about any given problem and the ability to logically deconstruct it in
smaller, more digestible thoughts. If there's confusion in the mind, there
probably will be confusion in the code as well.

Also, it just seems as if many are unable to step back and look at the broader
side of things when coding. Asking questions like: Are we using the right
concepts here? Did we develop sufficient abstractions? Case in point: I was
just refactoring a code base for a client where the code was paved with a
`MemoryHolder` type, where simply `Buffer` would have been the better name for
the same concept.

~~~
appleflaxen
I don't think this is the right mental model of coding.

It seems a bit too similar to the idea that "most people simply cannot read".

We all accept that nobody can read without instruction, practice, and
feedback. Why is coding somehow different? If anything, I think reading is
_more_ foreign/difficult, because coding is explicit thinking, and we all
think. Whereas reading is a completely synthetic act that starts with
arbitrary symbols that must be memorized by rote before you can even take the
next step of using them.

None of us start out life being literate, but few people lack the ability to
become literate. Why is coding different?

~~~
jasode
_> None of us start out life being literate, but few people lack the ability
to become literate. Why is coding different?_

To elaborate on the sibling comments, coding is _generative_ work instead of
passive consumption like reading. This split becomes easier to see when
looking at a bunch of domains.

Most of people can learn to _read_ a book but very few can _write_ a good
book. Similarly, it's easier to _re-tell_ someone else's funny joke than be
creative and write original comedy.

Many people can _learn_ basic physics equations like "f=ma" is "force equals
mass times acceleration" but fewer are able to be _generative and discover_
new physics equations that are accepted by the science community.

Most people can listen to music and appreciate it, but a smaller percentage
can play instruments. And then within the set of musicians, an even smaller
percentage can _compose new original music._ There must be something more to
it than "training and practice" to explain why a 19-year old Chopin can
compose sophisticated piano compositions while most 70-year old professional
concert pianists that have performed for _decades_ with more repertoire than
Chopin have written no notable original music.

Coding may not be as hard as creating "Theory of General Relativity" but it's
not the same as reading literacy.

~~~
kspacewalk2
Generative work is not all created equal. Most of programming we all do is
like applying physics equations in practice. A tiny portion is truly on the
cutting edge, more akin to physics research.

Likewise, a lot more emails and discussion comments and school reports are
written, per volume, than masterpiece books.

~~~
iffybookmark
I'd be willing to bet that

{people who can look at a real world problem, choose an appropriate physics
model, and apply the right equations, correctly}

and

{people who can look at a real world problem, choose an appropriate logical
model, and apply the right language/frameworks/code, correctly}

are probably really quite similar sets.

------
l0b0
There's also cognitive bias bias - prematurely jumping to the conclusion that
some opinion is simply based on cognitive bias, and should _therefore_ be
dismissed or contradicted. Basically, it's good to use awareness of cognitive
bias to moderate your own thinking, but if you signal to others that you think
they are labouring under some bias don't be surprised if they shut down the
discussion ASAP.

~~~
celrod
I agree.

Criticizing someone for being biased is an ad hominem; dismissing their
arguments because of it is committing the genetic fallacy.

In the extreme, it reminds me of this blog post, about how knowing about
biases can let some folks universally dismiss others they disagree with (and
thus manage to never learn or consider alternative ideas):
[https://www.lesswrong.com/posts/AdYdLP2sRqPMoe8fb/knowing-
ab...](https://www.lesswrong.com/posts/AdYdLP2sRqPMoe8fb/knowing-about-biases-
can-hurt-people)

------
cjonas
I work as a "consultant", building cloud based business software. Above all
else, my job is to deliver value. Something I constantly struggle with is
"clean code" vs just pumping out a mostly static transaction script and moving
on.

More and more, I write code I'd almost be embarrassed for colleagues to
review. But for the type of work I'm doing (poorly defined, highly volatile,
potentially short lifespan), I can't justify anything more.

It doesn't feel good and it's still hard to accept that I'm doing the right
thing.

If the language I'm working in supported FP, then my world would be much
better.

~~~
Cthulhu_
I've been doing Go for a while now, and more and more I find myself just
writing out really dumb looking code. Add a new line, write another short-
lived variable, treat the guy reading it - me - like he's a complete idiot. I
don't work in a team at the moment, but what I do now is the groundwork for
something that will likely be used for the next decade (UI work, back / front-
end).

~~~
mtrycz2
> really dumb looking code

You cannot imagine how I'd value having really dumb looking code in my life.

Yet here I am, swimming in a swap of cleverness that I can't refactor nor will
ever be able to wrap my head around.

I envy you.

~~~
dgb23
Both "dumb" and abstracted code can be hard to read and change. The most
important part is consistency.

It is generally easier to write dumb code that is consistent. It requires less
mental energy and is often a very good starting point for refactoring into
something more declarative and DRY.

The pain comes from inconsistent code, often either based on wrong previous
assumptions, premature optimization or time pressure.

------
jackcosgrove
This is a good essay, and I think one of the main lessons is that programming
is a small part of software development.

The question of whether to use an inefficient, readable implementation or an
efficient, cryptic implementation can mostly be solved with the correct class
topology. If classes are SOLID, then it is easier to justify a cryptic
implementation of one method as that is hidden from most other developers. A
class that will be inspected by others more, such as a business logic service
layer, should lean towards inefficient, readable code.

Likewise, the question of whether to reuse or roll your own can also mostly be
solved with a good architecture. Logically separating components makes it
easier to tailor the implementation to the use case, and replace if necessary.

As always, breaking the problem into smaller problems is most of the battle.

Sadly software development as it is taught and measured by interviews, is
mostly about programming and algorithms. The real art and value in software
development is interface design and system architecture design.

------
ThePhysicist
Personally I found that my productivity as a software developer was much
higher when I was young and didn’t think so much about the cleanest code or
the perfect architecture for a problem and instead just tried different
approaches. Now I often feel paralyzed by having to “get it right” from the
beginning.

Valid (in my opinion) lessons I cherish today are keeping individual parts of
a code base simple and understandable, documenting and testing a lot and
preferring simpler solutions over complex ones.

~~~
RobertRoberts
This is exactly where in have ended up. And I am happier for it. My code is
simpler and less "clever" per line but far wiser is structure and in use.

I abstract far more carefully now and I don't mind a few extra lines of code
that make it easier to digest at a glance.

------
sidcool
Over the past few years I have grown disillusioned by the seemingly hyper
focus on clean code concept. It seems perfectly logical to have readable code.
But the excess that we programmers do with the concept.

~~~
ravenstine
I think what's worse than that obsession, and perhaps is actually what you
meant, is the concept of "elegant" code.

Elegant code is easily just as bad as spaghetti code. I can't even begin to
quantify how many hours of my life were wasted because someone thought it was
more important to make something elegant rather than understandable. I get
that it's satisfying to make something that "makes sense" if you've built a
mental model of the problem from the beginning, but if it's incomprehensible
to others(without serious devotion to figuring out what is going on) then it
might as well be crap in the first place. At least spaghetti code can be
fairly easy to hack because there's usually lots of duplication and
specialized code, making it straight forward to step through with a debugger
and make a change without mysteriously borking the entire app. Elegant code,
ironically, can be more flimsy because it's usually written assuming that the
system stays perfect, and changes to the system reveal single points of
failure.

Clean code can be written without necessarily going overboard with elegance to
the point where it's not easy to understand. Even dirty code can be workable
given documentation(can just be comments explaining what things do) and
consistency.

~~~
UK-Al05
Clean code has always meant to simple, easy to understand, not elegant.

~~~
collyw
I don't think they are mutually exclusive.

In a recent interview question I was asked to find the largest three numbers
in python list. My first attempt looped through the starting list, comparing
the number to the previous minimum number in the result list - replacing the
number if it was larger. I needed a get_minimum(function). It was the obvious
solution that came to my head at thr time.

An hour later I realized a far more elegant solution was to sort the list and
slice the last three elements. It felt far more elegant to me and was as easy
to understand as the initial obvious solution that came into my head.

(Though this is why I dislike these type of interview questions. the first
solution in an interview situation is not always the best or final one)

------
jariel
There's something deeply unnerving about ugly code, I don't think we can just
say 'well it works'.

I think we should maybe aspire to find a frame of reference for evaluating
when to change and not.

During development, I find iteration can be useful, i.e. when you have code
that's 'hot in your mind' and can be re-worked.

Another key that the article doesn't seem to reference is encapsulation. It's
one of the most golden concerns in software.

If 'ugly' is confined to a single function and doesn't leak outside - then
it's almost pointless to re-write it.

On the other end of the spectrum, if an API is 'ugly' it leaks all around the
code inside and out. Re-write is more expensive, but could possibly be more
worth it.

~~~
tartoran
Rewriting an ugly API could be problematic if not rewritten while fresh. Once
it starts being refed everywhere good luck.. I bet there are no test cases
either and the task becomes daunting and expensive

~~~
jariel
Yes, that's the thing. Exactly. APIs are by definition leaky. Look at Python
v2 v3 debacle. So it has to be really well planned, even then possibly not
worth it.

------
bpyne
So many good lessons for newer developers and good reminders for more
experienced ones. It was due to the Bike Shedding Effect that I came to not
appreciate code reviews. Every review seemed to break down into discussions of
variable and method names. I tried several times to create "off topic" rules
like no discussion of variable names, etc. It didn't work...ever.

Now that I've been away from mandated code reviews for quite a while I can
understand why reviewers go to the trivial. Firstly, "code reviews" are really
"system reviews". It's difficult reviewing a project whose requirements you
don't know with a design that came about through iterative design and possibly
in a language you don't use.

My current team is small and split into an integration group (mine) of two
people and a UX group of two people. My group partner and I look over each
other's projects and stay up on the requirements as the project progresses.
But it's completely informal. Yet it seems to work better than having a group
of 2, 3, X number of randomly chosen developers from my department.

~~~
Ididntdothis
That’s why pair programming works better than code reviews. Unless you have
crazy amounts of time there is almost no chance a code reviewer can really
understand the design of the change. So you end up with people picking on very
local stuff like variable names. With pair programming you have two people who
understand the history of the code and why decisions have been made.

~~~
C4stor
But seriously, naming things correctly makes understanding the global system
so much easier.

The whole point of naming things is that you don't have to dive in this 60 LoC
function, but rather can use its name to know what it does, and make a global
mental model.

If you're not naming things correctly, you effectively make it almost
impossible to review the design, and that's why people starts with asking
about names.

Reviewing names is the exact opposite of picking local stuff. Names have no
importance locally, but they have tremendous one globally.

So, please, use good names ^^

~~~
Ididntdothis
Totally agree about the importance of naming. But the big problems that are
hard to fix later come usually from design flaws, not from naming and often
are overlooked in reviews.

~~~
C4stor
I agree with you. My point, if it wans't clrear, is that I think correct
naming is a prerequisite to finding design flaws in review ! Hence we have the
same ultimate goal :-)

~~~
bpyne
I don't find that to be the case. I'm usually looking for bigger things like:
1\. making multiple trips to a relational database when a single statement
joining tables can make it one trip; 2\. not handling exceptions that can
cause data loss or data corruption.

(I'm not trying to be exhaustive. The examples above are common ones I've seen
regularly and they make me cringe.)

I don't need to know the variable names used in these cases. The very act of
not using a relational database properly or not handling exceptions is easy to
spot.

------
dnprock
I call this, Developer Inertia: It's hard to learn others' code, but it's easy
to fix to bugs in others' code. It's easy to write your code, but it's hard to
fix bugs in your code.

It's good to have someone else looking at your work in established projects.
Pair programming provides some value to overcome this problem.

Overcome your weakness, you'll become a better developer.

~~~
hyfgfh
You call what? Can you be more specific?

~~~
KMag
I think the GP's

> I call this, Developer Inertia:

should read

> I call this "Developer Inertia":

------
de_watcher
Naming stuff is not trivial and not a bikeshedding.

~~~
raverbashing
No, sorry. Wasting too much time is BS.

Use a good name and be done with it. You're naming a variable, not the title
of your Magnum Opus.

Edit: I think people are reading too much into the initial dismissiveness and
not going past the 1st line.

More important than picking the perfect name:

\- Being consistent with the naming (you called it a 'bolt' keep calling it a
'bolt' for the same type of object and for its life through the code path. If
bolt is not a great name you can change later.

\- Being easy to remember and type. There's no point with
TurningWheelThatGoesSqueak and having a TurningWheelThatGoesSquak and a
TurningWheelThatGoesSquewk this will only make the developers go crazy.
Simplify.

And yes bikeshedding is bad. I just find it too ironic that parent's username
reminds me of the place that took bikeshedding to the extremes.

~~~
onion2k
_You 're naming a variable, not the title of your Magnum Opus._

You're naming a variable, and that name is probably the most important
documentation about that specific point in the code. A lot of the time a
useful name will be quite obvious, so you should use one. If you see code that
has variable names like a, i, myVar, value, etc then it's _usually_ a sign the
developer didn't think very hard about the code that they were writing. Using
a name that gives some context to the data the variable should hold is
_massively_ useful to the next developer to work on the code (which is usually
you, so you'll be thankful you did).

~~~
raverbashing
'a' and 'i' are perfect for indexes or range traversals. Yes, myVar is bad.

You can always read the context and see 'for value in list_of_values' or
understand what's it that you're working with right now.

Yes, be more explicit on the tricky parts, but sometimes i = i+1 is just fine.

~~~
onion2k
_' a' and 'i' are perfect for indexes_

Except they're not as good as 'index', which is obvious and provides some
meaning, so why accept single character name?

I have an eslint rule that blocks single character var names on my projects.
It makes my team develop good habits, and no one has ever complained about it.
Our code is very readable.

~~~
raverbashing
> so why accept single character name?

Because readability suffers with repeated long variable names

There's a reason why math uses i,j,k and x,y,z and it's not to be petty.

~~~
onion2k
There's also a reason why a lot of people find equations incredibly hard to
parse.

~~~
Nevermark
This is a nice semi-random deeply nested spot to observe how the topic of
naming is so cognitively attractive.

Even in the meta-sense it is being discussed here.

There is something about naming that we like thinking about, and fine tune our
thoughts on, more than a simple need for legibility.

There is a sense of ownership in picking a name.

------
loopz

      // TODO it works, but it's ugly, rewrite
      function init() {
          // some code
      }
    

What is ugly, what would rewrite accomplish? Excellent example of bad
commenting. Comments are ideally unnecessary, so bad comments just litter code
and stink it up even more. No code will live forever anyways, and it says
something about someone when they falsely believe in perfection.

~~~
dajohnson89
I write similar comments sometimes, although a little more descriptive. It's
to acknowledge that this snippet has some issues and could be improved, but
for whatever reason not now.

I also go back sometimes and do just that. without the comment I forget 95% of
the time.

------
symplee
Worth the click even to just skim the hilarious images that illustrate the
points.

------
catchmeifyoucan
I think line anxiety might be another one. Unless it’s just me. If I feel that
a function or a file is just irregularly long compared to the others, I find
myself asking if I need to break this thing up to be more abstract

~~~
shervinafshar
Definitely not just you. To quiet such anxiety, I usually ask myself whether
the length of the function is affecting the readability and speedy
understanding of the logic. Longer functions are not necessarily bad, but if
they pack so many logical units and so many scope variables, they could be
improved.

------
reallydontask
> When you look at complicated systems and clever solutions - most likely it
> took a lot of time and resources to implement it, but all you can see is the
> smooth result. It is easy to fall into this fallacy because complex
> solutions always require a lot of work, testing, and iterations.

I used to have this problem all the time with the managing director:

MD: Company X has a really nice and simple help system. Why can't we do the
same?

Me: We can, but we need a product owner, a designer, a developer and a tester
full time on this for at least a couple of months.

MD: I don't understand why we can't just copy what they did.

~~~
rendall
"MD: I don't understand why we can't just copy what they did."

What was your reply?

~~~
jacobush
We can. They have a product owner, a designer, a developer and a tester full
time.

------
kroltan
If the author reads this: Dark theme completely FUBARs the code snippets.

Sample: [https://i.imgur.com/QUO48fn.png](https://i.imgur.com/QUO48fn.png)

~~~
Deely123123
How did you change theme to Dark?

~~~
Cthulhu_
Happened automatically on my device, probably because I have macOS running in
dark mode. It's a feature. Switch your OS to the color scheme and language you
prefer and websites / apps should adjust accordingly. Should. We're not quite
there yet.

------
wwn_se
Not all code is critical and intended to maintained forever. Everyone is not
working at Google, Amazon etc. most code I write is not run daily by 1000s of
users.

~~~
ravenstine
Maybe there are just lots of FAANG engineers on HN, but it's amusing how many
people overestimate the size and scope of the projects most software
developers work on. There's so much discouragement and pessimism if a software
wasn't written to handle millions of requests, or intended to live on for 15+
years.

On the contrary, there's software written all the time that has a realistic
lifetime of a few years before it's either replaced with a 3rd party
alternative or rewritten by some disgruntled programmer. Deadlines and
business priorities often mean that foregoing what most of us would tout as
"best practices" actually makes sense. Some software is only used by 10 people
at a time, and can be maintained by one or two engineers. It probably makes
more sense for the engineers to choose what's best for them and the company
over what most people would say are the right things to do. There are people I
know who don't write tests for their software(something I don't know that I
would ever do), but each piece of software they write is small and has a
lifespan of a few years at most, and the advantage to removing barriers to
development is that they can make changes very quickly and get things done.

~~~
mesaframe
But, how would the developer know about the lifetime of the code. How would
the developer know if code will never be extended or will remain stale for the
rest of the life?

~~~
Cthulhu_
Exactly this. I'm working on a codebase right now that looks like it was
written by someone who had barely finished school, figured out how to use a
function in JS to produce the HTML for an input element inside of an XHR
response callback, then just kept reusing that over the next eight years
leading to multiple >10K LOC files. I'm not sure the original author ever
intended the application to be around that long.

------
Dowwie
I've been putting off writing about similar subjects. This is a favorite genre
for me.

I often consider path dependence in product development. The decisions we face
for any given circumstance is limited by prior decisions and experiences, even
though past circumstances may no longer be relevant.

------
hyfgfh
Also about trade-offs of clean code [https://overreacted.io/goodbye-clean-
code/](https://overreacted.io/goodbye-clean-code/)

------
msantore
Image selection for the article is on fleek!

------
bgodlove88
Best laugh I've had all day.

------
strangattractor
by who can pay?

------
crimsonalucard
The entire human race is a cognitive bias, you can't have christians and
muslims be right at the same time. One population of religious people must be
wrong and therefore a huge portion of the population must be cognitively
biased to an extreme degree of believing in something completely wrong.

It is part of human nature to be hugely biased to a completely absurd degree
and software engineers and scientists are not immune to this effect.

The worst that I've seen this in (to the best of my ability, as I'm
cognitively biased too) is in design patterns.

Typical scenario:

Object instantiation requires 50 parameters to be fully realized. Some
coworker suggested that we instantiate an object with empty members and create
50 methods on an object that each take one parameter to load the object.

Apparently having a function that takes 50 parameters create the object was a
code smell because it didn't have a fancy name. That fancy name was "builder
pattern."

If you can't see how utterly stupid builder pattern is for this case...
well... cognitive bias.

~~~
titzer
> If you can't see how utterly stupid builder pattern is for this case...
> well... cognitive bias.

You cannot win against the prevailing wisdom. Try having a public mutable
field on a class, and watch people lose their ever-loving minds. But why do we
have more than half of the classes' exposed with getters _and_ setters?
Mmmmfh.

I think the problem is that it is really hard to reason about software in the
large. So we reason about software in smaller and smaller granularity, e.g.
classes. And then we want to load down every single class with armor as if it
needs to be protected against the wild hordes of unwashed masses who will
rampantly mutate it. But most classes naturally fit into a larger unit, a
cohort of classes, and they are coupled with each other, and they mutually
share state. You can't understand the whole thing by staring at a single
class, anyway. But it _feels_ like we are doing the right thing because we are
told every class needs armor. Nevermind that now every class is 3x bigger, and
so the whole thing is 3x bigger, which makes it 9x harder to fit in your head!
Uggh!

~~~
Nevermark
Well, obviously the simple solution is not to use getter/setter methods but
simply overload the implicit get and set operators of the field when you do
need to track or react to field value accesses and changes.

Not a language option? ... Oh, so simple ... and yet, so far away.

~~~
crimsonalucard
No dude. The solution is not to use the builder pattern period. Just have a
function/constructor with 50 parameters.

Why? Because the instantiated object requires all 50 parameters to be fully
realized. Having to call 50 separate setters means you can create an "invalid"
object where only 23 setters are called. Why allow this to happen at all? It's
like a null value, why allow that value to exist on a type?

If you need all 50 parameters to be fully realized, then the logical thing to
do is to only allow the object to exist with all 50 parameters in place. A
constructor with 50 parameters insures that this fact is reality. It's that
simple.

Yet even you, with the answer right in front of your nose staring you in the
face still thought that you 50 setters is okay with syntactical sugar! That's
how powerful cognitive bias is with design patters. "Builder pattern" is a
word that lends a sort of artificial aura into what is essentially stupidly
dividing up a constructor into 50 setters for no good reason.

