♪ Oh, oh ♪
♪ Oh yeah ♪
♪ Red light, red light, red light ♪
♪ Hey yeah ♪
- 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.
- 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.
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.
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.
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.
Look for keywords.
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?
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.
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.
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.
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?
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.
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?
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.
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.
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.
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.
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?
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.
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.
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.
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.
Pretty much the same hands,
we got some recalcitrant printers.
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.
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?
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.
I am parsing two strings out of a command line
for a server that runs for weeks.
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?
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?
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)
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?
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.
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.
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.
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.
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.
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."
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.
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?
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.
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.
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?
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)
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.
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?
But, wow, can I have my last half hour back?
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,
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)
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?
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.
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.
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.
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)
- 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.
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.
- 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.