JEREMY WALKER: Hi, my name is Jeremy Walker.
I'm a developer advocate here at Google.
And today, we're going to talk about the three
flaws in software design with Max Kanat-Alexander.
Max, why don't you introduce yourself?
MAX KANAT-ALEXANDER: Well, hi, Jeremy.
My name is Max, and I wrote a book called "Code Simplicity,"
which is published by O'Reilly Media.
And I now work on developer productivity
internally at YouTube.
JEREMY WALKER: Great.
So these three flaws actually relate to the Law of Change,
and I thought it'd be good if you
talked a little bit about that first.
Would you mind just giving us a little bit of details on that?
MAX KANAT-ALEXANDER: Sure.
So in my book, "Code Simplicity,"
I go over the fundamental laws of software design.
And one of them is the Law of Change,
which states, "The longer your program exists,
the more probable it is that any piece of it
will have to change."
And basically, what this really means
is that if you want your system to continue
to be helpful to people, you're going
to have to keep changing it.
Obviously, software doesn't change itself.
People change it in response to demands.
But if you just look at it objectively as though there
were no people involved, it just looks
like the software has to keep changing.
And so that's the law that you could apply.
You can expect, basically, that you're
going to have to keep up with change,
and that the probability that it will change--
of any piece of it will change increases as time approaches
infinity, basically.
JEREMY WALKER: Great.
Well, why don't we get into the first flaw,
which is writing code that isn't needed.
MAX KANAT-ALEXANDER: Sure.
So the first flaw in software design--
so I have these things, and they're
called the "Three Flaws of Software Design."
And these are the major categories
of errors that developers make when
they're trying to design software.
And the first one is writing code that isn't needed.
Now, this sounds like another rule
called "You ain't going to need it," which is very popular.
It comes from extreme programming
and from other philosophies.
But the problem that I have with that phrase
is that's a little bit misnamed, because it's not really about
whether you're going to need the code as much as about
whether you know how it needs to be designed now.
What really matters is that-- you want really
well-designed code that's maintainable into the future,
because you want to be able to cope
with change-- without getting yourself into a situation
where it's impossible to deal with that change
or where it's hard to change the system.
And what you're writing for is you're
writing for requirements.
And in a way, it's impossible to really know
what those real requirements are really
going to be until the actual situation arrives
that you need to write the code for.
So I don't say that you ain't going to need it,
but I just say you shouldn't write code
that you don't need yet, or you should wait until you need it.
And the reason is that you run into all kinds of problems,
right?
So let's say you've written something that's
going to reverse strings, right?
That's its whole purpose.
That's the only requirement that you have.
And you decide, well, you know what?
I like reversing things.
Reversing things is easy.
Let's make it also able to reverse
all the pixels in an image.
Why you did this-- I don't know.
You just thought maybe I'm going to need it someday.
I don't need it now, but maybe I'll need it someday.
The problem is that that significantly
complicates your code.
It makes your tests harder to write.
And probably by the time that you actually
need to reverse pixels, you'll discover
that you did something completely different,
like the code that you wrote didn't perform the right way.
Or it's just not appropriate-- like you thought
you were going to read JPEGs, and really you
need to read PNGs.
It's just going to be the wrong code.
Or it's possible that it was the right code,
but because it wasn't used, that by the time
you actually get to that point, it's rotted
and it no longer works, and you're
going to have to change it anyway.
So you're going to need to redesign it.
And people think, well, I'm saving myself time
by writing this before I need it.
But ultimately, you're not saving yourself time,
because first, you wrote something that you didn't need,
and then you wrote something that you did need.
So you actually did basically the work twice-- or maybe
one and a half times of the work that you needed to do.
And you're adding complexity.
And in a way, a lot of what people
are trying to do when they do this is they're
trying to handle all the things that
could happen in the future.
But you don't know all the things that could happen.
And I'm going to get a little bit more
into that in another flaw, but this is--
and these flaws are little bit related.
Sometimes, they overlap a little bit.
But one of the errors that people are making when they try
to do this is they're trying to handle every possible thing,
and they can't.
So there's all kinds of different ways to do this.
It's very common to see people write whole classes
or functions that they don't need, right?
So like maybe they wrote a whole class
to handle reading pictures because they wanted
to reverse pixels in the image, right?
But a lot of the time, what I see
is just little lines of code they don't need.
People are doing checks that they don't need to be doing.
I see this a lot when I'm doing code reviews.
In fact, one of the most common comments
that I make in code reviews is, you don't need that yet,
or, do you need that yet?
Or what's the requirement that's actually
driving this particular piece of code?
And it's amazing how much simpler
you can make code by just continuously asking this over
and over and over-- is, do you need this?
Or when is this going to be needed?
Or do you need it now?
And the rule that you can apply to avoid this situation
or to handle this flaw is that you don't write code
until you actually need it, and you
remove any code that isn't being used.
So the second part is the part that gets occasionally missed
with the YAGNI, or "You Ain't Going to Need It"
rule-- is that once code is dead,
it should just be deleted.
And people think, well, I don't know.
Maybe I'm going to need that dead code again in the future,
but that's why we have version control systems.
If you really need it, you can pull it back out
of the version control.
Just delete it now.
Many codebases have gotten into extreme complexity
just by leaving around tons and tons of dead code.
And now nobody wants to refactor them, because they think,
oh, look, I'm going to have to refactor all of these things--
when really, 90% of those things are dead code
and should have just been deleted, right?
It's much, much more approachable to solve
the problems of a smaller code base.
In addition, the fact that you don't
have to fix the tests of code that doesn't exist.
There's all kind of advantages to code that doesn't exist
or code that isn't written.
JEREMY WALKER: Yeah.
MAX KANAT-ALEXANDER: So to demonstrate this principle,
what we're going to do is we're going to do an example code
review, and this code review is a little bit exaggerated.
The code is worse than code that you might actually see,
and we're going to do a few more steps than you
might do in a real code review.
But we're going to do it to demonstrate the principle.
So we have this requirement for our piece of code.
And it says "Parse a date string that looks like
'YYYY-MM-DD' and return an array with three integers.
The first integer is the year, the second is the month,
and third is the day.
This is internal code in an application."
It's not a library.
We're not providing this to the outside world.
And like I said, it's a little bit of an exaggeration.
So let's look at this over here.
So the code that the developer writes and submits
to you, the code reviewer, is this mess.
So up here, it's got three constants that
say this is the year, this is the month, this is the day.
It's preparing to parse a year string, a month
string, and a day string.
It has comments for every line of code,
and you can see it's going through-- if you're not
familiar with Python, enumerate means give me both the index
and the item from an array.
In this case, we're going through the characters
in a string.
And it checks, OK, if we're in the first four characters,
this must be the year.
And then, oh, is it a valid integer?
We're going to check that we have
a hyphen in the right place, next two characters
have to be the month.
If they're not digits, then we should raise an exception.
OK, then we're going to figure out that this is the month.
OK, we've validated that all these things are right,
that the format is right.
Oh, and if the string is too long, we raise an exception.
Now we check if the whole year is an integer, and then, well,
if that's-- we throw another exception.
And then finally, we create an integer for the year.
And then finally, we create this return value array,
and then we set each thing in the return value array
and then we return the return value.
OK, so this is obviously a disaster, right?
And a lot of the time in code review
when you're presented with a disaster,
it's hard to know where to begin.
You just-- frustrating.
You say, well, just go away and don't ever write code again.
But that doesn't help you, and it
doesn't educate the person whose code you're reviewing.
So I want to try to take some tack, some direction that we
can do with this.
So the first thing I'm going to tell the developer
is, the first thing you don't need in this file
is all of the comments.
I didn't need any of those comments.
I can read code.
You told me what every line of code was doing.
Surprisingly, the code became considerably more readable.
It's not good.
But if you look through it, it's much shorter.
We lost about 20 lines.
It's about 55 lines now.
The first one was 71 lines.
And you can actually kind of start to tell what's going on.
So now, you can start to see the next problem.
And the next problem is we know actually--
so we did some research.
We investigated this.
And we said your input doesn't include any years, months,
or days that are out of range-- which
means there's no 13 for the month, there's no 32
for the day, and there's no negative 1
for the year or anything like that.
And what this represents is a really important principle,
which is that it's really important to find out
what your actual requirements are.
A lot of the time, developers assume requirements,
and then they write code for those assumed requirements.
And I see this a lot.
In fact, one of the most important things
that I feel like I do when I'm writing code
is ask a lot of questions to people.
Go and say, well, what does this actually need to do,
before I go and write it.
And I find that that allows for a lot of simplification
that I didn't otherwise expect.
Because sometimes if you don't know,
you just assume that, for example,
there's a lot of inputs that you could get.
But when you actually go and find out,
you discover there's actually very few inputs
that you could get, and your code can be much, much simpler
than you thought.
JEREMY WALKER: Yeah, I thought that was
really good in the book when I was reading it.
I think I always tend to over-think
like, I've got to check all the stuff.
But then I was like, oh, that's a good point.
You should really go back and ask, is the input coming in?
What is the range-- do I have to check everything?
And most times, I think, you don't, so--
MAX KANAT-ALEXANDER: Yeah, and there's
actually a blog post that I wrote
called "The Secret of Fast Programming:
Stop Thinking," which goes over all of the ways
that you can get information without having
to sit there and think about it, and also about
how that improves productivity.
And this is really just a facet of that, a piece of that,
or an aspect of that-- which is that the information that you
need for development is very frequently not in your mind.
JEREMY WALKER: Oh, OK.
MAX KANAT-ALEXANDER: Right?
And so inventing it, or using your mind to get it
is the wrong tool.
JEREMY WALKER: Oh, yeah.
OK, that makes sense.
MAX KANAT-ALEXANDER: Yeah.
Go back to the code here.
We also note that, if you remember from the original,
he checks that every individual digit is a digit,
and then he checks that the result of all those
was a digit.
It's not like-- you don't need to do that.
That's not necessary.
And so we're getting down to almost one page of code now.
It's starting to become readable.
I can almost tell what's happening here.
We're now at 41 lines.
And so our next comment of things-- and all are doing
is applying this exact principle over and over.
All we're doing is just continuously saying,
do you need this?
Do you need this?
Or telling him, you don't need this.
You don't need this.
So now we can say that it's always
integers in the year, month, or day positions.
So you don't have to check that they're integers.
You don't have to validate their integer-ness.
And now it's becoming more reasonable.
And your input always has hyphens in the hyphen position.
There's never any other character.
We're kind of getting closer to something that the developer
should realize at this point, but maybe they haven't.
And I'll tell you what that is when we get to the last point.
And now it's 29 lines and relatively
straightforward-- getting even more straightforward.
And also, so now we're going to say you never
have strings that are too long.
So there's never a string that's longer than a date should be.
And also, this is Python, so if you
want to return three values in an array,
you can actually just do this where you just write a triple.
You don't need to create an array
and then set all of its values.
So now, hopefully the developers thought about this,
but maybe they haven't-- that what we're telling them is
your input is always well-formed.
So you don't actually need to do any validation whatsoever.
All of your validation is unnecessary.
It's very possible that in a real code review,
this would have been our first comment.
But we might not have been able to see that,
because there was so much complexity that you might have
been able to see like, oh, what are
all the things we can eliminate.
You might have needed to go through this iterative process
with the review in order to actually
be able to figure that out.
But the ultimate thing that we're saying-- your input
is always well-formed.
You don't need to have a loop.
You don't need to have a state machine.
None of this is necessary.
You can write this whole function in two lines of code,
and here they are.
JEREMY WALKER: Perfect.
MAX KANAT-ALEXANDER: So that is our code review.
And that's an example of how you would
apply this law in your actual development.
So that completes our discussion of the first flaw
in software design.
Let's go to any questions.
JEREMY WALKER: Yes, so I actually had one question.
I always like real-world examples,
and you gave a pretty good one in the book, which I thought
would be good to cover here.
So can you talk about that?
It was with the lists, I believe.
MAX KANAT-ALEXANDER: Yeah, so this
is actually something that I did.
So I worked on a project called Bugzilla,
which is a bug tracking system.
And so there's drop down fields in Bugzilla.
You can choose what the severity of a bug is
or what the priority of a bug is.
And I was like, well, you know, there's
all kinds of things we want to do.
We want to allow people to rename them.
And right now, we're referring to all of them
by its strings in the database.
We're referring to other strings.
So I was like, let's-- oh, no, no.
Oh, right.
I remember what it was.
It wasn't that we wanted to rename them.
It was they wanted to be able turn them off or turn them on,
right?
So I added all this code, particularly
at the database layer.
I added database fields to say, is this field active-- right?
But I didn't actually implement it.
I just threw in a bunch of database stuff
because I thought, well, tomorrow, I'm
going to write the change that enables it in the front-end.
And then of course, tomorrow, something crazy came up,
and I had to work on this, and I totally forgot about,
and I had a million more important things.
And then three years later, somebody comes along, and is
like, what is this?
This doesn't make any sense.
And also, in those intervening three years,
we must have had at least 10 bugs filed
by people who had manually gone in and changed their database.
And some of the code had checked if the field value was active,
and some of the code hadn't checked
if the field value was active.
And so it caused incredibly confusing bugs.
People would report these weird bugs
like-- it shows up in the search interface but not on the bugs
or it shows up on the bug editing page
but not on the bug creation page.
And finally, when somebody did implement the ability
to disable values, they actually had
to do it a completely different way, because I
had named the database fields in a way that
was inconsistent with our later convention.
So they had to rename all the fields in the database,
and then write a completely different implementation
because our framework had completely
changed in those intervening three years.
And it ended up being a total waste of time, which
did nothing but cause bugs and confusion
for anybody who ever went in and tried to do this manually.
Because Bugzilla in particular was
a product where everybody wanted to customize it.
People understood database design,
and the they would make their own changes to it.
And so they would look at the tables,
and they would actually see this and they would actually
try to do that.
And it just caused nothing but trouble,
and it did nobody any good, basically.
JEREMY WALKER: Yeah, I thought that was a great example.
Well, great.
Well hopefully, that gives you a little bit of a taste.
That covers the first of the three flaws in our series.
Please join us for the next video.