- Code Reviews.
Alright, since we're going to be focusing
on reading code in this class,
we've got to have a measuring stick,
we've got to have priorities in reading code.
And I do these things in my head while I'm coding,
we do these in code reviews,
and this is also from my perspective goes priority
when we're reading and writing code.
Number one is integrity.
And if integrity isn't your number one,
I'm counting right now, you can't work for me.
Integrity is everything.
Integrity is about being serious, about reliability,
and it's really important.
In fact, I love asking this question.
This is my cell phone,
you've got one, I have one,
everybody you know has one,
we can't live without these things.
So, here's an interesting question,
if this cell phone stops working for the next hour,
your phone, my phone,
everybody's phone in the United States,
it stops working for the next hour,
how many people lose their lives?
I know it's a serious question,
but the reality is is that if this phone stops working
for the next hour,
the answer to that question is not zero,
somebody's gonna lose their lives.
Our lives are so embedded in technology today
that if things can go bad
and people are gonna lose their lives,
this is a very serious situation,
this is why integrity has to be number one.
Do you realize that we are one of the very few industries
where people's lives are at risk,
and we're not licensed to do our job.
I mean, how long do you think it's going to last
that we as software developers
are not gonna need a license to do our jobs
because at the end of the day everyone's life is at risk.
Now you might say "Bill, come on man,
"I'm in the gaming industry."
Or "I'm in some industry where people are having fun."
No, the reality is that,
even with the mental illness crisis in this country,
your software, your bug, your problem,
may be the one that puts somebody over the edge,
over the edge, and so I need you
to constantly put into your mind
that regardless of the industry,
and regardless of the software right?
People's lives, their happiness,
everything is being dependent
on the technology that you're building,
and we got to take integrity seriously.
Now integrity comes in two places.
There's a micro level and a macro level.
At the micro level, I want you to understand
that every line of code you write,
every line of code either does one of three things.
It allocates memory, reads that memory,
and it writes to that memory.
What's really interesting is,
all our code is doing is reading and writing to memory.
What's really interesting is all we are doing
in our code is reading and writing to memory.
And yet all of those reads and writes
cause everything around us to happen.
Lights are on, you're watching this video,
phones are ringing.
It's all reads and writes.
What's even crazier is we're really just reading
and writing integers at the end of the day
to really mind-blowing stuff.
So at a micro level, integrity comes from the fact that
those reads and writes
are always accurate consistent and efficient.
When they're not, then we have an integrity issue.
We've got corruption, software now has to shut down,
because if it doesn't,
we're gonna cause more damage than good.
But there's also integrity at a macro level.
Want you to understand that really what we do every day
is solve data transformation problems.
Data or InDesign is gonna be a big part of this class.
We work with data.
And data or InDesign says that
if you don't understand the data,
you do not understand the problem that you're working on,
because the problem is the data.
And our functions and our methods,
and these encapsulations that we write
are all about the data transformations
that we have to perform.
And so if your data transformations are not accurate,
consistent and efficient,
you also have integrity issues.
And we have to apply this idea of data or InDesign,
goes very focused on data or InDesign.
I'm gonna show you this in the language
as opposed to object or InDesign.
Data or InDesign is gonna be very big here,
and it's again all about our data transformations
being accurate, consistent and efficient.
You know, one of the things I like to do is,
I have a friend who's in the data science world,
and I always say to him, "You know man, you guys
"are a trip over there you know,
"you guys came up with two funny words,
"a machine learning and AI,
"and suddenly you guys created
"this whole kind of software industry around,
"just data science."
I'm like "Dude we've been doing data science,
"machine learning and AI for 30 years,
"we're even building predictive algorithms."
Just because we didn't you know, formalize the models
and all that stuff, doesn't mean
we haven't been doing, every is.
What I want you to have in your head
is that you are a data scientist.
If you are a software developer,
you are a data scientist because you're working with data,
you're transforming data,
and it's what you do on a daily basis.
If you don't understand the data,
you don't understand the problem.
If you don't understand the problem,
how can you write a single line of code to solve it.
Now, integrity is going to really come in two places
from a macro level, and things that we're gonna focus
on in this class as well.
But I already told you
before that lines of code is critical,
the amount of lines of code in your project
will determine the health of your team,
and the health of your project.
I said before that the average developer
cannot handle more than 10,000 lines of code,
cannot maintain a mental model
of more than 10,000 lines of code.
Once projects get larger than that,
we really need more developers,
and we keep focusing this idea
that we need to write less code.
And there was a study done
where they identified that for every thousand lines of code
that's written, the average developer
will produce 15 to 50 bugs.
Now I wanna put ourselves in the worst developer category
which basically means that for every 20 lines of code
you write and I write,
we've added a bug to the software whether we like it or not.
Start putting that in your head.
So when you start thinking about let me add setters,
and let me add getters, and let me add abstractions
on top of abstractions,
really what you're saying is
let me throw more code at this project,
and therefore let me throw more bugs at this project.
We need to take this idea in the reverse,
less is always more.
And what's interesting is,
when we have more code than we need right?
Then we've got more places for bugs to hide,
and it also means you've got more incomplete tests
because you've got to write that many more tests
to deal with that many more lines of code,
and eventually a codebase will collapse on itself,
and instead of you maintaining your frameworks
and your patterns, you start to just tack,
and that's when legacy becomes a real threat to the project.
Now another big part of integrity
outside of just writing less code
is also dealing with error handling.
An error handling is a big big part of integrity.
Here's the reality.
Anybody could write a software that works when life is good.
A six-year-old can google today and find code
that works when life is good.
Engineering is not about when life is good,
engineering is about when life is bad,
and things are happening,
and your software has to be able to maintain stability,
and not add to the crisis, but try to recover from it.
Error handling is what we do every day,
the majority of the code you write
is focused on failure, not the good case.
And if that's not happening today,
I need you to reevaluate what's going on.
I love the way Go deals with error handling,
it was how I've been dealing
with error handling for 30 years,
even when I had exception handling
in languages like C++ and C#.
I used it really just to deal with the unhandled stuff.
I would do very much what we're doing in Go today,
and return errors out or codes out,
and handle the errors right then and there,
and I love the way error handling is done in Go.
We're gonna talk about these things
when we get to the error handling section during design.
But there was a study done on some very critical software.
In fact Redis is on this list.
And how many lives today are dependent on Redis?
I mean there's a tremendous amount.
And they identify that out of these projects
they were looking at, and the critical failures
that caused this software
to either go down or have corruption.
92% of those problems could have been avoided,
92% if the error handling was better, was more focused.
92%, see error handling is really really critical
if integrity is important.
So we're gonna focus on that during design as well.
We're gonna be constantly focused on less code
and error handling as we get into design.
The next step here on our code reviews is readability.
Readability is number two.
Doesn't trump integrity, nothing trumps integrity,
you do in a code review and you find an integrity issue,
that has to be fixed.
But readability is number two.
We talked about optimizing for correctness
which is about readability.
Now readability means that
we're going to structure our software
in a way that's comprehensible.
And there's two aspects of readability
that I wannna focus on.
The first one is the average developer.
The average developer on your team should be able
to understand the full mental model of the codebase
that we're working on,
should be able to read every line of code,
have a full knowledge of the mental models,
and should be able to fix almost any bug,
in fact every bug that comes across the codebase.
Now what I love doing is asking you to think about this.
I want you to think about you,
and I want you to think about your team.
Have that in your head right now.
Now ask yourself, am I the average developer on my team?
Do I comprehend the entire codebase we're working on?
Do I have a strong mental model of what's going on?
Can I solve every single bug or problem
that comes across this codebase?
The answer is no,
then you're not the average developer.
Now I want you to think yourself for a second,
I'm I less than average or I'm I more than average?
Now the more than average developers
really have a burden on themselves
because you have to have responsibility
to not write code that is clever.
If you're less than average,
you have the responsibility to come back up.
Now I want you to evaluate everybody on your team,
who is average, who is less than average,
who is more than average.
Are those less than average people
working hard to come up to speed?
And are you helping them?
Or are you a hindrance?
As the average developer you have responsibility
to bring them up.
Are you or who else on your team is more than average?
Is that person writing really clever code?
Have you taken the responsibility to tell them
that their code is too clever?
And are they taking responsibility to not do that?
Look, you put me on a crypto team,
I am less than average, I've got to come up.
You put me on a team building business applications,
Web APIs, I'm gonna be above average,
I've got to make sure my code comes down.
And if you're hiring somebody,
and you don't know whether they're average less or more,
you're really going to cause problems on your team dynamic.
If you're hiring people who are less than average,
do you have the time to bring them up to speed?
The answer is probably no.
But if you do take the time,
think about how an amazing developer
they're gonna be for your team.
If you're hiring people who are more or above-average,
do you have time to teach them how to be less clever?
Are they willing to be less clever?
That's going to cause more problems
than probably the developers that are less than average.
I need you to do these evaluation
for yourself, for your team,
and when you're hiring, this is really important stuff.
But that's subjective right?
And it's based on the team,
and it's based on the software you're building.
And you always wanna make sure
that team dynamic is...
Everybody knows who they are,
what their responsibilities are,
the codebase is clean and healthy
with those mental models.
But that's subjective.
Let's talk about the piece that's not subjective.
What readability also means
is not hiding the cost of the code you're writing,
not hiding the cost.
Now, this one is hard for me to explain.
So, what I'm gonna use is a piece of code,
it's written in C++
to show you what I mean about not hiding cost.
And even if you've never seen C++ before,
I promise you, you'll be able to read this code.
So, let's take a look at this code that's written in C++.
What you see here on line seven is a type named foo.
Now, what's special about foo is that it has these features
that you find in object-oriented programming languages
like C++.
On line eight there's a constructor,
on line nine there's the copy constructor,
the move constructor, the destructor.
And if you're an object or a programming language developer,
you know what these do right?
Constructors get called
when a value is constructed, destructed.
If you're making a copy, if you're moving something,
we call these features,
because we're able to kind of tuck away code
when these things happen in our codebase.
And you can also see on line 13,
I've operator, overloaded the assignment,
which means anytime we use the assignment operator
against the value of type foo,
this code is gonna get called, we call these features.
Now look on line 16.
On line 16, what we have is a function named f1,
and f1 creates an object of type foo,
makes a copy of it on the return
and brings that copy back out.
The caller gets a copy, it's own copy
of the foo object being created on line 17.
Brilliant, I've shown you 18 lines of code
I haven't hidden any of it from you,
and this is what I wanna ask.
Look on line 23 and 27.
This is super interesting.
On line 23, we're calling the f1 function
which creates an object of type foo
and returns a copy of it back out
and it signs it to a local variable, foo one.
On line 27, we call f1 again,
the same function we call again,
reassigning to our foo one variable.
So here's the question.
Who can look at this code right now,
and I've shown you all of it,
but let's just focus on the two lines, they're 23 and 27.
How many objects of type foo get created
by the time this program runs and completes?
Does the copy constructor get called and when?
Does the move copy, move constructor get called and when?
And does the assignment operator ever get called and when?
Here's the reality is that you have no idea
by looking on line 23 and 27
what the full scope of behavior is on that call.
And by the way, you don't ask me
what version of C++ we're using,
the behavior changes, the semantics change,
also depending on what version of the language you're using.
This is a nightmare.
The fact that I cannot look on line 23
and get an understanding of how that code's gonna behave,
how it's going to perform on the machine,
and what my costs are is a nightmare.
This is why Go doesn't have constructors and destructors,
and these features operate over features
because they hide cost, they don't help us.
Now here's the reality.
When I run this code on the latest version of C++,
you're gonna see that the first call to foo
optimizes the copy away.
On line 23, the optimization by the compiler
says, we don't need to make a copy,
we're creating this object
for the first time won't reassign it.
But on the next call, line 27,
even though calling f1 again and reassigning it,
now constructors, assignment overloads, copy constructors,
two objects were made.
Oh my God, the cost difference
on line 23 to 27 is great.
We cannot write code like this,
these features hide cost.
Go doesn't have it because Go doesn't want you to hide cost.
And when I see code that's hiding the cost or the impact,
it's gonna be having on the machine.
Guess what, we have to fix it,
we have to walk away from it.
So Go doesn't have those features.
And it's very very important that we remember
that we are focusing on the real machine,
that way we get to be able to write code
that doesn't hide the cost,
and we give that developer at every moment in time
the ability to understand the cost they're taking
and the impact it's going to have.
And I love this quote from Peter Bourgon,
and he's another gentleman
whose big part of the Go community, a thought leader,
somebody should be looking up,
somebody should be watching talks from.
Making things easy to do is a false economy.
Focus on making things easy to understand,
and the rest will follow.
Now simplicity comes after readability.
Integrity, readability and simplicity.
Simplicity is interesting
because what simplicity says is,
that we wanna hide complexity.
Remember readability is about hiding,
not hiding cost.
Simplicity's about hiding complexity,
and our encapsulations right?
Our functions and our methods which are trying to read,
reduce the boilerplate code,
is trying to simplify things
if those encapsulations cause our ability
no longer understand the impact our code is having,
we can't have it.
Simplicity is not something you do day one,
it is something that you refactor into.
And one of the biggest things
that I can tell you about simplicity is what Dijkstra said.
You know, it's a great virtue,
it requires hard work to achieve,
education to appreciate it.
But complexity sells.
Well you got to get away from that idea
that complexity sells,
and we've got to refactor into simplicity as we go.
Go is an amazing language
as it comes to simplicity engineering.
I'm gonna try to point those things out to you as we go.
But there's two more big parts of simplicity here quotes
that I wanna say again.
Again we're trying to hide complexity,
but we're gonna focus on this in the class really well.
And Dijkstra said it again,
the purpose of abstraction is not to be vague,
but to create a new semantic level
where one can be absolutely precise.
We're gonna talk about at some point, precise APIs,
and how powerful they are in a language like Go.
Where we have a compiler,
and have the ability to find
and identify bugs at compile time
as a opposed to production.
And I'm gonna be asking you to kind of throw away
some of the API design that you've been taught around,
object-oriented programming here,
and really focus on the cost that that has
over the benefits of this idea of precise API design.
And Joe Beda whose company called Heptio, great company,
said this, and I love this also around simplicity,
computing is all about abstractions.
Those below are just details,
and those above are limiting complicated crazy town.
So anytime I look at a function or a method,
and I'm seeing that this is an abstraction, right?
We're seeing encapsulation,
I wanna make sure that these encapsulations
are not just testable,
I wanna make sure that they're providing
a new semantic precise level of understanding
that is not hiding cost okay?
But hiding complexity.