"Clean Code" is bad. What makes code "maintainable"? part 1 of n
Summary
TLDRIn this video script, the speaker critiques the conventional advice on code maintainability, arguing that much of it lacks practical principles and can even be harmful. They express a disdain for books like 'Clean Code,' which they believe promotes practices leading to team conflict and poor maintainability. The speaker emphasizes that maintainable code should be easy to understand for newcomers and stresses the importance of code isolation to prevent changes from causing unintended side effects. They also highlight the fallacy of top-down code reading and the inefficiency of hiding code logic behind abstractions, advocating for a more realistic approach to coding that acknowledges the inevitability of bugs and the need for easy debugging.
Takeaways
- 📚 The speaker criticizes common advice on writing good code, calling it 'crap' because it lacks real-world applicability and often leads to misguided practices.
- 📘 The speaker expresses a strong dislike for the book 'Clean Code', arguing that its advice is harmful and can lead to more problems in code maintainability.
- 🔍 The speaker emphasizes that code maintainability is about making the code easy for others to work on, especially those who are unfamiliar with it, rather than simplifying the writing process for the original author.
- 👀 The speaker points out that code is generally not read from top to bottom, and that the way most coding literature presents code is flawed and not reflective of real-world practices.
- 🔍 The speaker advocates for a bottom-up approach to understanding code, starting from a bug or error message and tracing back through the codebase to find the root cause.
- 🔗 The speaker discusses the problems with having many subclasses that are slight variants of each other, as it makes it difficult to trace through the code and understand the flow.
- 🛠 The speaker argues that changes made in one part of the application should not affect other unrelated parts, highlighting the importance of code isolation for maintainability.
- 🚫 The speaker criticizes the 'no global variables' and 'single responsibility principle' as being too simplistic and not necessarily preventing issues like the 'whack-a-mole' state of bugs.
- 🔑 The speaker uses an example from 'Clean Code' to illustrate how hiding decision-making within classes can lead to a lack of transparency and difficulty in debugging.
- 🛑 The speaker warns against the attitude of dismissing bugs that cannot be immediately reproduced, suggesting that this can lead to unresolved issues and a false sense of security.
- 🚫 The speaker concludes by stating that the philosophy behind books like 'Clean Code' is flawed, as it assumes that code can be made perfect through simplification, which is unrealistic.
Q & A
What is the main topic discussed in the video script?
-The main topic discussed in the video script is code maintainability, specifically what makes code good or bad and the critique of common advice on writing maintainable code.
What does the speaker claim about most of the advice on writing good code?
-The speaker claims that most of the advice on writing good code is 'crap' because it is not based on actual principles or heuristics and often does not translate to real-world improvements.
What does the speaker dislike about the book 'Clean Code'?
-The speaker dislikes 'Clean Code' because they believe the advice in it is harmful, it's specific to a particular programming language, and it promotes practices that can create contention and confusion within development teams.
What is the speaker's view on the importance of code readability?
-The speaker believes that code readability is crucial, but it should be about making the code easier to work on for someone unfamiliar with it, not just about writing code that is easy for the original author to write.
According to the speaker, what is the primary way developers understand a new code base?
-The speaker states that developers typically understand a new code base from the bottom up, starting with a bug or an error message, and tracing the code by following function calls and logs, rather than reading the code from top to bottom.
What does the speaker suggest is a common mistake made by those who follow 'Clean Code' and similar advice?
-The speaker suggests that a common mistake is the creation of subclasses that are tiny variants of each other, which can make it difficult to trace through the code and identify the source of bugs or features.
What does the speaker argue is the fundamental principle of maintainable code?
-The speaker argues that the fundamental principle of maintainable code is to make it easy to find and fix bugs when they occur, and to write tests to prevent them from recurring, rather than trying to write code that can't have bugs.
Why does the speaker criticize the approach of hiding information within private classes as suggested in 'Clean Code'?
-The speaker criticizes this approach because it prevents other parts of the program from accessing potentially relevant information for finding and fixing bugs, thus breaking the principle that changes in one part of the application should not affect other parts.
What does the speaker mean by the term 'whack-a-mole' in the context of software development?
-The term 'whack-a-mole' refers to a state in software development where fixing one bug causes another feature to stop working, creating a cycle of constant bug fixing with no end, often due to poor architecture or practices.
What is the speaker's view on the importance of code tests and the 'Open/Closed Principle' in maintaining code?
-The speaker believes that while code tests and principles like 'Open/Closed' are important, they are not sufficient on their own. They can still result in a 'whack-a-mole' state if the architecture and practices do not ensure that changes in one part of the application do not break other parts.
What does the speaker suggest is the attitude of some developers who follow the advice in books like 'Clean Code'?
-The speaker suggests that some developers who follow this advice may develop an attitude of dismissing bugs that they cannot reproduce as not real problems, which can be detrimental to the development process and lead to unresolved issues.
Outlines
🤔 The Myth of Good Code Advice
The speaker begins by challenging the conventional wisdom on what constitutes good code, criticizing much of the advice as being baseless and not grounded in practical principles. They argue that many coding principles, such as those from 'Clean Code', are often too language-specific and can lead to unnecessary debates and contention within development teams. The speaker emphasizes the importance of maintainability, stating that code should be easy for someone unfamiliar with it to work on, rather than focusing on ease of initial writing. They also highlight the common misconception that code is read linearly, which they claim is rarely the case in practice.
🔍 The Realities of Code Maintenance
In this section, the speaker discusses the typical process of engaging with a new codebase, which often involves fixing a list of existing bugs rather than having the luxury of studying the code from scratch. They describe the common practice of tracing code from the bottom up, starting with an error message or log and working backward through the code to understand the flow and identify where things go wrong. The speaker criticizes 'Clean Code' and similar principles for promoting a top-down approach that can obfuscate the code and make it difficult to trace issues, leading to a situation where developers must resort to runtime debugging to understand the code flow.
💡 The Importance of Code Isolation
The speaker emphasizes the importance of code isolation, where changes made in one part of the application should not affect other parts. They argue that principles such as 'no global variables' and 'single responsibility principle' are aimed at ensuring that changes are localized and do not have unintended consequences elsewhere in the application. The speaker also discusses the concept of 'whack-a-mole' programming, where fixing one issue inadvertently breaks another part of the application, often due to poor architecture or lack of adherence to isolation principles. They highlight the need for a coding philosophy that promotes changes to be contained within relevant parts of the application.
😤 The Pitfalls of Simplification and Concealment in Code
In the final paragraph, the speaker expresses frustration with the approach of simplifying and hiding code details, as advocated by 'Clean Code' and similar resources. They argue that this can lead to a lack of transparency and understanding, making it difficult to diagnose and fix bugs, especially when requirements change or edge cases are encountered. The speaker criticizes the assumption that code can be made 'clean' by simplification, asserting that this overlooks the reality that all code can have bugs and that the goal should be to make code maintainable and debuggable. They conclude by cautioning against the naivete of such approaches and the potential harm they can cause to real-world projects.
Mindmap
Keywords
💡Code Maintainability
💡Good Code vs. Bad Code
💡Clean Code
💡Design Patterns
💡Code Isolation
💡Global Variables
💡Single Responsibility Principle
💡Open/Closed Principle
💡Debugging
💡Unit Tests
💡SOLID Principles
Highlights
The speaker criticizes common advice on good code as being largely ineffective and not based on practical principles.
The speaker expresses a strong dislike for the book 'Clean Code', considering its advice harmful.
Design patterns and principles discussed are often language-specific and not universally applicable.
The speaker emphasizes the importance of code maintainability for unfamiliar developers rather than ease of initial writing.
Code is generally not read from top to bottom, contrary to what is often taught.
The speaker discusses the inefficiency of code that is optimized for printing in books rather than for real-world use.
People typically understand code from the bottom up, starting with a bug or error message.
The speaker argues against the practice of creating subclasses that are tiny variants of each other, as it complicates debugging.
The speaker criticizes the advice in 'Clean Code' that leads to obfuscation and difficulty in tracing code.
Isolating changes so they don't affect unrelated parts of the application is key to maintainable code.
The speaker discusses the 'whack-a-mole' state of projects where fixing one bug introduces new ones.
Even with principles like SOLID and unit tests, projects can still suffer from poor maintainability.
The speaker recounts a specific bug involving an expense report app to illustrate the problems with certain coding practices.
The speaker criticizes the advice in 'Clean Code' to hide decision-making processes within classes, which hinders debugging.
Maintainable code should allow for bugs to be found and fixed efficiently, not just written cleanly.
The speaker argues against the attitude of dismissing bugs that can't be immediately reproduced.
The speaker concludes by emphasizing the need for maintainable code that is practical and not just theoretically clean.
Transcripts
So today, I'm going to start
talking about code maintainability,
the idea of what makes
for good code and what makes for
bad code.
It's a huge topic and it's got a
ton of corner cases, so I'm not
going to be able to do all
of it.
I'm not even really going to get
much but a good start.
What I will say is that most of the
advice that I've seen about what
makes for good code
is crap.
And the reason that it's mostly
crap is because it's not really
based on any actual principles
or heuristics, by which I don't
mean that they don't have things
they want to accomplish,
but those things they want to
accomplish don't actually map to
making things better in the
real world except in some
hypothetical kind of sense.
I have a very different take on it.
I'm going to kind of try to walk
you through that and hopefully by
the time we get to the
end, assuming you stick with me,
you'll see why it is that I think
that most of the advice
is garbage, especially this thing.
I did a video not long ago on
programmer books and there were
some people that said I should
have included this.
There were a lot of people that
said they were happy I didn't
include this.
I hate this book.
I hated this book long before it
was trendy to hate this book.
I took it seriously for a very
short period of time and then it
became obvious to me that
the advice in here is bad.
I mean it's just it's harmful.
I'll get into why I'll talk about a
couple of examples today, but just
for the record.
There's one for the thumbnail.
This is the Internet of Bugs.
My name is Carl and we're talking
about coding advice on how to
make maintainable and clean code.
One thing about most of the advice,
this goes for actually "Clean Code",
a bunch of other
things, a lot of design patterns.
They're very specific to a specific
language and a lot of the
principles that they talk
about that are important are things
that are not important in other
languages and are not
relevant in other languages.
I have a set of ideas that I think
are way more applicable because I've
worked in a
bunch of different languages and I've
worked in a bunch of different
frameworks and I have
found the same techniques to be
useful over and over.
Things like "Clean Code" create an
opportunity for teams to argue with
each other and for
people to point to things and to
tell each other that they're wrong
and it just creates
contention in a group and that's
pointless especially when a lot of
the advice actually
takes you the wrong direction.
I try really hard to make my advice
based on my experience and based on
what I've seen
and what I've discovered over the
years maps across a bunch of
different languages and
a bunch of different platforms.
Hopefully as we go, you'll see that.
To start with, the point of code
maintainability is to make the code
easier to work on for someone
who's unfamiliar with it, not to
make it easier for you to write in
the first place.
There's a lot of hypothetical
garbage in the advice on how you
write code.
It's not about some hypothetical
"how changes might get made in the
future."
It's about "how do you find the
places that changes need to be made
now" and "how do you
assure that you're correct in that"
and that those changes are isolated.
It's not just how easy it is for
somebody who's not you to maintain
your project but
you'll find, if you've been doing
this a while, that it's very often
the case that you're
away from a project for a while and
then you come back to it and you
have to figure it
out all over again.
It's not at all unusual for me to
be like, "Okay, what idiot wrote this?"
"Oh look, I did six months ago."
Second principle: code is generally
not read.
You don't start at the top of a
file and read down and then go to
the next file and read
down. Not if you have a choice at least.
If the only way that you can
understand a code base is to read
it like that, then the
original author failed.
There's one exception to this.
There are times that I'm hired to
audit code.
Basically I'm brought in to
evaluate the progress or lack
thereof that some other developer
has made and in that case I do
often read code from top to bottom
but in that case I'd
argue I'm not really maintaining it
and it's not about maintainability.
I'm auditing it really so that's a
different case than that when I'm
talking about.
Aside from that, I never read code
from top to bottom if I have any
choice at all.
This is where most of the
literature fails utterly.
I know as a published technical
author myself that it's really hard
to write code listings
so that they can be printed out on
dead trees and bound into a book so
that they can be
understood.
It's difficult and it's a skill to
get it to work that way but if you
think that it
makes sense to optimize your code
so that everything is two or three
lines and something
that should take half a page ends
up getting spread across three
pages so the typesetter
hasn't eased your time of figuring
out where to put the page breaks.
I think you're valuing the wrong thing.
So here's a listing from "Clean Code"
pages 30 and 31.
If you can't tell that the goal
here was to have a lot of different
places that page
breaks could happen without the
reader having to turn pages back
and forth, I don't know
what to convince you.
All of that code could have fit
easily in three quarters of a page
but it's all spread
out to make it easier to typeset.
I thought about actually drawing
arrows so you could actually see
the flow of the code
but no one's going to be convinced by that.
Either you're going to look at it
and you're going to realize that it's
stupid or you're
not and if you think that that's
the way code ought to be written, I
don't know what
to tell you.
In the real world, the way people
figure out code is from the bottom
up. When I'm going to fix a new code
base, right, when I'm brought in to
work on a code base,
it's never the case that someone
brings me in and says, "Okay, here's
a new code base.
Go take a look.
Take a week.
No big deal.
Read the whole thing.
Figure it out."
All that kind of stuff.
Never happens.
What happens is I get brought in to
work on a new codebase and there's
already a list
of "we need this fixed and we need
this fixed and we need this fixed
and we need this added."
There's always a laundry list of
stuff that already needs to happen.
If there wasn't already a bunch of
stuff that they needed to get done,
they wouldn't be bringing somebody else in.
So what you do, what I do, what
most of the folks that I've talked
to do is we start with
a bug.
We start with something in the bug
that tells us what's going on.
We look for an error message, for
some on-screen text.
We look for a log statement,
something that's in the bug report
and then we search for that
string in the code base and we
figure out, Okay, here's the place
where that string
gets printed.
Here's the part where that string
shows up on the screen, Okay, I
know I'm around
here somewhere.
Let me read this part.
Okay, cool.
Now, how did I get here?
Then you go take your editor, you
say, "Right click, show me what
function called this."
And then you jump up a level and
then you figure out, "Okay, so now
I know what's going
on here.
Right click, go up a level."
And that's how you trace code,
right?
And that happens not just the first
time, but bug after bug after bug
after bug.
That's occasionally the case that I'll
get a bug and I'll know right where to go,
but generally that's because there's a
bug around there that I've already
had to deal with.
And it's not at all unusual for me
to be working on big code bases and
work on them
for a year and there'll be a chunk
of that code base that I've never
even looked at because
I've never needed to because there
hasn't been a bug in there that I've
had to worry about.
So many, many code bases just
utterly fail to make any sense this
way. And a lot of times it's because of
these top down techniques that
supposedly make code
more clean, but it actually just
obfuscate everything.
One way that that happens is you
have a whole bunch of different subclasses
that are just
tiny little variants of each other, right?
So I'll say, "Hey, IDE, right click,
show me what method calls this."
And I'll get like a dozen answers
because they're all of these subclasses
that all do
different things and I have no way
of knowing which one it was that
happened to be calling
this portion of the code when the
bug happened.
And so now I have to give up on the
being able to find it in the code
base.
I have to switch gears.
I have to get the thing up at
runtime.
I have to reproduce the function.
I have to figure out how to debug
it or add print statements to it or
something.
So I can figure out which one of
those classes was the one that was
actually there when the
actual bug happened and then I can
continue working up the stack.
And that's just a nightmare.
It takes way more time than it
needs to.
So here's a listing from chapter 10
of Clean Code.
This is page I think 148 or 149
something like that.
I don't know if your addition is
the same as mine, but when you're
trying to go up code
and you hit something like this
with all of these different "this
extends SQL" you've
hit a dead end or really you've hit
a maze of twisty little classes
that are all alike.
And there's no way of knowing which
one's which.
It's a productivity killer when you're
trying to figure out what's going
on, where this
bug came from, or where this
feature needs to get added. In the
case where I do manage
to get up to the top-ish.
And so I have some idea of the
slice of the app that's actually
relevant to what I'm working
on.
Hopefully I didn't have to go to
runtime to figure that out.
But once I figure that out, the
next idea is that everything that I
need to do in order
to change the bug that affects this
thing down here should be part of
that slice.
I mean, not necessarily the exact
methods all the way down in the
stack, but this class
and this class and this class or
this object and this object and
this object or this function
and this function and this function
or this file that calls this file
that calls this
file, right?
The rest of the code, the rest of
the application hopefully is not
containing stuff that's causing
this particular bug because that
makes it really, really hard to
chase down.
So that's part of the whole code
isolation thing, right?
So the corollary to that is when I
make a change in this stack, that
change that happens
in this vertical part of the
application should not break stuff
elsewhere in the application.
So this is where you get these maxims
like "no global variables," "single
responsibility
principle," "open closed principle,'
all this advice that you get
basically boiled down
to "changes here, shouldn't break
anything not here," saying it that
way, saying "changes
here shouldn't break anything not
here" is harder for a beginner to
understand.
It's a lot easier for a beginner to
understand "no global variables," right?
But it's not at all unusual for me
to find projects where that's
completely violated.
It's a state I call whack-a-mole.
I wrote about it in my book where
basically every time you change
something here, every
time you fix one bug, some feature
that used to work stops working
somewhere else.
It's a nightmare and it has killed
more projects than anything else
that I've ever seen and
it usually happens right at the end
of the project when everything is
trying to come
together and you're doing final QA
and then oh, there's a bug over
here and then you fix
that and oh, there's a bug over
here that wasn't there yesterday
and it's always the
result of poor architecture, poor
leadership, poor developers.
Maybe the developer was incompetent,
maybe they were over their head,
maybe they weren't
paying enough attention, maybe they
just didn't care enough, it's hard
to know.
But by the time you get to the end
of the project and you get in this
whack-a-mole state, you
generally can't fix that without
making major structural changes
that just cause the project
to go way over budget and way over
time.
The thing is it's not at all
unusual for me to find a project
that is in that whack-a-mole
state and then go through it and
find out that it's got unit tests
and the unit tests
all pass and it's got open closed
principle, it's using the solid
principle and it doesn't
have global variables and all that
kind of stuff.
Those principles just aren't
sufficient, right?
You can follow all those rules and
still end up with whack-a-mole
because they're just
not good enough.
It's better to have the principal
say, look, don't do whack-a-mole,
right? Make sure that as you're coding
that the stuff here affects the
stuff here and the stuff
over there doesn't affect the stuff
here in vice versa.
So let's talk about a specific
example.
So imagine a hypothetical expense
report app.
So imagine you get a bug ticket
that the user submitted receipts
totaling $25.13 but the
report only said $25 even.
So where did the extra $0.13 go?
So I've had a variant of this
problem, it wasn't exactly this, it
wasn't expense reports
but it was the same kind of thing
where the amount ended up being
wrong because of something
that you'll find out on in a minute.
So for purposes of this discussion
though, I'm going to talk about it
as if it was expense
reports but this is more or less an
actual problem that I've run into
in real life.
So I'm looking for why it is that
this stuff that I can see added up
to $25.13 didn't end
up saying $25.13 and where the hell
the other 13 cents go? And I look for
places that the cents
get cut off.
I look for places that things get
rounded, I can't find any of that,
I have no idea what's
going on, takes me days off and on
fighting with this, I still have no
real idea.
And then what happens is I'm
looking at the specs and I'm
looking at the accounting manual
and I realize that city day in
question, the per diem for that was
$25.
Turned out that the issue wasn't
that the $25.13 was getting truncated
$25.
It's the $25.13 was being ignored
completely because the system had
decided that those
receipts weren't relevant because
that person was supposed to be on
per diem that day.
And in this particular case, the
conditions by which the program
decided "is this a per diem
kind of expense report" or a "look at
the receipts from the restaurants"
kind of expense report
was wrong.
In that particular set of
circumstances, it made the wrong
choice because of weird accounting
reasons.
This is a particularly egregious
kind of bug and it's specifically
caused by the technique
on page 109 and 110 of Clean Code.
I'll put those on the screen.
What this does is it tells you that
you should return an amount that's
either the per diem
amount or it's the total of the
receipts amount and not tell
anywhere else in the program
which one you picked. You're
explicitly told by Clean Code at
this point to hide why you
made that decision from the rest of
the program.
And this is under the guise of
error handling.
It makes me furious.
They're telling you that the right
way to handle a potential error is
take information
that might be relevant to finding a
bug and prevent anywhere else in
the code from having
access to that information because
it's hidden inside the private part
of this class.
But it breaks the basic fundamental
"stuff over here shouldn't be
affected by stuff over
there" thing because it draws a line
between "over here" and "over there"
and it hides all
of this stuff in these private
classes and doesn't let anybody
else in the rest of the
program see any of that information.
This is an utter failure of the
philosophy that goes into Clean
Code and honestly most
of the programming advice that you
see. It assumes that you can make
code cleaner by simplifying
things and that's just wrong
because that assumption is only
valid in a world where the
code you're simplifying is a
hundred percent guaranteed to be a
hundred percent correct for all time and it's not. Even if
you have a 100% test
coverage, and they did
more or less in the case that I ran
into, that doesn't mean you
understood the requirements!
That doesn't mean the requirements
haven't changed!
It just means that the code you
wrote matches the understanding
that you had at the time,
and in this case as with a ton of
cases there was a corner case-there
was a set of circumstances
under which the original programmers
understanding was wrong- maybe even
the initial specification
was wrong.
It took a while for that to get
triggered and noticed but because
of the way this thing
got coded it was impossible to
understand what was going on
without digging into the
internal private methods of this
one class way the heck over there
that gave no indication
it was relevant to what was going
on over here.
The other thing that happens in a
lot of times in these clean code
systems, a report
will come in of a bug that happens
to a user and it will be impossible
to figure out how
that could have happened and it
will be almost impossible to figure
out how to reproduce
that and so the developer just says
"oh I can't reproduce that" and just
closes it and that's
the kind of attitude that these
kinds of books foster and it's the
kind of attitude that
a lot of people that follow these
books have, because they write code
in such a way that
it's really hard to find where
things are going wrong and that way
they can pretend
that "oh that wasn't really a
problem because it couldn't be
reproduced so it doesn't really
matter it doesn't really count
against me it's not a real bug"
Oh it's irritating! And
the reason that they do this, the
reason that the people that write
this kind of book believe
in this kind of code hiding and
believe in "simplifying" code and
"cleaning" code this way
is because they fundamentally
believe that their code is perfect
and no code is perfect.
The point of maintainable code is
not to write code that can't have
bugs, It is not possible
to write code that can't have bugs.
The value of maintainable code is
writing code so that
when the bugs happen, and they will
happen, you can find them, and you
can fix them, and
you can write tests to make sure
they don't pop up again. And it's a
completely naive viewpoint
from people writing these books and
a lot of people have this attitude
and usually those
are people who are developers who
have never actually had to support
the code they've written
they just write the code and they
throw it over the wall of the Ops
team and when something
goes wrong "it's their problem not
my problem it couldn't have been my
problem because I
had to have written the right thing
because all of my unit tests passed
so it's gotta
be somebody else's problem it can't
have been my problem." Maybe the
author just doesn't
know any better maybe the author is
one of those developers that's
never had to actually
debug code maybe the author is
catering to the like Prima Donna
mindset that these books
encourage in developers and tends
to be present in the developers
that follow these authors.
Either way it's just it doesn't
work in the real world. It's
counterproductive. There are
a ton of other examples from clean
code and other books like it that I
could go through,
and I'm sure others will come up in
future videos, but I can't cover all of it
in one video, so I think I'm gonna call it
here, before I get too more
irritated. Okay. Deep cleansing
breath. Remember, the Internet is
full of bugs, and anyone that says
different is probably
trying to sell your their book on
how to simplify your code. Let's be
careful out there.
Weitere ähnliche Videos ansehen
5.0 / 5 (0 votes)