JEREMY WALKER: Hi, my name's Jeremy Walker.
I'm a developer advocate here at Google,
and we are continuing our conversation
on the three flaws in software design
with Max Kanat-Alexander from YouTube.
Now we're going to cover flaw number two,
so do you want to take it from here?
MAX KANAT-ALEXANDER: Yeah, sure.
So this is the second part in our four-part series
about the three flaws of software design.
And our second flaw is not making the code easy to change.
And you can call this rigid design.
It's a system that's basically too rigid, too difficult
to change.
And there are two ways you can accomplish this.
One of them is design in too many assumptions
about the future and the other is writing code
without enough design.
I'm going to go into what both of these
really mean and give examples.
So the first way to do this is to make too many assumptions
about the future.
The traditional way that people think about doing this
is the waterfall model, where you do big design up front
and then you do big development, and then
big QA, and then big release.
But the problem is that by the time it gets to your users,
it doesn't really match the requirements.
Ultimately, what this means is that there's
been a failure to understand that your requirements will
change.
So, what do I mean about that?
Well, you can do it in a big way.
For example, I worked on a product called Bugzilla,
which was a bug tracking system.
At the beginning of the history of Bugzilla,
it was used only by the Mozilla project--
the people who make Firefox now.
And it was only used for the browser
and a few other projects, small projects at the time.
They had about 100,000 bug reports in the system.
There was one version field, where
you could say what version the bug was found in,
and one target milestone field, which
was the branch that you expected the bug to be fixed in.
And there were no custom fields because custom fields are evil.
In fact, supposedly-- this is actually
a joke in the bug tracker world, that custom fields are evil.
There was a famous article by Joel Spolsky about it
who also wrote a bug tracker.
Now the last time that I was working on the Bugzilla
project, there were 10,000 installations
on almost every operating system imaginable,
including HP-UX, AIX, and things that you probably
haven't heard of or seen or worked with in a decade.
Users need to track bugs on multiple branches,
so they need to know that it affects
multiple different versions.
And some projects need to track millions of bugs.
Yahoo has a Bugzilla with millions of bugs in it.
And Red Hat has a Bugzilla installation
which has one component for every single package
that you can install on Red Hat Linux or Fedora,
a Red Hat [INAUDIBLE], which is something like 80,000
or 90,000 components in the system
that you can file bugs against, because they all
have different owners.
JEREMY WALKER: Oh yeah, yeah.
MAX KANAT-ALEXANDER: And we had to add a custom field system.
It turns out that not one bug tracker
fits all sizes of projects.
And Bugzilla is a widely-used product,
but there are very few developers who work on it.
It's about the size of maybe a very small feature team
at most companies.
I would say that the amount of effort that was on it when
I was last working on it was maybe
about the total of three full-time engineers,
and when I left we had 1,500 open feature
requests against Bugzilla.
So the chance that its requirements will change
is very high.
And so the problem was that we had a lot of assumptions.
For example, that it would only ever
run on one database system, based
on our original requirements.
Or we had an assumption that nobody would ever
need to track a bug across multiple projects--
the same bug across multiple projects.
But it turns out that many people needed to do that.
And one of Bugzilla's fundamental assumptions,
which is built into its external APIs,
is that product names are unique.
If you can file a bug against a product, which
is a particular categorization, then that name is unique
and our external API depends on that fact
in a way that's almost impossible to change.
So you can't have products with the same name
and different sort of classifications.
And having multiple versions for the same bug
was very, very difficult because of the way
it was designed originally.
And it turns out that when you want
to cause people to install a server product that
uses a database system, they have a database system
and they're not going to install a new one just
to install your product.
Particularly Oracle shops don't want
to install MySQL just for one product.
They want to use Oracle.
The developers before my time had written raw SQL
all over the entire application, so there
were 5,000 to 10,000 lines of SQL
throughout the application that had to somehow be made generic
across-- and the design of the system
depended, actually, upon programmatically building SQL
queries.
So it was very difficult to actually make
that work across different systems.
We did, we did.
But it was a lot of work.
You can also do this in small ways.
For example, when you use a magic number instead
of a constant, you're assuming-- you
just litter a number all over the code base-- you're
assuming that number is not going to change.
You might be right, but you're probably wrong.
JEREMY WALKER: Yeah, I've run into that before.
MAX KANAT-ALEXANDER: You'll have people say,
we will never need to blah.
This is a danger phrase.
You hear people say, we will never
need to have people outside of our team see this code.
And then one day it gets open sourced.
You'll say, we'll never need to support newer standards.
Right?
People say, there will never be an HTTP 2.0.
But it turns out there is an HTTP 2.0 now.
Nobody would have-- I think when HTTP 1.1 came out,
people were like, we're done, it's fine.
One of the most dangerous ones that people say
is, we will never need to support international users.
This is very, very difficult to bolt on to a system
after you've written a few hundred thousand lines of code.
And there's infinite numbers of things
that people can say we will never
need to and be wrong about.
But, that doesn't mean that you should
try to anticipate everything because then
you also get into writing code you don't need,
which was our previous flaw.
The way that you handle it is you
design code based on what you know now, and know for sure.
What you have actual, factual data about,
not on what you think will happen in the future.
Now, sometimes you know now that internationalization
is difficult to add on in the future.
So that's something you know now,
but you don't know now that you will need internationalization.
So the difference there is that you add code
maybe to support internationalization,
but you don't actually add the whole framework
to actually do the internationalization.
Maybe you add a minimal, minimal layer that makes it possible,
or will make it easy to make that change in the future
if you have to.
But you don't implement a whole framework
that extracts all the strings and sends them off
to translators and everything because you
wouldn't need to do that.
So another way to basically do rigid design
is to just not design your codes.
Often you see spaghetti code, which is just a long bunch
of if-else statements or 1,000-line method that just
sort of goes through-- if it's spaghetti, it's a long string.
And you could even possibly say the more you assume
things won't change, the more your code becomes spaghetti.
Spaghetti is, of course, hard to change
because you have code duplication,
people are cutting and pasting, it's very complex,
it's hard to understand what's going on at any given point.
It's not safe to change.
You could introduce a bug or you could introduce security holes
and not know.
It's funny because the more spaghetti your code becomes,
the harder it becomes to change it.
And so it actually sort of becomes a compounding problem
or a vicious cycle where it's not safe to change,
so nobody wants to change it, so nobody wants to clean it up
and it just gets worse and worse and worse.
I've worked on a lot of code like this, actually.
A lot of the time what people do is
they send me in to fix things like this.
And you could possibly even define spaghetti by the fact
there's code duplication complexity
and it's not safe to change.
And the way that you solve this problem, it's not that you say,
all code must be designed perfectly all the time.
Instead you say, the quality level of your design
should be proportional to the length of future time
in which your system will continue to help people.
Not every single system needs to have perfect, amazing design.
You don't need to sit around and think
about what should be the class structure
for my five-line script that I'm going to write as a hack
and I'm never going to use again.
But if you think that your system is
going to be used for a reasonable period of time
in the future, and you should assume
that it's going to be used for much longer than
you think it's going to be used.
Because there's been a lot of projects in the world that
were only going to be around for six months, and then
ended up being around for 10 years.
So you should err on the side of a little more design
than you think is necessary.
But you don't need to design it into infinity,
and not everything needs to have amazing design.
You just need to consider how many people am I
going to help in the future?
How long am I going to help those people for?
And use that to inform your design, basically,
or the quality level of your design.
So that wraps it up for our second flaw.
Any questions?
JEREMY WALKER: Well I did have one.
Again, I read the book so I like to lead into these examples,
but you gave a good example of this in real life.
I think you've already given some,
but I thought it'd be good idea to point out
if you remember the agency example.
MAX KANAT-ALEXANDER: Yes.
So, I had a friend who worked as a contractor-- and this is just
one example of many developers that I've interviewed
and discussed this sorts of things with.
His company was a contractor to a particular agency,
and this agency had a design for a system.
And the way that this agency wanted to do things
was they wanted to write a 300-page requirements
document before any code was written.
JEREMY WALKER: Over a long period of time, right?
MAX KANAT-ALEXANDER: Oh yeah, it took them
two years or something to write this document.
And by the time the document was actually
delivered to my friend's company,
it was internally inconsistent.
It was very hard to understand.
It was extremely complex to implement any piece of it.
And by the time that they actually
got it out the door, which was years and years and years
because they kept having to clarify things,
it really wasn't relevant to the requirements
of the actual users at that time anymore.
This is sort of like the ne plus ultra,
like the extreme of assuming that your requirements won't
change.
You spend a five year process before you release anything,
where you've designed some incredibly complex system
and you're almost bound to fail.
Particularly because a good test of truth
is throwing it against something in the physical universe.
And so until you've sort of thrown your software
against a user, you really don't know if it's right.
And so waiting five years to throw an idea against a user
means you have a lot of wrong that you have to fix.
JEREMY WALKER: I think you mention
in the book you shouldn't overdo it by a lot-- you should only
go two months out at most, or something like that?
Or was it less than that?
MAX KANAT-ALEXANDER: Bugzilla, we released every six months,
but we would release development versions as an open source
project continuously, very frequently,
and also anybody could check out our code from source
at any time and report bugs to us.
So it really depends on the framework you're in
and the type of product you're writing.
But I would say, don't look out any farther
than you're sure you know the future.
JEREMY WALKER: Great.
Well, that covers the second of the three
flaws in software design.
Please join us for the next video in the series.