♪ Oh, oh ♪
♪ Oh yeah ♪
♪ Red light, red light, red light ♪
♪ Hey yeah ♪
(rock music)
(applause)
- I don't need to introduce
you to Kate's technical
and professional accomplishments.
However, what some of you might not know
is that Kate has helped
start a organization
which helped myself and three other women
attend this conference.
With that said, it's my honor
to introduce Kate Gregory.
(applause)
- Good morning, thank you Annie.
Yeah, thank you for the Include plug also,
and I'm so excited seeing
all the Include shirts
wandering around the
halls yesterday and today.
Thank you all for stopping
by and talking to us.
What I wanted to talk to
you about this morning
is what I've been doing
for the last few years
about how do you write simple code,
how do you design things to be simple?
It's a word that I've been using,
but I realized that I
needed to back up a bit
and actually explain
what I mean by simple,
because it's a deceptive word,
and different people think
it means different things,
so there's more than one
way to keep things simple.
I teach C++ as a thing, like intro to C++,
C++ fundamentals, but
I also teach in groups
and one on one individual pieces, right?
Here's a new thing that we
just got, here's how it works
and why I want you to use it from now on.
And no matter what you're teaching,
when you are teaching something
you tend to start very simple.
You deliberately leave things out.
We should be making sure
that this number is even
or positive or a number
in the first place,
but we're not gonna do that
because I wanna focus on something.
So if I'm trying to teach someone
what the modulo operator is in C++,
a whole pile of stuff about
how you can't take it of a string,
it's not helping anyone, right?
We're focusing on that one thing.
And so we tend to assume
our inputs are reasonable and work.
We certainly assume they're not malicious.
Talks about buffer overflow attacks
and those sorts of things are wonderful,
but they don't really belong in
how do I call a standard function
to parse a string into an integer?
Very often, an algorithm
for getting something done
will work in two directions.
Here's what you do if the two
elements you wanna move around
in the collection one is before the other,
and here's how you do it
if one is after the other.
We will often only show half the work.
And that, I guess, we are
doing to keep it simple.
That is not the simple I'm here
to talk to you about today.
We don't do those things, by the way,
because we're stupid or bad or lazy.
We do them for very good reasons.
We are not trying to teach the universe,
we're trying to teach this
one little grain of sand.
And so we show this one
little grain of sand
and we let the learner
concentrate on that one thing.
If every time we taught
anything we surrounded it
with all of the error
checking and malice checking
and unit testing and best
practices, there would be no room
for the thing the person's
supposed to be trying to learn.
I'm a Canadian.
Marshall McLuhan was a Canadian, he said,
"The medium is the message."
This is a true thing.
We write samples that will fit on a slide.
We write samples that will fit
on a single page of the IDE,
using whatever screen you're likely using
when you're teaching the person.
That's what the whole
we're only gonna do one direction thing
tends to come from, and it's not wrong.
That's what I really
want you to understand,
all of these things
come from a good place.
We're trying to lower the
cognitive burden on the person
who's trying to learn
what we're teaching them.
Even scrolling can be a cognitive burden.
So when you're talking to a beginner,
when you're talking to a newbie,
you do a certain kind of simplification
to ease their learning process.
And I'm not here to say
that that's bad, okay?
The other reason that we do
some of these things in samples like this
is because they are completely imaginary.
It's also why the variable
names are so terrible.
You have a vector of integers called V
because it's only a vector,
it's not the unfulfilled
purchase orders, right?
Because that's not what you're doing.
You're not writing an
order management system,
you're showing people
how to sort a vector,
and so your vector ends up called V.
But in real life,
after someone has received
that kind of training,
life is more complicated than that.
In real life, you have to check,
you have to make sure
that you got the sort
of input you expected.
You have to do the calculations
forwards and backwards,
up and back, before and after.
So of course the code gets bigger.
It gets more complicated, it
has to be more complicated.
But unfortunately,
some developers, maybe
even almost all developers,
they start to internalize something.
We're not beginners, so
we don't want simple.
Real life is complicated.
And look at me, look what I can do, right?
Like this wooden carving.
I don't know how long that
took, but that's amazing, right?
I will point out to you it's behind glass
because it's so brittle that
if someone were to touch it,
it would break.
(audience laughs)
Have you not ever once said half in joke,
as my mother would say,
and whole in earnest,
if it was hard to write,
it should be hard to read.
(audience laughs)
If it was easy, anyone could do it.
We have a little of that in our hearts,
and that's what I want to address
when I talk about simplicity.
This is a word cloud from a survey
that the foundation did of C++ developers.
Now to be fair,
the question specifically
asked about difficulties,
so the fact that you see the
word difficulty in big letters,
it wasn't like they said,
what do you think about C++?
They said, tell us
about your difficulties,
and so we got these words.
But hard to understand,
difficult to understand,
impossible, and my favorite, nope.
(audience laughs)
Look for keywords.
There's constexpr.
There's references which
might mean the ampersand thing
or rvalue references,
but could mean books and tutorials, right?
We don't know what that word
means without the context.
I don't see auto.
I don't see noexcept.
People didn't say key words
when they said what they didn't,
what they found difficult,
but they sure did find things difficult.
So what I wanna advocate
for today is simple code
as defined by this list of adjectives.
Not short code, not
using-small-words code,
not see-Jane-run code.
What I call simple code is
code that explains itself.
When you read it, you know what it does.
You understand it.
It says find unfulfilled orders,
ship ready orders,
update order list.
It's got words in it.
It's almost like reading a story.
You understand it, and you
understand it on the first try.
Code that surprises you,
where you go, wait, what?
Oh, that's a bitwise or, okay, fine.
I don't want that, that's not simple.
Code that fools you
the first time through,
code that needs a comment to
explain what it really does
as opposed to what it looks
like it does is not simple.
Transparent code has nothing to hide.
I'm not against encapsulation,
in fact, encapsulation
is one of the best ways
to achieve simplicity, but
obscuration is a different game.
I've had pushback from my
last bullet on this slide,
that code that meets all these things,
that lays itself out in front of you,
tells you what it does, hides nothing,
does what you think it
does, becomes pleasant.
That you could actually enjoy
working in that codebase.
And people have said things like,
if it was fun they
wouldn't pay you to do it.
So let me tell you a secret.
It can be fun and they will
still pay you to do it,
because they need it done.
And if you're doing it
well, it really is fun.
Okay, I'm going to say
that simpler is better.
So like spoiler alert, yes.
But you know, I'm pretty
active on Stack Overflow
and all the other Stack Exchange sites,
and they have a hate-on
for subjective questions.
So if you go to the travel
site, you're not allowed to ask,
which is better to go on
vacation, Italy or Switzerland?
Because that's not a
question with an answer
that can be correct or incorrect,
and that's the problem
with is simpler better?
First of all, better than what?
But also, define better.
So let me ask some slightly
more detailed questions.
It might be better because
you can write it faster.
It might be better
because it has less bugs.
It might be better because
it's more performant.
It might be better because
it's easier to read
or easier to change or,
as I mentioned, more fun.
So let's take a look at those.
Is it faster to write simple code?
No, no, no, no, no. (chuckles)
Here's another wooden sculpture,
very different from the previous one.
But was that like an
afternoon's work for someone?
No.
Making things smooth and
even, making things match up
can be just as hard as making
things ornate and complicated.
It takes work.
In fact, there's that famous quote,
I'm sorry this letter is so long,
but I didn't have time
to write you a short one.
The first time I wanted to
use this quote in a talk,
I went to the internet to confirm
who wrote this talk, this quote.
Opinions vary.
Mark Twain, if you're an American
you probably think Mark Twain wrote this.
Blaise Pascal gets a lot of credit,
and Samuel Johnson gets a lot of credit.
So apparently you just pick an author,
I'm gonna pick Margaret Atwood.
As Margaret Atwood famously said.
(audience laughs)
But the thing is it is more
work to write a shorter letter,
it is more work to write simpler code,
you need to build new habits.
You need to look at
code that works, right?
You ran the tests, it's
good, it said seven,
you're like, yes, I was going for seven.
But you're not done because
you're gonna review it,
you're gonna revisit it.
You're maybe going to refactor it
so that it goes from being code that works
to code that works and
everybody can see why.
That's effort.
But maybe it's effort for a purpose,
because it turns out that usually
when you make this code simpler,
it is also more correct.
If someone writes something
that is complicated across many axes,
it's very long, it's maybe spaghetti code,
the variable names are horrible,
there's no functions, there's no classes,
nothing is explained,
it's riddled with comments
and the first couple of comments
you read aren't correct,
they say sort but there's
no sign of any sorting
that kind of thing.
That code often has bugs in it.
Really simple example, RAII.
It's Wednesday, so we've
got two full days behind us
and two and a bit days in front of us,
and I have heard someone say embrace RAII
at least five times, and I will
hear it another five, right?
And it's not a new concept,
because there's still people who aren't.
When your cleanup is in the destructor,
you can't forget to clean up.
That means using RAII makes
your programs more correct.
Is that why most of us do it?
I see no hands.
Is it because it's way
easier to write that way?
Yeah, right?
It's simpler for us.
Put the cleanup in the destructor
and carry on about our business.
The destructor will be called
when the magical close brace occurs,
or the exception's thrown
or the return statement or whatever.
So it's simpler code,
no one has to read through the
six different cleanup blocks
scattered throughout your long
function, but there's no bug
of that sometimes we
don't flush the file thing
that happens when you're doing
all your cleanup by hand.
And that's not just a
one-off against RAII.
This way of writing
code, this way of saying,
yeah, it works, but
now I need to make sure
that it shows that it works,
actually, some bugs disappear
as you're doing that,
because you thought it worked,
but maybe it didn't quite.
I really advocate,
and I will have more tips
about how to be simple,
but you can see I'm mushing
them in with the benefits.
I really, really advocate, look for places
where you're setting up
future inconsistencies.
So if you have two functions
that are very similar,
they perhaps take a different
number of parameters,
in the future, every time there's a change
we have to change both of those functions,
and they can become inconsistent.
If you write one function,
and this doesn't always work,
but if you can write one
function and give a default value
to one of the parameters of that function,
well now, tautologically,
there's only one function,
which means there's only one place
you have to make the future change
which means you can't get out of sync.
So you stay correct going
forward into the future.
And also it's simpler for everyone.
I've been on those projects.
I remember once we were in a language
that didn't have a mechanism for us
to do the kinda overloading
we needed to do,
so we were doing
copy-and-paste inheritance,
and they wanted a super simple change.
Let's say it was a wording change,
it wasn't, but it could have been.
And I said I'll have to make
the change in 18 places.
This is how many developer
hours it's going to cost you
to make the change in 18
places, make the 18 commits,
do the 18 tests, and they're
like, it's not worth it.
If we could have changed it in one place
they would have done it.
So, any time you're taking advantage
of it being the 21st century,
you're going to take away those efforts
to keep things in sync later on.
The problem with
copy-and-paste inheritance,
or even just copy-and-pasting
the same 50 lines of code everywhere,
is those 50 lines of code
should stay the same,
and they don't.
So when you make a
function that gets called,
then you only have to change the function.
You can't get inconsistent.
I'll say embrace templates
if no one else does,
but I think a few other people have.
If you've got a bunch of
functions that are very similar
but take different types,
there's no inheritance involved,
you can still write a templated function,
and again, now you just have to change
the templated function instead
of all the similar functions.
It's more work, right?
You're writing the shorter letter,
you've copy-and-pasted your
way to a bunch of functions
that look a lot like each
other but they're great.
But if you close the door and move on,
what are you setting up
for when there's a change in the future?
And here's a weirdness, and
I noticed it first with RAII,
but it happens elsewhere.
When you have all kinds of
complexity all over the page,
and you decide to do some encapsulation,
you invent some kind of an abstraction,
and you move this five
lines and these 11 lines
and these 27 lines, into functions,
maybe destructors, but
it works for anything,
obviously the calling code gets shorter.
It also gets simpler
in the sense of being
expressive and transparent
because the functions have
names, which is amazing.
So you can read now in the calling code,
instead of reading 400 lines of code,
you're reading words
from your business domain
that talk about orders or employees,
and that talk about shipping and updating
and pricing and refunding.
That's great, but,
we've only swept it
under the carpet, right?
Well no, weirdly, when
you go into the class,
the class ends up pretty simple.
It's got like five member functions,
some are five lines long,
some are 30 lines long,
they've got good names, they've
got parameters with names,
it all makes sense, and you're
left kinda looking around.
Where did all the complication go?
And I have to tell you,
I see it evaporate.
That's a gift.
What the heck, we'll take it.
I'm also going to plug
other people's code.
Who likes other people's code?
It's very early in the morning
but I'm not getting many hands at all,
maybe a fifth, and some of them were slow,
like wait, maybe I should say
I like other people's code.
(audience laughs)
I love other people's code.
We'll start with this reason,
they already tested it.
Especially if it's a standard library.
I probably have at least
one maintainer in the room.
They tested it, they've thought about
what if you're assigning to yourself
and all that stuff that
you forget to check for.
And they have more correctness than you,
simply because they've
started it before you did.
What about faster?
Wouldn't that be great?
If it was fun, and
maintainable, and readable
and you were proud of it, and
it was also magically faster?
Probably not.
Compare these two lines.
The simpler one is like,
hey, let's just use auto.
Auto is great, thank you, love it,
embrace auto, almost always auto.
But whatever a P is, presumably
some kind of person object
if it's in a collection of people,
maybe it's expensive to copy.
Maybe not, this might
be a dinky little loop,
this might not matter.
But it's not faster to
do it by value, right?
It's close enough to
the same or it's slower.
So in order to get the benefit,
you have to know something.
And you also have to be
able to retrieve something
right at that moment,
so right while you're writing the loop
you have to say to
yourself, maybe a reference.
And that level of knowledge,
it's not just knowing it,
but having it pump up and tell you
at the exact right moment to use it
is non-trivial to achieve.
So the absolutely naivest
version of most code
is not as fast as the version
where you come back and make it perform.
In the same way as the
naivest version of most code
is not as simple as when you come back
and make it more readable,
more expressive, more transparent.
I am never going to advocate
that you choose simplicity
over performance,
but you will notice that
the word if is in bold.
If a real choice exists,
go with performance,
but that's a big if.
Compilers are amazing.
Optimizers are amazing.
If you get the brilliant idea that like,
oh, this variable inside
this loop never changes,
I should move it out of the loop.
Yeah, you are like little
baby, says the optimizer.
And guaranteed they're better than you
if you haven't measured yet.
Guaranteed.
And don't measure your
debug build, come on.
Measure your release build,
see if you even have an issue,
and if you do have an issue,
then go for performance in
that case, but not before.
And hey, let's mention libraries again.
Guess who might be faster than you?
Again, because it's their job
and it's because what they do,
but also because they
know a little bit about
how the rest of the
library is implemented.
So in any given situation
they may be able to write
something that you could not.
I don't want you to think
that writing the way I want you to write
is nothing but a gift to the future.
Yesterday I got a lot of Twitter traction
by asking you to imagine that
your code will be maintained
by your own child when they're a grown-up.
How would you write differently then?
But it's not just that.
You're gonna be in this code
again this afternoon, right?
As soon as you finish
typing it you read it over
to see if it's correct or not.
And then you test it, and
if you get a surprise,
you're going through in the debugger.
So from the moment the code
has left your fingertips,
you benefit from it
being this kind of code.
I did some work once in Perl,
and I once watched other
people work in APL,
which uses a lot of Greek letters,
and we used to joke that these
were write-only languages.
And some C++ kinda has
that write-only feel to it as well.
But you are the very first
reader of your own code
and you do it like microseconds
after you've finished writing it.
So be good to you as well as
to your imaginary successor.
And of course, there's you in 10 seconds
and there's you tomorrow and
there's you in six months.
That super clever trick that you pulled,
you may really regret it in January.
You may really, really regret it in 2020.
And when you've changed laptops
and you don't have the document anymore
that you wrote about how the trick worked,
then you're really, really sorry.
I come back to this because
I get argued about it.
You can really enjoy reading things.
You can read over code and go
in to make a change and say,
oh, this is wonderful.
I just go into the sales tax
section and I make this change,
and I think that's all I need to do.
Is there any better feeling than
I think that's all I need to do?
Because we don't really believe it.
Whatever we've been
tasked with, we are like,
(inhales) I'm going into the dragon now.
The government changed the
rules, I may be some time.
And then, oh, I just have
to change the enum, okay.
That's fun, embrace that.
Try to write code that gives you that
because it's probably
you, do it for yourself.
And I've mentioned other
people's code a couple times now,
I've told you reasons to like it.
It's tested, it's well designed,
they thought of the
edge cases, it's faster,
it's already done, you
don't have to do it.
But I gotta tell you something
that you're probably not gonna like.
Other people's code
can be beautiful.
Hands up if you've ever
written beautiful code.
Lots of hands, but you're not me.
(audience laughs)
I write beautiful code!
Maybe more than one person
can write beautiful code.
Why is the canoe here?
It's one of the few pictures
that's my photo credit
instead of something Creative-Commonsy.
It's my picture of my canoe.
And this canoe is famously
slightly older than James McNellis.
(audience laughs)
It's beautiful, right?
It's insanely simple,
it has no moving parts.
We say that a lot metaphorically,
that's true literally.
And if you know anything about boats,
when you look at it
you see where it can go
and where it can't go and what it can do.
It can carry a lot of stuff,
you wouldn't take it down rapids,
and it can float in very shallow water.
A C++ programmer famously asked
me, "You made this canoe?"
Well yes, with my partner,
we made this canoe.
Did you cut the wood into
the little thin strips?
No, I did not.
I went to the wood store like a heathen
and I just bought wood,
and I made a canoe.
And you can go to the
library store like a heathen
and buy a vector, or find if,
and you can make a beautiful program.
Because other people's code
is not just some ugly junk
that you have to put up with
because it has benefits,
it can actually be beautiful.
And when you embrace that, and don't think
that you're the only one
who can write anything good,
you are making better code.
You're getting all those
other benefits too.
So I come into businesses these days,
they very often have a legacy
that they are not thinking
of as a gift from the past.
It's not like they just
inherited a diamond necklace
from a great-aunt they
didn't know they have.
They have legacy code, I hear albatross.
And usually the only person who
understands it is long gone,
and they need to do something,
and it's not clear what the something is.
And I say what the something is
is we need to take your
giant wooden statue
that has to be behind glass
because no one can touch it,
and turn it into something
that's very smooth,
very polished, and very strong,
that doesn't have a lot of scratches
and pits in the surface,
and that you can work with from now on.
It's not fragile.
And that is hard.
This is not Play-Doh, right?
This is hard work.
You have to know an awful
lot to make code simpler,
or even to evaluate the
simplicity, the readability,
and the expressivity of
code, whether you wrote it,
or whether somebody 20 years wrote it
and you're picking it up now.
You have to know this language.
You have to know what's changed
recently or 25 years ago.
You have to know the libraries.
If you don't know that there's
a function in algorithm called any of,
which returns true or false
depending on whether any of the elements
in a collection meet some predicate,
then you're going to
write the loop to do that
instead of calling any of.
And you have to know our idioms.
Whether that's if P equals, single equals,
some function that returns a pointer,
it's not a mistake, right?
We're simultaneously calling the function
and testing if we got a
non-null pointer back.
Some people come in from other languages
and they don't understand
that line of code at all.
Should you take it away?
Should you say, first we'll
get P and then we'll test P?
You have to know the idioms
that everyone around you is using,
which is tricky if you're a team of one.
The kind of simplicity
that is complete and elegant and readable
is nothing at all like, I left
that out to keep it simple.
We're using the same word,
but it's a whole different concept.
So we're going for this
polished-piece-of-metal simple,
which is hard to do.
So perhaps I've convinced you.
My speaker notes for this
slide say wah-ha-ha-ha.
(audience laughs)
I'll give you some things you can do.
They're actually a lot
harder than they sound.
Simple, but not easy.
The first thing to do is
to gain an appreciation
for this metric of evaluating code.
This code looks simple,
this code does not.
That's an actual step in the process,
and if you begin to know
what simple code looks like,
then you will move towards
writing simple code from the get-go,
but you will also notice
when you have somehow accidentally created
that trees and doors
and people and whatnot
wooden statue behind the
glass, and think about,
how can I make this simpler?
How can I take a moment and
shorten up our letter here?
Do some refactoring, do some renaming,
do some encapsulating, and
make it back into something
that's expressive and transparent and fun.
And as I mentioned earlier,
one important way to do that
is by taking away opportunities
to be inconsistent.
This is not Lego.
Do you see?
This is Lego-Duplo interop.
That's a thing.
And how is Lego-Duplo interop achieved?
Even Lego-Lego interop,
how is that achieved?
How can you take a four and
a two and use them together?
Because of total and utter consistency.
They're all consistent on
the tops and on the bottoms
and even between the different sizes
so they will work together.
And if someone's just
like, oh, for simplicity
I felt like making
bigger holes on this one,
that would not be okay.
So valuing consistency is a big part
of what I want you to start doing.
One really entry-level behavior, names,
and I find names in comments.
Here's a comment.
Here's some code.
(audience laughs)
Ah, you're not beginners.
They say a consultant is someone
who will borrow your watch
to tell you what time it is.
Here's what the consultant does, right?
You said right there, I'm
going to make a total,
then you called it I.
What's wrong with you?
(audience laughs)
Now what may be wrong
with you, not this person,
because they used a ranged for,
but we used to have to type our variables,
like every letter of them, ourselves.
It's true.
I also had to walk to school
uphill both ways in the snow,
and I had to type my whole variable name
instead of typing a couple of
letters of my variable name
and having the autocomplete help me out.
So, names help.
Here's another thing that helps.
This is a solved problem, right?
Good news, boss!
I've solved the adding up of the numbers
in the vector problem.
Wow, that's been intractable for decades.
(audience laughs)
Functions have names.
That's why we say don't write raw loops.
It's not for perf or security,
it's because the loop doesn't have a name.
It gets a comment, sort the collection,
or we could call sort, just saying.
But there's more to it than this.
When you replace a bunch of
magic numbers with an enum,
they all get names.
Enum classes, if you can, please.
When you make a constant,
const, constexpr, whatever,
just please, not a macro, again
you gave the number a name,
and now people are reading it.
If price is greater than approval limits,
stuff that happens when you need approval,
like it's obvious.
Whereas if price is greater
than 472 or X three,
I don't know.
It's amazing because it's so easy to do,
it's amazing what it does to your code.
Variable names.
Single-letter variable names.
Here's the problem with
single-letter variable names.
We put it to you in our terms.
You build a lookup table.
You say to yourself, A
means this, I means that,
D two means this, D seven means
that, and four pages later,
you are looking up in your lookup table.
Let us save an indirection, okay?
We're all in favor of saving indirections,
let's eliminate that.
Take a look here.
There's a comment.
You can all be consultants
after this talk.
A couple thousand lines later,
the variable gets a value,
that's a different peeve, right?
Wall of variables at the top,
like literally 50 lines of
variables being declared,
and then we'll start using them.
Why didn't we go double D
three equals getGrossReceipts?
Because we didn't.
Another couple thousand lines later,
if, I don't know, whatever,
apparently we're gonna
take 5% off D three.
Do you still remember what
D three is in real life
if you're not on a slide?
If you are wandering around
in a multi-tens-of-thousands-of-lines
file?
Or if D three is greater than D seven,
now you have to look two things up
in your little mental lookup table.
Tools do this, okay?
I don't even have to do
this, the tool will do this.
Double totalRevenue, notice
I can now drop the comment.
And now a thousand lines later
when it says totalRevenue
equals getGrossReceipts,
it's telling me a story,
I'm agreeing with it,
it's open, it's obvious,
there's no lookup table,
I move right along.
Later, if whatever, totalRevenue
is multiplied by .95,
if totalRevenue is
greater than oldRevenue,
I mean, there's now a
story that was not there
even if you could remember
that D three was total revenue,
it didn't lay out as a story.
Now it does.
I know this seems like it
couldn't possibly really help.
It really helps.
And you can charge a lot of money for it.
I want your functions to be short.
Oh yes, 15,000-line file.
Oh yes, actual chat conversation
with someone else on the team,
if you go to line 8,752,
you see what it does there?
It's a real thing.
In Visual Studio, by the way,
Control + G to go to a
particular line number,
important information in my life.
But I don't want your functions
short so you can print them.
Who printed a page this decade?
Very small number of hands.
This year?
Pretty much the same hands,
we got some recalcitrant printers.
(audience laughs)
I don't print things anymore.
I used to print things when
they were seven pages long.
When they're half a page long
I don't need to print them.
The only reason I'm printing
them is not to scroll.
But, I don't, this isn't about printing.
I want your functions to be
short so they can have a name.
You cannot give a good
name to 5,000 lines of code
unless it's called Do All The
Work or Process Everything
or Run The Business.
(audience laughs)
If you can't name it, maybe it isn't it.
I'm not saying you can't have
and in your function names
because sometimes you do.
But I'm saying if you
don't have a name for it,
maybe it is two functions or seven.
And this is a good place to mention
what I call emotionally short functions.
And I've mentioned
emotionally short functions
in a couple of different
contexts and I have said,
I don't know how long
standard accumulate it is,
and every time I do that,
someone's in the room
who wrote standard
accumulate and who tells me.
But I don't know how long
standard accumulate is,
I don't know how long find if is.
I don't care.
I never step into it, come
on, do you ever step into it?
It's zero lines as far as we're concerned.
So we're not responsible.
That's fantastic, that's the
shortest possible function.
It's a magic function that
you don't need to know about,
and it doesn't have to be something
from the standard library.
I work on a project where
they load stuff from disk
up into a ridiculous data structure
that I'm not gonna describe to you.
And then they work in the data structure,
but there are certain things that happen
that require you, the easiest thing to do
is to just flush that data
structure and reload from disk
because it's gonna be
calculated differently.
And this function's
called Update Database.
And there are these places
in the code where people,
and it's almost like an invocation.
Wait, before I do this, we'll
just call Update Database.
That may or may not be
right, but here's the point,
I've never stepped into Update Database.
It was literally written
in the last century
and I don't know how it works.
I know what it does, I've
never stepped through it.
As far as I'm concerned,
it is zero lines long.
It might really be 10,000 lines long,
and there's a good chance that
it is, but it hasn't changed,
it doesn't need to be
maintained, it just is.
If you have stuff like
that in your universe,
and I bet you do, those
are also short functions.
And leave them alone, don't
get in there and simplify them.
They're already simple by being invisible.
That's as simple as you can get, okay?
So don't assume that I want
everything 10 lines long,
only the things that we all
have to read and step through
and understand and modify and maintain.
And the new stuff, please do not write
a 10,000-line function this week.
This is real code.
Are you having fun?
(audience murmurs)
Is it pleasant?
Is this pleasant?
I don't think this is pleasant.
Is it unsurprising?
Oh no, it is not unsurprising.
It is trying, if you can't
tell, to parse a command line.
And there's something there
called lpCmdLine, Hungarian notation.
Other people's code.
Stringstream, now we'll parse it out.
You know what this does, right?
I'm taking that command line
and I am getting driver name
and pipe name out of it,
I threw Hungarian under the bus
and I also switched to strings.
And I say if the driver
name is still blank
or the pipe name is still blank,
that's when we do not have a happy path.
Otherwise, I do some
decorating to the pipe name,
which if you haven't had to
do named pipes on Windows,
pay no attention to that.
Then I return true.
These aren't quite identical code.
This version writes a space
into the middle of the input string,
sorry, writes a null into the
middle of the input string
so that it can use strcpy,
which will stop when it gets to the null,
and then it puts the space back,
which means you can't
give it a const string,
because you're changing it,
which means it's very
hard to mock and test.
That's how I came to change this code.
This is not simple code,
you puzzle, you wait,
why are you suddenly writing a null
into the middle of the string?
Oh, it's a hack so that you can use copy.
This fails on a lot of points.
This code is simpler.
It is shorter but that's
really not the deal, right?
Requires you to know
what a stringstream is.
Also requires you to deal with
the person in the coffee room
who gets on the streams
are slow hobbyhorse.
(audience laughs)
I am parsing two strings
out of a command line
for a server that runs for weeks.
(audience laughs)
Remember, don't choose
simplicity over performance
if that's really the issue.
Some more simple things you can do.
Long, long lists of parameters,
10 parameters, 20 parameters,
especially when they're all the same type.
That's fun, isn't it?
Here I really feel it's about abstraction.
If this thing takes seven bools,
make a struct with seven
bools in it that have names,
and pass an instance of that struct.
And you can set the
struct up before the call,
everybody can see what's happening.
You're setting verbose to true,
you're setting auto-print
to false, and so on.
Rather than, oh look, they
called false, false, false, true,
but they should have called
false, false, false, false.
That's an easy bug to spot.
It's all the more so
if your abstraction actually has meaning.
The number of rectangley
oriented functions
that take four integers
that I have seen is, well,
about a hundred more than
anyone should be asked to see.
And what do the integers mean, right?
Are they X one, Y one, X two, Y two?
Or are they X one, Y one, width, height?
Or height, width, or whatever,
we got lots of possible,
X, X one, X two, Y one, Y two?
Like, you do not know, right?
And you have to pray
for good parameter names
if there are any at all,
or go and read the code,
and those are both awful.
But if I change this function now
so that it takes a rectangle, and that's,
I know that's kicking
the can down the road,
but someone's defined rectangle for me,
or so that it takes two points.
Now this function is easier to call,
almost impossible to call
wrong, for the rest of time.
Three strings and a float,
what is that in your world?
Is that a person?
Is that some business object like an order
or an invoice or a policy?
Make that.
And I literally advocate, if
you're maintaining old code,
if you're writing it you should
be writing it right from,
you shouldn't be, what
are you doing typing
bool, bool, bool, bool, bool?
Stop it.
These things tend to grow.
The function doesn't take any parameters,
then it gets one bool,
then it gets another one.
20 years is a long time,
it gets seven bools,
the first three aren't
used anymore. (laughs)
(audience laughs)
But when you go into this code
for the first time in 15 years
and you gain the understanding,
say, of what these four integers
are, I actually advocate
for recording that understanding
immediately by refactoring.
And just right then and there,
as soon as you get, oh, it's two points,
make it be two points,
because you have put in
90, 95% of the effort
by figuring out it was two points.
The actual changing the
signature, we have tools, right?
The actual changing the
signature is almost nothing,
almost as quick as typing the comment,
and then it's done forever.
Sometimes the reason you have
so many parameters is because,
like the function that couldn't
come up with a good name,
it isn't really one function.
The top third of the function
uses three of the parameters,
the bottom third uses the other,
like, it's really
independent of each other.
And sometimes I will
advocate for breaking it up,
and I don't mean that you
have the big 10-param function
calling the three smaller functions,
I mean, you actually have
the three smaller functions
and the call site calls all three of them.
Now I know, if there
are multiple call sites
and they all have to remember
to phone, to call all three,
maybe I'm setting you
up to be inconsistent,
someone could drop one of the three calls.
Hey, these rules contradict themselves.
Judgment and experience
still count for something.
There are times when you do it this way,
there are times when you do it that way.
I didn't say this was going to be easy.
The other way to get rid
of a ton of parameters,
I know all the cool kids are
all free functions these days,
but member functions and
encapsulation do serve a purpose.
If this is taking,
especially multiple properties
of some individual object,
maybe it should be a member
function of that object.
And this may be a multi-step thing,
it may start by taking six strings,
and then you may decide to do
some encapsulation elsewhere
and so when you get to this function,
it's like E.FirstName,
E.LastName, E.DepartmentName,
and you're like wait,
if this was a member
function of the E object,
we would have a lot less
junk to pass back and forth.
It doesn't always happen, but look for it,
be open to it.
This is arrow code.
And I know some of you are
going to try to read it,
and I can't make it smaller,
so please stop trying to read it.
I will read it to you, okay?
It's called Calculate, and
it has three tests in it.
It says, is X less than the limit?
Okay good, we can keep going.
Down at the bottom there's an else
where we set an error
code and return bool.
It returned false because it was bad.
Assuming X was less than the limit,
we say, well, is Y positive?
Is it greater than or equal to zero?
Actually non-negative.
Good, we'll keep going.
Are we shipping?
Excellent, and here's where
the actual magic happens.
You notice in order to
fit this on the screen
I couldn't have any
magic, but there's magic,
and we return true, and then
everything else is elses.
This code is not simple.
Imagine that you are a junior developer,
you are new to the project,
and we have a fourth rule,
and it is your job to put the
fourth rule into this code.
I'm pretty sure you're
going to put your else
in the wrong place on your first try.
It's also hard to read,
it's hard to understand.
You can very clearly see
that we only do the magic
if X is under the limit,
if Y is non-negative,
and if we are shipping,
but the rest of it can be
a little hard to follow.
This is the exact same logic.
I did not change the signature,
I always get someone who's like,
well, you shouldn't return bool,
what about expected, blah blah blah.
In the most simple refactorings
you're not changing the signature,
so the behavior is unchanged.
We still return a bool,
we still set this member variable error.
But now, you notice the font got bigger.
You notice there's a lot less
lines of code on the screen,
and the magic is here at the margin,
rather than before, way over to the side.
And that's important.
Finding the good stuff easily.
But also, if you were the person
to add the fourth test, right?
Nothing to that.
Put it anywhere.
Because that's part of
the problem, it's like,
well I wanna test after X but
before Y because optimization,
and you can do that
here, anywhere you want.
The errors are exactly
with the conditions.
The conditions, by the
way, are flipped, right?
So I was before, I was like,
if X is less than the limit,
now I'm saying the badness,
if X is greater than
or equal to the limit.
So you do have to do that,
and it is a possible bug,
so tests will help you.
But now your code is more
readable and it's telling a story.
You're clearly checking
all your preconditions,
and if we're still here,
now we're gonna do it.
And that's why it got shorter.
I don't need an else
when you return the if,
and that's the only thing
that took away lines of code
and made the font bigger,
is not having to have elses,
but it's also what eliminated
all that sideways scrolling.
I'm a big fan of excess consting
as an exploration technique.
So I don't mean const
correctness, I really,
I consider that to be the
price of admission, okay?
If your code is not const-correct,
make your code const-correct.
But this is going beyond that.
So you just literally, you get const
in your clipboard buffer,
and you go plunk, plunk, plunk,
plunk, plunk, plunk, plunk.
All the local variables,
all the parameters,
the ends of all the member
functions, everything,
and then you build.
No thinking, just build.
And of course, it's gonna fail, right?
Some of those things are not const.
And you're like, that's
fine, I'll take those off,
but some will stay.
Especially in a long calculation
that came from a paper process
or a spreadsheet process,
there's all these like
intermediate things.
So they say, here's the total revenue,
and there's a big long expression,
and here's the total population,
and there's another big long expression,
and then they're like, so the ratio is,
and they divide them,
and they never change
those variables again.
They've got names to lead
someone through the calculation,
so marking them const,
like it's not preventing a
bug or enabling optimization
or all the reasons we tell you to const,
but when I come into this
code 15 years from now
I'm gonna say, ah, you have 10 variables,
but you don't really.
Only two of them continue to change
after their first value.
And so as I read through
the next thousand lines,
I only have to have my little alert table
keeping track of just those two.
The others are just numbers with names.
They're not consts in the sense of an enum
or a compile-time expression,
but they don't change
once they get a value.
I have less to keep track of,
my cognitive burden goes down.
And it's not a hard thing to do
because I said like, don't think,
just paste, paste, paste,
paste, paste, build,
and then delete, delete, delete,
'til the compiler's happy,
it's like you're back in first year again.
This hurts hard
when you have out params
and in/out params, right?
In fact, some people
may have been mumbling
that to themselves like,
reasonable point until
you have an out param.
You know what I'm gonna say?
Yeah, don't have an out param,
that's exactly what I'm gonna say.
I consider it a feature
that this makes it painful
to have out params,
because you slam the const on everything
and then suddenly it's not const, right?
And all you're doing is passing,
that's also why I say don't think, okay?
Because you see it being
passed to a function,
don't stop and figure out
if the function changes it,
the compiler will tell you.
This is a good opportunity
to discover in/out params and out params,
and to perhaps replace them if you're able
to change the signature of the function
that's being called so that
it returns something useful.
And you can return some
encapsulation of your own devising,
a struct or a class, you
can return std::optional,
and yes, you can even return a tuple.
Here's how I tend to return tuples,
first the function returns nothing at all,
just does something,
then it returns an int,
then it needs to return two ints,
then it needs to return
two ints and a string,
so I use a tuple and eventually I give up
and I encapsulate something sensible.
If you get all of that all at once,
you'll probably just go
straight to the struct.
And again, maybe this
should be a member function
of that in/out thing.
So if you have an update employee info
that takes an employee by reference, what?
Why isn't that the employee's
update info member function?
Which can be non-const,
I'm not objecting to non-const functions.
A lot of times out
params are kind of a sign
that maybe we're not as
encapsulated as we should be.
You're all here, so you
already want to keep up
with what's happening in our world.
You need to spread the word to everyone
that there is no
substitute for keeping up.
I did a talk here last year
about the core guidelines.
One of them is like don't use const_cast.
Alternative to const_cast,
the mutable keyword.
A bunch of people afterwards on Twitter,
that mutable thing
looks kinda interesting,
I should try that.
I wonder when that came into the language.
It was a difficult question to ask.
It turns out I had a
24-year-old at the time
and mutable is older than him, okay?
Not a new concept, sorry, idea.
(audience laughs)
If you're gonna use loops,
can you use a ranged for?
I know your fingers do the other for
like without you thinking, right?
You go loop and it comes out,
but can you use a ranged for?
Can you reprogram your fingers?
I still meet private constructors.
And they're sometimes correct, right?
You got some kind of factory
thing, Singleton, boo, hiss,
where there is actually a
friend or a member function
that's calling the constructor.
That constructor should be private.
But when I go looking
and I can't find any code
that calls the constructor, I'm puzzled.
Why did they write this constructor?
Then you're, oh, of course,
they're suppressing
this constructor, right?
But we have delete for that.
When you don't say delete,
when you make it private,
I go looking to see who calls it.
When you say delete, mystery over.
So it's a simple little change,
but it's communicating to me,
compiler probably couldn't care less
which of the two you do,
but I would prefer you make them deleted.
Non-static member initializers,
not particularly new,
yet still not being
used, I don't know why.
They are easier and faster,
and you can't forget
when you add another constructor,
and you probably won't forget
when you add another member variable.
So for default values of member variables,
use non-static member
initializers, can you?
You be grateful that you have them.
Oops, the library.
I didn't say keep up with the language,
I said keep up with the library.
Std::optional is great.
Std::expected is great.
Things are happening,
people are writing things
so that you don't have to.
If you think you should
write a simple wrapper that,
please stop and use the library.
If it's not in the library
and you know it's also not on its way
and you can't get it in some
sort of experimental version,
then maybe you will, but honestly,
anything you can think of in 15 seconds
is probably not that unique and original.
So the library keeps changing,
keep knowing that the library is changing.
When I was very young, I've
been paid to program since 1979,
and I didn't learn to program
in that job, I already knew,
I had a great-uncle who
I didn't see very often
as the way of great-uncles, and he said,
"Oh, look at you, you've grown up,
"you're an adult, what are you doing?"
I'm like, "Oh, I'm a programmer."
Oh, (clucks) you were such
a creative little girl.
(audience laughs)
What?
I think he thought it was
like being maybe an operator,
that I was mounting tapes
and putting card decks into
card readers and things.
I'm as creative as it's possible to be
because I make worlds
out of the skin of my fingertips, right?
And we all do,
and I think we all have learned
that programming is a creative activity.
Now I wanna convince you
that programming is a social activity.
It's asynchronous, but
you are communicating.
You are making code
and leaving behind code
and it speaks.
It's not the immediacy of
yelling at someone in Slack,
but it is communication.
And I'd like you to
consider a concept I learned
from other people who
quoted to me Rico Mariani
called the pit of success.
You want your people to fall
into the pit of success.
What I mean by that,
what he means by that,
it's a wonderful analogy,
the most obvious thing,
the no-brainer choice, the
zero-effort behavior, they win,
for whatever definition of win you want.
So if you have people working for you
and you would like them all to
start coming to conferences,
you make it so they all go to conferences
and they have to go to a lot of trouble
not to go to conferences.
If you have people working for you
and you would like them to
show leadership abilities
and lead things and make decisions,
you set things up so
that's what they're doing,
and they have to go to
some trouble not to.
When you leave code behind,
you can leave behind a pit of success
for the person after you to fall into.
You set up defaults, and
if they follow right along
and don't think, the
goodness will happen, right?
That's what you're setting up.
And not just next chronologically,
it can also be next,
sitting at the next chair.
Think about what you're saying to people
with what you write.
I've railed a couple times
about being inconsistent,
about setting up opportunities
to be inconsistent.
When you leave that behind,
you're not leaving
behind a pit of success.
They have to climb like
a mountain of success
by changing it in all the
places it needs to be changed.
If there's only one thing,
they can't be inconsistent
when they do the obvious thing
which is change the single function,
they're done, they win, they succeeded.
You set that up for them.
Same with non-static
member initializers, right?
When they add another constructor,
they're not gonna add an
inconsistent default value.
All of these things
set them up to succeed.
If you've got good encapsulation
and you've put your
cleanup in the destructor,
they don't have to remember to clean up.
When someone later adds a throw
that didn't use to be there,
the bug of like, oh, yeah,
so I need to clean up in the
catch is never going to happen.
You set that up.
Who's played chase the const?
These are not happy hands.
Sometimes you ask people
questions, they're like,
yay, I do that, I love that,
and that was not what
those hands said. (laughs)
If you start const-correct,
nobody will ever have
to play chase the const
and swear at you, but also
they'll probably follow along.
When they see member
functions marked const
when they write a new class,
they're going to stop and think about
whether they should mark
their member functions const.
When they see parameters
being taken by const ref,
they're going to almost think
that that's the boilerplate
that they should be following,
so you're setting them up to be right.
You write good names, you
write short functions,
that's what we do here.
You write A two B seven X three,
they'll be right behind you
with A three and B eight and X four.
Now I do sometimes get pushback
because I'm telling you how to write,
telling you how to write for yourself,
I'm telling you how to write
for the people after you,
telling you to set things
up like they were babies
who need to be led.
And people say, we don't need guidelines,
we don't need advice, we
can read 50 lines of code,
we're big boys and girls.
So I wanna show you this picture.
I bet you think this is
a picture of a river.
It is not.
I mean, the river's in the picture,
but that's not why I took it.
I took lots and lots of
pictures of this river.
I took a picture of this guardrail.
This guardrail
astonished me by existing.
You see, the day before
I took this picture,
I took this picture.
It's a volcano.
Down where you can't see, is lava.
(audience laughs)
Every 15 or 20 seconds,
it start to make a noise.
And the noise gets louder
and louder and it reeks,
you wouldn't believe how it
reeks, and then lava comes out.
There's blobs, they look like rocks,
between me and the man I don't
know there are drops of lava.
In fact, shortly after
I took this picture,
a lady in flip-flops came by and told me
that I needed to move a
little further up the mountain
because some lava had landed
where I was standing two weeks before,
and she wouldn't want
me to be hit by lava.
It was very nice of her.
(audience laughs)
There's no fence, right?
You stand as close to the
lava as you feel like,
and if Mr. Raincoat wanted
to jump over the edge,
none of us could have stopped him,
but he's a big adult and he
didn't jump over the edge,
nobody jumped over, you see
people have gone higher.
So after this experience, when
I was walking up this river,
I'm like, "This is the only
guardrail in the entire country,
"I'm taking a picture of it."
(audience laughs)
If you look really closely,
there's a woman in the river.
This doesn't keep you out of the river,
it keeps you from falling into the river.
I would have thought maybe
keeping you from falling into the volcano
would have been higher up the
priority list, but whatever.
It keeps you from falling into the river,
later you end up in the river anyway.
If you wanna walk in the
river, walk in the river.
If you wanna stand on
the edge of the volcano,
stand on the edge of the volcano,
but I kinda liked having the fence
because it kept me from
falling accidentally, right?
That's the deal.
When you're saying follow
this rule, follow that rule,
have short functions, give
good names, this isn't a wall,
this isn't a 10-foot
higher barbed wire thing,
it's just some wood.
It just keeps me from
falling accidentally,
and I welcomed it, I wanted
it, I took a picture of it.
And when I'm in some of your codebases,
I'm begging for something I can hold on to
like a little wooden rickety fence.
So when you go in and you
refactor and you encapsulate,
you're not trying to
lock people out of things
that aren't good, you're
just trying to make it
the natural, easy thing to
do things that are good.
Just a little bit of a guide.
In which spirit, I'd like to
plead with you not to do this.
I will confess that I don't
really know what this does,
I found it on the internet.
(audience laughs)
But you can tell an architect made it.
It says in it abstract, and concrete,
and there's dotted lines, and inheritance.
It's probably a lot of
work, whatever it is.
Is it simple?
Can you see what it does?
You cannot.
I found a blog post about
abusing design patterns,
and my slides are gonna be available
and there's a link in the slides,
and I took it and I converted
it all to C++, and it works.
Here's what it does, first
you need a factory pointer,
which of course you
call by getting factory
after calling getInstance
on the factory maker,
and then you can create a subject,
and then you can create an observer
and attach the observer to the subject,
then you can set up a command,
because the factory will
also create you a command,
and then you can finally
execute the command,
whereupon the program prints
out hello world and exits.
(audience laughs)
Now what's wrong with this code is
there's only one kind of factory.
I'm not passing any
parameters to get factory.
There's only one kind of subject,
there's only one kind of observer,
and there's only one command.
All this flexibility not being used.
And we can laugh, but
we have all lived this.
We have all been in systems
that could work for four
different database vendors,
two of whom are out of business.
And no one's ever, ever
changed database vendors.
I worked for a while with a client
that had written something
using all Microsoft stuff.
So it was .NET, SQL Server,
everything was .NET,
IIS, you name it.
And their salespeople got some pushback
and said, "We would never buy this
"unless it supported Oracle."
So the developers had to go back
and change every spec of that program
so that it worked with Oracle or SQL.
Spoiler, that deal didn't close anyway,
and nobody ever wanted Oracle again ever.
And that's not unusual.
That's our life.
If you write flexibility
before you need it,
you maintain that flexibility
for the rest of time,
and nobody really knows
what anything does,
you can't tell that
this prints hello world.
For what?
If you need it, you need
it, you get a benefit.
But if you don't need it and
you don't get the benefit,
you just saw this cool
diagram on the internet
and wanted to be an
architect, please don't.
But that leads me to a paradox,
because I told you that
abstraction was good,
told you that short functions were good,
splitting things up was good,
giving things names was good.
But then I'm like,
"Oh, this has too many
layers of indirection."
Well again, experience and judgment count.
This isn't a mechanical process
that will do all of your thinking for you.
And the paradox is that every single thing
that you can do to make code simpler,
that same behavior can make
the code more complicated.
If you want to run a loop
from zero to number of
items in a collection,
or if you wanna say
if outstanding blah blah
is greater than zero,
should zero perhaps be
in some sort of an enum?
Should we have some constexpr Z-E-R-O
so that we all know it's
zero, not have a magic number?
That's not making our code simpler.
It was great when it was 472,
it's not so great when it's zero.
So every single thing
that can make code simpler
can also make it more complicated.
And I cannot give you the simple rules
for writing simple code.
All the rules will say,
usually, maybe, a lot, not many,
unless you have a good reason.
And that's not a flaw in C++,
it's not a flaw in software development,
it's a law of the universe.
Have you taught someone to drive?
How fast should you go?
Well, the speed limit.
You just ran into someone going
less than the speed limit.
Oh, the speed limit,
or whatever the person
in front of you is going,
whichever is less.
That's good, a dog ran out on the road.
Okay, the speed limit, speed
of the person in front of you,
whichever is less, or slow
down if you see obstacles.
Okay, we're going the speed limit
and it's a very sharp corner,
and the road went that
way but you did not.
Okay, you also sometimes have
to slow down for corners.
I haven't done the weather yet, right?
And that's just the first question.
Have you played what lane should you be in
with a nervous driver?
(audience laughs)
I'm a new grandparent, very
proud of being a grandparent.
But if my daughter were to say to me,
the baby is crying,
what is my simple answer
for what to do about that? (chuckles)
(audience laughs)
Questions can be simple, but
they don't have simple answers.
It's true about what
lane you should be in,
it's true about the crying baby.
The answer to the crying baby, by the way,
step zero anyway, is pick them up, okay?
It's after that that it diverges.
Should you use exceptions?
How long should a function be?
Is this a good variable name?
Tell me how I would know if a
variable name was good or not.
They're simple questions, they
don't have simple answers.
They can't have simple answers.
It's not like, well, if
we keep working at this,
we'll be able to prove that no function
should be more than 43 lines long.
We will never have that rule.
But if you have the value,
if you want your code to be expressive,
transparent, understandable, reassuring,
then you will answer not
the general question,
how long should a function
be, but the specific question,
how long should this
function be, is answerable.
So let's do something harder,
because giving things good
names and keeping things short
and all of that, you get some judgment,
and you get judgment
from using bad judgment
because that's what gives you experience.
I hope you write like that from the get-go
but you can bring it in.
That's not the whole thing.
You want big gains,
you change your team.
Here's an example someone gave me.
Obviously written by a very smart person
because it's using a special integer type,
so they must be very clever.
Unfortunately, GetSize returns
a different special integer
type that's 16 bits long.
So some people when they
see this say, wow, C++,
it's so complicated.
You have all these
different integer types,
I have to keep track of
which ones are eight bits
and which ones are 16 bits
and which ones are signed
and which ones are unsigned.
I want some Play-Doh now, I
don't want all this complexity.
And I'm like, that's not the problem, man.
The problem in this code
was that I and GetSize have
nothing to do with each other.
They have no relationship.
GetSize is a free function,
I don't know what it looks at
to figure out the sizes of things,
and I is just a local variable.
If we had a real collection,
we could iterate over the
collection in a variety of ways,
and we wouldn't need to
memorize integer types.
So when you meet a little complexity,
sometimes you're tempted
to haul a bunch more complexity out,
especially because you're smart,
and you're creating that wooden thing
with the sculptures and the doors and the,
has to be behind glass.
And you step back and say, actually,
what's really going on
is a different thing.
Those two are different
because they're unrelated.
Fixing them to be the same,
but it's still coincidentally the same,
that's not really fixing it.
To really fix it,
your code reflects the
relationship they truly have.
Well, that comes up to a real hard thing.
You can learn to recognize things.
A ranged for, I'm touching
every element in the collection,
I should use a ranged for.
Sean's famous line from five years ago
that I love to quote, this
is obviously a rotate.
Two pages of code that were
not obviously anything.
(audience laughs)
But you can learn, and
you can recognize things
from the standard library.
You can recognize a ranged for,
you can recognize all kinds of things
and plunk them in instead.
Standard library has a stack.
Do not need you to write a stack,
do not need you to write a
JSON parser this weekend.
Come on, hands up.
Yeah, got some confessions,
or a logger, or something
to go get over HTTP.
These are solved problems, my friends.
But then, can we go too far?
Who's heard of an immediately initialized,
no, immediately invoked
initializing lambda expression?
Yeah, you see this is a crowd,
a third of you have heard,
which is a ton, right?
If I went anywhere else,
I would not get 1/3 of
you having heard of it.
It's a way cool thing though, right?
You have some variable,
you'd like it to be const,
but initializing it is super complicated,
so you can initialize it
by calling a function.
That's obvious, but that
refactoring is hard,
so you have to figure out
what the parameters are to the function.
If you just slam a lambda in
there with a reference capture,
you can just, you literally
just put the braces and stuff
around the code that's already there,
and then you stick a pair of parentheses
out the end of the lambda to invoke it.
And it's magic, and the
variable can be const,
and people who don't see those parentheses
do not know what you did.
And I love this idiom for this reason,
it's right on the border.
It's not a ranged for, we all
know what a ranged for is.
But a lot of people don't
necessarily know what this is,
so if you use it, are you
making your code simpler
or are you surprising people,
which I said not to do
because that's totally a surprise like,
wait, it's being invoked?
Ha, cool!
But, wow, can I have
my last half hour back?
(audience laughs)
You need to know where the
things you wanna use exist.
It's not simple to surprise people,
so it's not enough that you know it.
I want you to replace
your complicated things,
not just with idioms,
but with familiar idioms
that express your intent,
with well-known library classes
that others will recognize.
Hopefully everyone's gonna
recognize all of algorithm
and all of numeric, and if they don't,
we have cppreference,
but there are other things
that are maybe less well known.
You want to introduce appropriate
abstractions in your code,
so that people say,
oh, that's a rectangle,
I know what a rectangle is,
that's a purchase order, cool, right?
But not something that
hides what's going on.
Because while you're doing all this,
obviously we're not omitting,
we're not going back to Play-Doh,
we're not like, oh, we only
do forward to keep it simple.
All our needed capabilities
are still there,
the core information,
the guts of what this
thing does is on display,
that's the point is
expressive and transparent,
so you don't hide everything
behind a wall of injectors
and adapters and factories and whatnots,
and you don't prevent future changes.
And I really want to talk
about that as its own thing.
Everyone says that Einstein says
that things should be as simple
as possible, but no simpler,
and then I learned from Phil Nash
that he actually said
something more complicated,
and someone else
paraphrased it down to this,
(audience laughs)
which could not be more perfect.
Your simplicity needs to
live in this larger context
which is time-aware.
It's simpler now to
just type 472, isn't it,
then to go set up some kind of constants
or some enums or something.
But, later, it will be much
simpler to have the constant.
So, what are you gonna do?
You're gonna do the right thing.
When you get down to the end
of a long chain of function calls
and realize you need something
that the top of the chain knows,
but which is not in the parameters,
don't you just say like,
I could have a global?
And you might even tell yourself,
well, it's not global mutable state.
Today it's not, it will be.
That's the problem with globals.
The right thing to do, you already know.
It's not always the faster thing to do,
it's not always the easier thing to do,
I'm asking you to take the time
to write the shorter letter.
The really, though, hardest of all,
you have crafted some gorgeous code.
I was once part of a team
that took a main loop,
the big engine of the whole app,
it was pages and pages long,
it had gotos, had multiple catches.
It was almost impossible
to see what it did,
like you literally needed to print it
and take a highlighter to it.
And we made it fit on a page,
and it read like English.
While not canceled, that kinda thing.
It was gorgeous, we were
so pleased with ourselves,
and someone said, I thought
this was a hard problem.
Is that what you got?
That was tough, and I have to tell you,
when you make something
look easy and obvious
and transparent and expressive,
someone can say, why did
that take you a week?
And that's hard.
I thought you were an architect,
but you don't have a
impenetrable UML diagram for me.
You have to know you're doing it right,
and you have to know you're
not leaving anything out.
You have to know you've achieved
elegant, valuable, beautiful simplicity.
But you worry, you're nervous.
Is this simple-didn't-think-it-through?
I don't know, it looks like
simple-didn't-think-it-through.
I'm pretty sure I made simple-brilliant,
I was totally going for simple-brilliant,
but now I have to stand
up in front of everyone
and say this is simple-brilliant,
and someone's gonna say,
is that all you got?
You know your language in your library,
but are the people
around you going to say,
that's not simple at all,
I have no idea what any of that is,
you just architected it up?
With your std::optional, (laughs)
(audience laughs)
and your noexcept.
But here's another one people ask me.
If I write code that anyone
can read, anyone can maintain,
what happens to my job?
(audience laughs)
Oh, you're gonna be on a lot of projects,
you're not going anywhere. (laughs)
You're gonna save the
company a ton of money,
because juniors can maintain
your beautiful, simple, wonderful code,
and juniors will be
led into the right ways
by using your code as an example.
You're not going anywhere,
except to this other project
that really needs you, right?
You will have job security,
and if there's not enough
projects where you are,
anyone else will be happy to have you.
Please don't ever write gross
code so they can't fire you.
(audience laughs)
Write code that stands up for you,
because you are leaving behind
communications to the future,
and does that reflect you?
Does that show who you are?
Does that show what you know?
That's what I want you to do.
How far have you come from a beginner?
So what do you need to do?
You need to learn.
You need to learn our language,
you need to learn the library,
you need to learn what other people know,
you need to know what's faster,
you need to know how to
measure what's faster,
you need to know what your optimizers do,
you need to know so much.
You need to read other people's code
because that's how you find out
what kinda code is readable.
You need to care.
You need to care about yourself tomorrow.
Don't just slam it in to get
it done so you can go home,
care about yourself tomorrow,
and yes, the mythical
future maintainer as well.
You need to test things.
Is this faster than that?
Do all the people in my department know
what an immediately invoked
initializing lambda is?
Don't just guess, test.
It's easy for me, I asked
for a show of hands,
somewhat harder for you, but do it.
And you need to communicate.
You need to communicate with your code,
but you also need to
communicate about your code.
At code review time, why
did I do it this way?
Or to someone else, why
did you do it that way?
Did you consider this other way?
Do you think it would
be more expressive if?
It could be as simple as,
does this loop touch
everything in the collection?
Perhaps we could consider a ranged for.
And that kind of communication
brings everyone's code
onto this simple-brilliant
side of the line,
and when you are consistently
doing that for yourself
and for everyone around you,
you are very definitely not a beginner.
Thank you.
(applause)
I have six minutes for questions,
if you can go to the mic so
that I don't have to repeat,
I would appreciate it.
- [Man] I have a question.
- Yes?
- One of the
important attributes of simple,
I would say clean code, is testability.
- Stability, yes.
- It feels like you
avoided that topic.
Could you tell if there is a reason?
- So when you say stable,
do you mean that it doesn't blow up now,
or that it stays good over time?
- [Man] I mean testable,
not stable, testable.
- Oh, testability.
Yes, absolutely.
I think that that fits
into the expressive part.
If this code does six things,
you're going to need six tests,
and if this code hides
that it does six things,
you don't know how many tests you need.
So as you make it more
expressive and transparent,
you make it more testable.
Obviously the mechanics
of hooking tests into it
is a different one, but for example,
if you take that command-line parser,
the reason I didn't like the old one
was I couldn't test it by
passing a literal string.
So it does all connect,
this kind of simpler code will
also be more testable code.
- I would say it's actually like looking,
making it more testable
would make it more simpler.
- Yes, they work together, yes.
Plus, the tests are
expressing your intent.
We'll go to this side.
- Hey there, I really enjoyed your talk.
There was a little bit of attention
I think you kinda touched on a bit,
but I'd like to ask about.
So on one hand you talk about using idioms
which are familiar so that
they're more readable,
but on the other hand you talk about
how creating a simple
codebase is a team effort
and requires collective idioms.
So then what advice would you give
for moving the bar of
where your team's idioms
so that better idioms that
your team aren't using
eventually become familiar?
- Absolutely it's about moving your team.
So you can't just start
writing things a certain way
in your piece of the code,
because the chances are
they're gonna undo it
or argue about it a lot.
So it's more like speaking,
or typing at each other
if you're all remote,
and saying, I just learned this thing
that I think would be highly appropriate
in the new section we're
working on, discussing it,
and then starting to use it,
and then maybe later someone
who's got some time says,
I actually think that idiom
would apply in this older code,
I think I'm gonna update it,
and it spreads in that way.
But the group has to know it first.
Otherwise, there's the corner of the code
that only one person knows how to work
and the others never go in,
and that is the opposite
of transparent, right?
- Yeah, thank you.
- [Kate] Thank you, over here.
- It's funny, I was asking
a very similar question.
I think these are wonderful values
and I think they're the
right aspirational values,
and I'm very excited by this talk.
I'm wondering when you're a consultant
and you're in a position of leadership,
it's very easy to set an
example for your environment.
But I've been in many situations
and I've seen many situations
where somebody on the team
wants to go in this direction,
and the leadership or their
colleagues are opposed to it,
and I'm wondering how you
deal with the social problem.
- The social problem is very real.
If you own the codebase, life is good.
And if you're in from outside
with a sign on your head
that says how much you cost
a day, people listen to you.
But if you're just one of the team,
trying to lead people forward is hard.
If you can, if making a
change doesn't require them
to learn new things, if you simply write,
for example, inverting
the arrow code, right?
They don't have to learn
an idiom to get that,
they can just see that, gee,
your functions are shorter
and I kind of understand them better,
and slowly it'll spread.
That'll be a slower process,
but that's how you would do it.
So there are things you can do that,
encapsulating instead of
passing 27 parameters.
Over time, people will say,
why is it that I enjoy working
in this corner of the code more?
And then, it's not a secret,
you can be super open about it,
this is not your magical weapon
that enables you to be better than them,
you share it with them,
and you've proven it by doing
it yourself a little bit.
- [Man] Ultimately though this
is a very long-term process,
it sounds like.
- Oh, it's not a week,
that's for sure. (laughs)
Hi.
- Hello, thanks again for
what a wonderful presentation.
It only took you what,
an hour and a quarter,
and so is that all you're here for?
- [Kate] Yeah. (laughs)
- Just to echo one of your points.
But I'd like to ask a
more serious question,
which is a kind of a
cultural, educational problem.
I've been active in my own organization
to try to promote some
of these same values,
and by the way, a book that
I've found very helpful
for doing that is Clean
Code by Robert Martin.
It doesn't touch on all of this
but it touches on a lot of it.
But one of the implications
of a lot of this,
of some of this, is that writing software
is a literary activity at certain points.
- [Kate] I would agree completely.
- Yeah, and like you, I joined this field
back when most of us didn't have a degree
in computer science, we had
degrees in other things.
And I'm a little worried
sometimes about the fact
that there seems to be a
kind of cultural resistance
to the idea that the
ability to write good names
is itself a very important ability
for our success as a community.
What can we do about that
to try to help people to understand that?
- There is sometimes, it started as a joke
that I received on purple
mimeographed paper,
and if you don't know what
purple mimeograph refers to,
go find an older person,
which literally said
if it was hard to write
it should be hard to read,
and it was one of those
things that's a joke
but it's not a joke.
And so there will be people
who resist being empathetic
and resist expressing
everything they know,
they wanna hold something back.
I knew someone who once
built a circuit board
and scraped all the numbers off the chips.
(man laughs)
That's if everyone understands
it, where's my job?
When you are good, your job is safe.
You only hide information
because you don't think you're good.
So if I can tell you that you're good,
you're going to start to share.
So if it's the person next to you,
tell that person that they're good.
If it's you, you're good, okay?
Start acting it, because good
people are generous people,
they have nothing to lose.
Be generous.
- Hear, hear.
- That's my advice.
- [Man] Okay, thanks.
- So, I'm wondering a little
bit more about teaching.
You had the example
with the architecture
astronaut hello world.
These are very useful concepts,
abstractions are good.
- [Kate] Abstractions are good.
- And usually in a teaching environment
it's hard to actually motivate
that, these abstractions,
because the problems are
never big enough, I feel,
to actually be useful.
And so you learn these things
and as a student you would either think,
what's the point, why am I doing this,
or otherwise you're thinking,
oh, this is the way I'm
supposed to write these things,
and then you abuse the
system when it's wrong.
Is there kind of something
we can do about this,
at least in an educational context?
- It's very challenging
in an educational context
because you have the 15-line example
and then you add flexibility
that it doesn't need
because it's only 15 lines long,
but you can't give them
a 15,000-line example
because you have 15 minutes.
And there's no long,
no easy thing to do,
you can tell them only
do this when it's big,
but they don't know
what big is yet, right?
So other than trying to delay those things
'til later in the program
when they have a little more judgment
as maybe as part of a culminating project
where they're working on a larger base,
but the same is true with everything.
We all work, 90% of us, maintaining code
and undergrads are never
asked to maintain code,
they're always asked to write new code.
So the education problem,
I do not have time to go into,
we'll talk more one on
one if you like though,
it is a big deal, thank
you for raising it.
- [Man] Thank you.
- I'm sorry, but I'm over time
so I can't take another question.
Thank you all, please
enjoy the rest of your day.
(applause)
