- So let's really look at how composition
works in Go with this example,
and I want to give you some guidelines,
around doing this and writing code in Go.
It's probably a little bit against,
what you're used to doing.
A lot of you have been taught,
to start with the interface,
start with the behavior,
try to figure out what those contracts are.
I don't want you to do that.
That is guessing.
I don't want you to do that.
Remember, the problem is solved in the concrete,
not in the abstract.
So we need a concrete influenced,
implementation solution first,
in order to know how to decouple.
This is going to simplify your code,
and allow you to focus on what's important,
which is the problem,
and remember, the problem is the data.
Now, there's a couple of things here also,
that I want to talk about,
before we get into this example.
Things that I see, that are missing,
and one of these things,
is the idea of done.
How do you know when you're done with a piece of code?
And if you manage people,
how do you know that they are done?
This is a really big, important question,
and for me, done comes in two different answers.
The first one is,
you're really got to make sure,
that you have your unit tests,
and you got to have some level of test coverage.
For me, I want to see anywhere from 70 to 80 percent,
of code coverage on tests,
before you can come to me,
and say you're done.
It's really hard to get above that sometimes in Go,
because we focus on so much error handling,
and a 100% code coverage,
really doesn't add value to you,
it just means that you execute every line of code once.
I would like to a 100% code coverage on the happy path,
but overall, 70 to 80 percent,
and I'm good.
The other part of this question,
which really relates to me is,
is the code decoupled from the change
we expect to happen?
Now, we might decide that we know
what the decoupling is required,
but we don't need it today,
because we don't have
multiple implementations with something.
We don't have to need today.
Remember, you're writing code for today,
we're designing and architecting for tomorrow.
So, deciding to decouple,
may or may not happen immediately,
but I want to ask the question,
do we know what has to be decoupled,
and do we want to do that?
And I can do this,
and I can ask this question,
because decoupling is part two of everything I do.
It is a refactoring.
We solve problems in the concrete first,
we refactor into the decoupling.
There's a couple other things,
that I see on that developers have problems with.
They have a real problem breaking problems down.
I'm not talking about tasks,
or GitHub tasks or Jira tasks.,
I'm talking about looking at a problem,
and saying, I've got to solve this,
and I've got to solve that,
and I've got to solve this,
and it has to be in this order,
and if I don't solve this,
and the other problems don't matter,
and these are little, these are large,
and these can be multiple tasks to solve a problem.
We're going to go through that exercise as well.
One of the very last things that I see quite a bit,
is software developers don't really understand,
how to create a layered API.
And this is really going to help you with a codebase,
instead of you trying to solve every problem,
in a few functions.
What we really want to do,
is find a layered approach,
then initially just three layers,
is all we're going to need.
We can have what I call our Primitive layer,
and this layer knows how to do one thing,
and one thing very, very well.
We're always focused on what is that one thing,
and one thing very, very well.
We write out the code for this layer,
we write our unit tests for this layer right,
and we write this layer,
so it is testable.
Now, when I say testable,
this doesn't mean it interfaces,
remember, I'm working in the concrete,
interfaces, decoupling happens through a refactoring,
and yet I have to already start writing unit tests.
Unit tests,
code that is testable,
usually means that the data,
that we're passing in is reproducible,
and the data coming out,
okay, is testable.
So, I'm always going to be telling you,
over and over again,
testability is about the data,
the code we're writing is about the data,
decoupling is about the data.
The problem you're trying to solve is about the data.
So, when we're think about unit testing here,
I want you to think about,
can I write a function in the primitive layer,
where I can give it a set of data,
get a set of data back out,
and we can validate it and test it.
Now, your next layer, I would call the Lower Level,
the Lower Level layer,
and that's the layer that sits on top of the primitive API,
that does maybe some raw things,
a little higher level than the primitive layer,
and again we're going to write those unit tests,
and this layer to,
should be testable in its own right.
We should be able to test everything coming in,
and everything going out,
and a lot of time,
this layer and these layers,
are probably unexported, they could be,
but sometimes it's nice to export the Lower Level,
the Lower Layer API,
because it's very usable if you write it correctly,
and it gives the developer, the users,
maybe more control over the things they need.
And then you're going to have your,
High Level API.
This is where you're trying to do
as much as you can for the user,
to make their life better.
You get it sitting on top of the Lower Level,
which is sitting on top of the Primitive Level.
Since every one of these levels have been coded,
and unit tested,
by the time you get up here,
we're really just thinking about,
an ease of use for the developer,
and we're going to be able to hopefully
unit test this as well.
Sometimes these higher level functions,
require more kinda integration kinds of tests,
but we want to think about,
how we test every layer all the way up.
Eventually, your higher level tests
might be able to replace some of your lower level,
and primitive unit tests,
because they cover those test cases for you,
and so there are times,
where I'm even writing unit tests,
that I know aren't going to exist forever,
because as I move up these levels of API,
I'm getting the same code coverage
on the tests that I've written here,
and that's okay,
I cannot stress enough,
that I don't want you to be throwing code away,
don't, I mean no, I'm sorry,
I don't want you to be worried,
about throwing code away right,
refactoring is about making something work,
and looking at how we make it better,
how we make it more readable,
how do we do things,
and the best days are when we actually remove code,
so don't get hung up that I might be writing
some unit tests here,
that eventually we're going to delete,
because we're going to get some better coverage here,
and writing those tests right away,
because I want to make sure these layers are working.
So this stuff is very, very important,
and we want to have these things in our head,
as we move forward okay,
and as we start really starting to write more code,
you're going to see me focus on,
what are the real problems in front of us?
How do we layer this API?
Not only so it's usable,
but it's testable and it doesn't mean we need interfaces,
it means that we need strong
inputs and outputs around the data,
that we can test against,
and then how do we refactor code,
to then deal with change,
once we have a concrete solution.