Two's Complement - Deploy First Development
Episode Date: August 17, 2024Our hosts congratulate themselves on finally having decent microphones. Matt quizzes Ben on his "Deploy First" approach to software development. Ben explains branch-based deployment environments. He a...ssures Matt he's a mortal. Matt promises to be less rubbish.
Transcript
Discussion (0)
I'm Matt Godbolt.
And I'm Ben Rady.
And this is Two's Compliment, a programming podcast.
Hey, Ben.
Hey, Matt.
How are you doing, my friend?
I am good. And I think this time both of us have
got our microphones on the right setting pointing the right way. I'll believe it when I hear it.
You're not wrong, right? We've not had great success so far. We started out strong and then
we both upgraded microphones and things and then the number of combinations and permutations of
things that can go wrong exponentially increased this is like when you have some difficult thing
to do and you're like procrastinating and you're like i know i'll just like work on the build system
or whatever what we're doing instead we were going to record a podcast but instead we're just going
to mess around with microphone setups and audio engineering. Exactly. Yeah. We've turned our podcast into an excuse to do that.
That seems about right.
I was meant to be writing peer reviews all day today.
And so I've been doing literally everything but that.
Why is it so difficult?
Why is it so hard to write things for humans when code is straightforward you know like i'd rather sit
down and write some lock free queue than to write things nice or otherwise things about my my peers
um it's hard that's why humans human stuff's difficult right well because there's no type
checking that's what it is maybe llms will do the type. Maybe that's what I should do is I should, you know, paste, paste.
No, I should not do that.
This is for me to my peers to tell them about what,
how I think they can improve,
not for some LLM to turn it into word soup for 300.
Anyway, this is not what we were talking about.
I've just got totally sidetracked at the beginning but um so um i was really interested actually in
something you and i've talked about outside of this before which is your sort of philosophy
of developing a new product or project or machine or whatever i guess project is the main thing
yeah and you know how how you approach it, right?
And obviously testing is a big part of that,
but there's one aspect which I've taken on board
and I think it's really important.
And you should talk about it.
Yeah.
You're talking about deploy first?
I'm talking about deploy first.
Yes.
Yeah.
Yeah.
I mean, it's generally everything that you want to do
in any sort of project is think about the risks, the things that you know that just ain't so.
Right.
And one of the easy things to know that just ain't so is, oh, yeah, we'll just deploy this out to whatever thing right right we'll put
it on a computer somewhere i have you know project.py or i just need to copy it somewhere
and then just run it right how hard can it be right exactly so that is a place where
um you can i i think there's a general pattern of these things, which is it usually makes sense
to confirm easy things are actually easy. And one of those things should be deployment. Deployment
should be easy because hopefully you're going to be doing it a lot. So starting with deployment,
when it couldn't possibly be easier, when you have like one line of code that like
writes to your logging system that says like hello world hello literally yeah yeah yeah even if your
logging system is just standard out for now right it's it's like well what could possibly be easier
than that well it's like oh if it's so easy then why don't you start with that right and then and
then there's this little demon that shows up and and and says well what if it's not easy right what
if it's a huge pain in the butt what if i just do it later when i don't have to worry about it after i have written the fun lock free
queue and all of the other cool stuff and i don't want to have to deal with me pretty quick
no right exactly it's messy and it's it's a bit like writing reviews for humans, right? It can go wrong in so many different ways.
And it's not in a beautiful platonic ideal of my editor
and my TDD-based sort of workflow.
It's like, no, some bits get tarballed up or CI has to be working
or I have to copy it to a machine and I have to get the environment set up
and all of the things.
Yes, yes.
And the more stuff you have to deploy,
the harder it's going to be. And it's, it's the classic thing of like, it's really hard to build
big, complicated things. You build big, complicated things by building tiny little things and
combining them all together. You know, no one likes to review the 10,000 line PR. No one wants
to be on rotation when the huge change that someone has been working on for three months and then went on vacation rolls out.
Right.
You want to do things one tiny piece at a time.
And the best way to do that when it comes to deployment is take the dumbest possible thing that could even remotely smell like the thing that you want to build and deploy it.
Right.
Is it a web service?
Okay, cool. right is it a web service okay cool get some basic web server in place and you know make your
hello world.html and you know deploy it and make sure that you can pull it up on a browser and then
it works you know is it some running service okay cool is is is this service actually run if you if
you kill it what happens right does your alerting work right like you if you go under the machine
that it's running on and do like a pkill, do you get the
alert on your phone saying that it shut down?
Because you're assuming
that that infrastructure is in place.
And if it's not or it doesn't work the way that you
think or it's broken or hasn't been configured yet,
there's going to be no easier time
to troubleshoot that than
when you have one line of code.
Right? So I'm
a big fan of starting out with this,
because it sort of fits this model of, well,
if this is so easy, then let's just do it right now.
And if it's not easy, then we'll know about it
sooner rather than later, and we can account for it.
And the other thing it lets you do is, as you're
making changes to the code, it's like, oh, we're going to need access to a large disk because we need to cache some things on disk when we're processing.
Okay, cool.
You're rolling out that change as one kind of atomic change rather than getting to the end of some big development cycle and being like, okay, we're going to need a big disk, we're going to need two network cards, and we're going to need a backup server over here that's in hot deployment mode.
And then now you've got to configure all of that all at once for this one deployment
and everything's got to work perfectly the first time.
That sucks.
Right.
Sort of what you're hedging for is incrementality
from like literally from hello world exit zero
through to massive system that does all of these things,
but you can do it one thing at a time
and be sure each thing works individually,
as opposed to trying to,
as you say,
debug 12 different things that happen at once.
I also get the,
there's a lovely aspect to doing deploy first,
which is like,
I can point out usually some kind of dashboard that has like a green
light that says my service is up and running.
Obviously we're talking very specifically here about the kind of services
in a corporate environment or or similar
here on me obviously like if you're if you're building a game may actually no maybe if you're
building a game you make sure that you can take your your dumb apk or whatever it is and then
install it on your phone and say look it just says matt's game and you can click the button
and it quits and that's all you can do but now i can commit that in
the repo i can make my ci step do that every time build the apk all those kinds of things yeah that's
an interesting interesting one but i was thinking more like like literally our world where we there
usually is a service that you use you know some kind of docker based thing or use some kind of
nomad or other system home homegrown system to say,
I have a new thing that's running.
Please make that run as well
and do all the normal bits and pieces,
you know, know where its logs go,
make sure they get archived, all that kind of stuff.
So yeah, deploying something and that I can point out,
even if it's not doing anything yet and say,
well, it's running, all we have to do now is add the code.
Right.
You can do something.
I was literally just speaking to somebody
about doing this very thing.
It's like we,
internally,
we wanted to make a server
that publishes
some specific form of derived data
that my team works on.
And there's a team
that's going to be consuming it.
And I said to the head of that team,
I think I might just deploy something
which publishes the value zero
continuously for you. Because I can point at it and say it's up and running you can get
we can get all the boring is the tcp connectivity right is the task group so they configured right
all that nonsense can get out the way and then when you're receiving a bunch of zeros later on
i can replace it with you know some high quality predictions for the market right right. It's an exercise for the reader at that point, right?
Because we all know that where the bodies are mostly buried is in the wiring.
I do like that.
So let me ask you something about that, which is how do you feel about continuous deployment?
Because it seems to sort of lead into it once if this incrementality aspect of it if you're if you if you start from deployment then do you keep it up
yeah absolutely and i mean i think uh i mean me personally it i it would have to be a very unique
circumstance for me to not take the time and energy that it takes to continuously deploy a
system these days right it would have to be like there's only one piece of hardware that this can
run on or with and it costs more than your house so we only have one and it's in production uh
and so i'm like okay well if that's the case then maybe we can't continuously deploy it but
i'm feeling we can and we'll figure out a way.
We have two computers that are a little bit like that.
But one of them is a backup staging instance that we deploy to every day.
Now, we don't do it continuously in our world.
A lot of what we do on our side is sort of based around the market hours of a market.
And starting up intraday is an important test but it's not the
same as what we normally do which is run continuously all day and so we want to make
sure that we we can in fact run continuously all day um but it's still continuous ish continuous
deployment intermittent regular intermittent deployment regular deployment i suppose is right
right you know we have a staging environment where every morning it starts up with the latest version of the code then it's the expectation if you've committed it and it
passed all the tests it goes to staging like as a matter of course and then fairly soon it'll end up
in production but no it's i the the deploy first thing is something that you said fairly early on
in our me knowing you and me and it was definitely struck a
chord of being like no that's a cool thing to do that's an awesome thing yeah so definitely worth
talking about and i really like your idea of like i'm just going to set up the service that publishes
all zeros and then you can integrate with that because you know as you were saying like you know
you want to get all of these sort of basic of the way. Again, making sure the easy things are easy. Because
if you deploy that and the service that whoever it is is building
is like, wow, I'm getting all ones. You know that there's something very
wrong and you know it has to be a small set
of things. It can't also be bugs in your code or bugs in
their code or whatever it might be.
It's like, well, we just did this one stupid thing and that didn't even work. So we're
clearly missing something, right? Yeah. It's much easier to, to, to debug something when it's,
when it's simple. So, and then, you know, you're always like building on top of all of
this kind of infrastructure, right? Your deployment environment,
your logging environment, you know, any tools that you build for observability, it's really easy to
just kind of assume all that stuff works. And so you better make sure that it does, right?
One of the things that I do pretty much in every system that I build these days
that has any kind of alerting based into it, which is most of
them, is I build ways to intentionally trigger faults, right? So like in a web app, for example,
I'll have a route that you can hit that raises an exception, right? And, you know, the kind of
exception that it raises might be variable. You might even be able to pass in different parameters
to get different types of errors so that can be handled in different ways. But I want to be able to deploy my system to production.
So like, you know, can it continue as deployment model?
I'm just going to, you know, make a change or whatever it is or just, you know, have the system be running in production.
And I want to hit that route that creates an exception in the production service.
And then I want to see my phone light up and say like, there's an error in production, right?
And I want to be able to do that at any time.
If I have even the slightest whiff of a hint
that maybe the alerting is broken in some way
and there's something terrible going on
that I don't know about,
I can immediately dismiss those fears or most of them
by being like, well, let's cause an error on purpose
and make sure that we get an alert, right? And make sure that our logs work and everything else. Of course. Yeah.
Those things are the kinds of things you have to be, have a good working relationship with
your operations folks so that they know when you're about to do this, you know, maybe you,
of course you, so you put your, you know, the equivalent of your pager duty in maintenance
mode, but you still make sure that it appears in the UI and then you trust the pager duty will
in fact read your phone.
But that's a really important thing.
I mean,
it sort of comes back down to,
you know,
who watches the watchers,
you know,
where do you draw the line about that,
that observability aspect of your,
your app?
And,
and actually now I think about it,
this is a complete non-secretary,
but that kind of worrying about like,
how do I test the thing that is like the last
line of defense like when if an exception if my my web server has an exception i mean
you mentioned killing the process that's another thing that you might reasonably do is log into the
box and go kill minus nine that pid and watch it die and then also watch your phone light up and
again you don't want to be doing that every single day,
but it's nice to be able to do that as part of your checks from time to time.
Right.
But yeah, how do you test that your monitoring is working? I mean, I guess, again, that things like Prometheus and Grafana, you can just go and look at them.
They're there.
Yeah.
Yeah.
But, you know, there are lots of situations where you have an absence of evidence problem.
It's like, OK, this, you know, locked thread count is always zero.
Right.
Have we ever seen it not be zero?
Do we know that this metric works? Do we know that it's actually measuring the number of locked threads and not like some other random
variable that is unassigned or unused or whatever? Very good example as well. Because yeah, if you
say things like that, it's like, it's one of those things that's almost always going to be zero
whenever you take a look at it, just because of the, I mean, assuming you're meaning like
some kind of like a lock that you take out before you do some work.
Yeah.
For the first approximation, it lives at zero, but it's very important when it's non-zero.
And maybe if it's stuck at non-zero, you know that there's a problem.
Right, right, right.
Or any kind of monitoring.
Yeah.
I mean, and how do you test that, right?
Do you just put a, you know, I've been, I mean, again, this is perhaps a different thing.
Again, you know, you can put a big honking, great big global variable.
I know this is a C trick where I'll do Xtern,
bool, hack equals false,
and then I'll poke it in the debugger
and then make one of the things actually like if hack,
then while hack effectively, just in an infinite loop.
And then I can test it exogenously,
but that's not very reproducible. you know, sitting at while hack effectively just in an infinite loop. And then I can test it exogenously,
but that's not very reproducible.
But then do you really want to put code like that in production?
You know,
I mean,
I have to say,
yeah,
I don't know.
Like I'm pretty bold about putting stuff into my systems that give me more observability.
For sure. For sure. give me more observability for sure for sure um but
there's observability and then there's like deliberately putting a something which is known
to be broken in your code yeah so that you can test that the thing that detects that it's going
to be broken works live in your production system yeah yeah and i don't know i you know i now i've
said it out loud as explicit as that,
it sounds terrible,
especially in the context
of, say, trading systems, right?
There are famously cases
of companies
who are trading companies
that have made mistakes
of this flavor
that have subsequently
then folded.
So it's not something
you want to do lightly.
But on the other hand,
I don't also know
of a better way
of being sure,
you know, Portland sure, one might say. portland sure which is a whole other conversation probably that's a whole episode that's a whole
other um yeah i mean one of the questions that i frequently ask um when it comes to
because you know i mean it would be easy to caricature me as a person who just like yeah
i wrote all the unit tests and they all passed and I'm going to deploy it to production and not think about it anymore.
Not just easy, but done every day at work and online.
If we had comments for this podcast, if we don't, I'm sure that that would be in the comments, right? And all of the jokes that you've seen maybe about the paper towel dispenser
over the trash can that once you trigger it, the paper towels just continuously stream
out.
Because it sees it as a hand.
Yes. And the subtitle is all the unit tests pass, that kind of thing.
Oh, there's another one with, there's a um like a a a pipe
and it's got a hole in the pipe but there's a second hole in the in the pipe and the water is
leaking out and then going into like the l-shaped pipe squirting out it's like again yeah everything's
fine here like the test pass all the tests one into the other yeah yeah but one of the things
that that i do and i i ask of the people who work on my teams, and I
will gladly ask of anyone that asks me for advice, is if you build something, if you
created some new capability, functionality within your system, have you ever actually
seen it work?
Like in a live running system?
It doesn't necessarily have to be the production system.
It could even sometimes,
depending on exactly what it is, just be your workstation. But have you ever actually seen it
work? Because if you haven't, you have no reason to believe, however many unit tests you've written,
that it does. That's a really interesting point there. Yeah. And so whatever you need to do,
you're talking about poking variables into the system or all of these other things, whatever you need to do to create the behavior,
create the effect that you just spent days, weeks, months trying to build, do it.
Yeah.
And design the system so that you can do it and design the deployment system so that you
can see it happen.
Right.
Like all of these things need to be done.
If you have this model of, okay, I'm going to just write a bunch of unit tests
and do a continuous deployment of my system into production,
and it's all just going to work great, and I never have to check anything,
and I never have to do anything that even smells like manual testing,
I've never been able to develop software like that and i love tests that's that's really no that is genuinely very interesting
to hear and heartening as as a mortal who doesn't like it's not quite as uh not quite as
in this situation but but you know like i've always felt slightly dirty doing that i feel like i'm admitting
something here but like you know the fact that i've even tell you these tricks that i have right
for like well this is a specific deploy i'm gonna do it just to see because it feels ephemeral and
it feels like it could break again because it's not automated and so i know that that's sort of
wrong on the one hand but if i'm now going to take the other side of the and play devil's advocate to my
own like feeling then it's like seeing it work once is still infinitely better than deploying
it and never actually having seen it work at all right even if it breaks the next day you know what
you've done is you've shown that the on-ramp works and then after that you kind of assume maybe too much
that it the the the transitively you test all the bits around it and then no one should break the
on-ramp or whatever the thing that actually causes it to happen and obviously if you can
contrive it to happen in a controlled way and you can have tests to do it but that's one thing but
that yeah this but yeah as i say i've always dirty. Like I'm doing it wrong when you, you, you have the, Oh, um,
you know what I'm going to do is I'm going to put a massive sleep in this
thread and commented in and out just so that I can show that my slow thread
detector thing fires up because you know, I can't otherwise, you know,
I can write all the tests in the world.
And all I'm really doing is showing that my mock, when it returns time,
I appropriately log it time down that
doesn't feel very satisfying right right no absolutely and and i mean the only thing that i
would the only thing i would be concerned about with that is if you now feel like you have to do
that sort of what i would call exploratory testing right every time you make a change right now right
yeah like i don't feel
comfortable deploying this out to production until i go do the sleep thing again right yeah that is
telling you okay you you probably need to write some tests here or you maybe need to design this
in a way where it's easier to test or you need to design in a way where it's observable where
it's like maybe you're not testing it with automated tests, but you have some test environment that can simulate it or reproduce it and, and, and get that out of there. Because, you know, if, if you're, if you're stuck in this world, cause I mean, the purpose of this kind of like, have you seen it work once? It goes right back to what we were talking about at the very start of this conversation, which is, it's what you don't know that ain't so, what you know that ain't so that gets you in trouble. Right.
Yeah. And the, and, and seeing it work as an opportunity to prove that you're wrong, that your assumptions
about how things work aren't so.
And if you pass on that, you're just sort of waiting to be wrong in a fantastically
horrible way.
Right.
But what you don't want to do is turn that into a crutch right like once you've done it one
time once you've had that opportunity to to disprove yourself and you failed to disprove
yourself if you feel like you have to do that over and over again you're missing tests or you're
missing some other form of feedback that you you need to create because it's not scalable to do
that for every piece of functionality in your system every time you change it. Exactly. It's the document that says this is how you're meant to break it.
Yeah.
I mean, I could probably make a case in very extreme circumstances where you might need to do that,
like where if your code is, say, extremely performant,
you can't have loads of if statements in the middle of it for all these different things,
then maybe, but then typically you've already taken paid a massive amount of cost
to develop that thing and then you put a bloody great big um like comment at the beginning it
says nobody touches this code under any circumstance without running these tests and it's
tucked away in the corner of your code base again the lock free stuff of which i was alluding to
earlier exactly exactly this you know you spend ages getting it working and it's almost impossible to test
that it is completely right
under all circumstances
because, you know,
it's that kind of multi-threaded thing.
But yeah, it has that flavor of like,
well, once you do get it working
at great cost,
you don't touch it
and then it's fine.
And then if you do,
you maybe do have
some extra manual steps around.
Right, right.
And I have those things too. Like I have things in my code base right now,
where it's like, you know, little functions that have been written little, you know,
main entry points that, you know, run a whole bunch of multi-threaded code in a tight loop,
hoping that if there's any concurrency issues in there, we will find them knowing that that
is not going to save us. But at least if we detect it that way, we found one.
Right.
Yeah, exactly.
That's exactly the kind of thing, you know,
do you want your unit test to sit there for six minutes while it just runs
every possible comment?
No, nobody wants that, but you do.
It's nice to have maybe even if you run them daily,
maybe if you just run them when you make the changes.
I just run them when I change it.
Yeah.
Yeah.
Because again,
it's a very small amount of code that is under test there.
Right.
It's a very like specific thing.
And you design the system so that it is a very specific.
Yeah.
You can scatter that across the whole code base.
You find the abstraction that means that like the horrible code lives in one place.
And then you test the crap out of it using non-traditional means.
And then you kind of say all right we're
done here dust your hands off and go i hope we never have to touch that again right right it
actually reminds me so like the the the cliche of putting all your eggs in one basket right there's
the original version of that had more in that cliche it's put all your eggs in one basket and then watch that basket.
Interesting, yeah.
So it's not necessarily a dumb thing to do
is putting all your eggs in one basket.
It's you can, you just watch the basket, right?
Yeah, you consolidate your risk into one place
where you know where to look
as opposed to scatter it throughout your code base
in this particular instance, yeah.
Which, I mean, we've seen the number of times,
you know, like anytime you have some complicated um that you have to do exactly right and then if you have
to scatter it through your code base then you probably designed your apis wrong it's much better
to put it in one place and have an api that means that the awkward to do thing is in one place so
that when you inevitably get it wrong you only have to fix it in one place as well.
Yeah.
No, that's cool.
I mean, that's an interesting...
What I thought of when you were talking about
the manual testing and stuff,
and if you've never seen it fail,
or succeed, sorry, if you've never seen it succeed,
that's kind of inverted from literally every test I ever write,
which is I start up my ID,
I open a new file in the in the relevant place and i do whatever boilerplate i need to do to get an empty
test and i said the first test i do is def should fail and i do a search false and then i hit the
button that says run my tests and i sure as heck it should fail on the line that says assert force
because the number of times that I've misspelled the word test
in the file name, like Tset or something like that,
or some other thing that means that it doesn't treat it as a test
and it rather runs it as just a regular Python file
and there's nothing to do because it's just a bunch of deaths
or it's a C++ thing and whatever, you know,
any number of reasons why it doesn't actually execute it as a test
can give you the most horrible sense
of full security you're like hey i'm writing these tests one after another not a single one has
failed i am great yeah right i am a superstar i am this is oh my gosh i'm gonna have to this
this day will go down in history as being the day that i got everything right and then you realize
started by misspelling the file name of the program of the test and now you feel very very
silly so yeah you might as well just throw it all out at that point yes to be honest there's not
gonna be anything good in there yeah absolutely yeah but yeah i mean you know i i think all of
the deploy first stuff sort of comes from from a base philosophy right and it and it is the same
philosophy that tells you you you know, make sure
your tests fail as you expect them to fail, right? You don't have any behavior, you run your tests,
they fail and they fail the way that you expect. If you, if you build something, how do you know
that it actually works? Have you ever seen it work? Well, you need to figure this out. And it's,
it's like a, it's like a scientific mindset, scientific mindset right it's like how am i going to prove that i am wrong and just sort of thinking about those things in a very systematic
and continuous way like every little step you take how am i going to prove that i'm wrong here
right right um and i think yeah it's very easy to fall in the trap of asserting the behavior that
you know to be right because you're still i I mean, but that finds a remarkable, I mean, still finds a remarkable amount of idiocy in my own code.
Oh yeah.
A number of times I'm like, oh yeah, this is, how could there possibly be a bug in this thing?
It's so easy to write a test for it.
It's almost rude, rude not to.
And then you find the bug anyway.
Oh gosh.
But then, yeah, to then turn on its head and say like, okay, well, I've written this list of processor or this lock-free queue.
How could I show that it isn't working?
Or how could I show that it is working even, right?
You know, that's, you know,
as you say, spawn up your 12 threads
and then you get them dumping stuff in
and then you make sure that you can read it
out the other end or whatever.
And then, yeah.
And that's, yeah, that's cool.
But this was meant to be deploy and then we turned it into test,
which is the way we do these things.
But they go hand in hand, I think.
It's about discovering the things that are broken as soon as possible.
Right.
Howsoever they are.
And then developing confidence that it is actually working.
And then, yeah, if you're deploying your software first,
then you can always point.
Like I can point to my CEO when he's been telling me,
when is this thing going to be up?
And I can point him at the publishing zero version and go like, we're only a configuration file away from this being something important to publish instead of a bunch of zeros.
But like the plumbing's all done.
And that's pretty satisfying.
Yeah, yeah.
Well, and relating some of what we were just talking about back to deployment one of the things that you may discover
if you start asking the question of have you seen this work is you need an environment to watch it
run right ah yeah yeah and sometimes that can be your production environment but um it's better
if it doesn't have to be right right. Right. And so, you know,
I think we've talked on a couple of episodes before
about, you know, branch-based environments
and things like that.
I think so.
If we haven't, we should,
because it's a very good topic.
Let's assume we have.
Yeah.
You've got a very good setup on your current project,
just as a pre-C,
where you, effectively every branch in Git
is its own environment.
And so everything gets deployed to an environment with that name.
And it's like a unique DNS name and blah, blah, blah, blah.
But it means that you can basically just for the hopefully lowish cost that whatever provider
you're using as the backend for each branch, you get a copy for every pull request effectively.
Right.
Exactly.
Exactly. And so that really gives people on my team at least no excuse. you get a copy for every pull request effectively. Right, exactly, exactly.
And so that really gives people on my team
at least no excuse to say,
have you seen this work?
Because they're like, well, it's too hard to see it work.
I'm like, well, then the design of the software is wrong
because everything else is set up to make this super easy.
But yeah, if you don't have a way
to easily run your system in a realistic environment that gives you confidence that it does actually behave the way that you think it behaves, it's going to be very difficult for you to answer this question.
Have you seen this work?
Have you seen it work, right?
Well, I'm not sure how I would do that.
I think that's something we solve yeah if you can't see how to do it then that's a structural
problem with either the way that the team is set up i mean and i'm now thinking that like this is
exactly the problem with my team right now is that there are a number of things that are very
byzantine and bespoke that are difficult we kind of lean on the crutch of having a one-size-fits-all
staging environment where we do discover things but because it's the tragedy of the commons of like everyone in there if two people have made
us screw up because they couldn't test it any other way then we've got two problems in the same
environment and right you know you say to them well i and they're also they can quite reasonably
say how could we have known this beforehand and i'm like well sorry you can't so i'm i'll tell i'm taking a note here uh
be less rubbish yeah yeah this is all my fault well yeah and i mean and you know it's it's like
time in the testing environment is like time on the mainframe you know you got to schedule
i get from two o'clock to four o'clock no it's i mean for us there's a physical hardware component
which is unfortunate well and that's um you know us there's a physical hardware component which is unfortunate
well and that's um you know not everything quite a lot of things can be run either on our local
development machines or in small like cloudish environments but we have got some in our
particular case some very obscure networking stuff but i have got a po out to buy more hardware so
you know it will happen,
but yeah,
it's still less than ideal.
And some of this stuff is like,
so let me,
all right,
we're,
we're,
we should probably wrap up.
We're about the right amount of time in,
but one of the things that I find hardest, and this is where I lean back on,
and I was actually only quoting you the other day about this,
when I was telling people it was okay that they,
these kinds of mistakes were happening,
which,
and I blamed you, is is is it failed fast right these were things that we couldn't otherwise test and when i say that we know that we have configuration files that like
um have the essentially command line parameters for a whole bunch of interconnected
programs that run in an environment right right? You can imagine this. There's things like the names of Kafka topics.
There's the name of brokers that are the brokers for this environment versus some other environment.
There's every other like command lines, which you might imagine that you might have.
And so we have one that's like, this is the staging environment command line.
This is the production command line.
This is the development one.
And this is the all and for like N things, right?
And there's always a bit of wiring somewhere, right? i mean yeah there are ways and means of like making them
automated or whatever but for whatever reason it's like the place where we do go oh i'm going
to turn on this flag for our staging environment that makes it on purpose different from production
because we're testing something that we want to run for a long time and then see if it compares
good yeah all that good stuff right but it's also the number one place to typo a command line flag name yeah and you could try ahead of time running it on your dev
machine but you will be publishing to a topic that is used in the staging environment or god forbid
in production so you better make sure you don't type in that bit of the command and so people
don't quite reasonably you're like okay i run it maybe and i maybe if i'm feeling really brave i carefully comment out all the things that i know
to be production affecting and then i make sure and obviously there's some network partitioning
as well to prevent the worst of these things from happening but it's still a risk so ultimately
really we all look at it we go through code review three of us stare at it, and then we commit it. And only then do we discover, oh, it's dash, dash, blah, underscore thing rather than dash, dash, blah, dash thing.
Of course it is, but none of us could have seen that stupid thing.
So the only sort of comeback I have is it fails immediately at like seven in the morning when it deploys.
And we've got plenty of time to go and make a patch release to to fix it before like we care about it but um i'm now just trying to think how that works in in in
say your branch-based environment how do you deal with the fact that there are some configuration
things that maybe are different in production right you're like okay this really has the
dash dash no you are allowed to here are the credentials for the thing that you can do
so i the way that we do it is the configuration is literally just code.
We don't have configuration files.
We have classes.
And everything, and there aren't very many of these,
but everything that is, oh, this happens in production or this is not,
is keyed off the branch name, right?
Okay.
And it's probably less than half a dozen things
that i can so that's i mean yeah that makes sense but yeah and there's and not there's not
surprisingly unit tests for all the configurations so it's sort of like oh when you have this uh
setting set then it creates these objects instead of those objects and there are unit tests for all
of those things that confirm uh like there's a suite of tests for the main configuration that's got the special bits in it
yeah there's a test a unit test for like the deployed branch configuration which includes
the main configuration but also all the pr ones there's one for uh local testing and there's one
for sort of like our operational scripts and things that run in the same environment. And all of those things are unit tested.
Got it.
That makes sense.
I think, I don't know if that could work for us now.
One of the things that I like about the command line flag based version is that we often use that locally a lot as well.
So I want to run something that looks like the staging environment, but I'm going to do tons of changes to how it looks.
And then I can also intersubjectively paste that into Slack like slack and say hey this is a reproducer for that issue
you can run this locally and it go whereas if it was like a local config that i then actually had
to edit i'd have to check it in somewhere and say you need to pull this version but yeah that's an
interesting way of solving this this problem yeah it's interesting because on this project and this
is not something that I have done before,
I think we talked about this a little bit
in the transition from Linux to Mac episode,
where we sort of redid all of our operational things in Java
because we were forced to
because we were changing operating systems.
Right, and rather than running, you know,
sed and orc and whatever,
you're like, well, I'll just write the three lines of Java
that does it, and then it works on both operating systems.
Yeah, yeah, yeah.
And one side effect of that is that we have very much
sort of turned into this, for better or worse,
I'm not saying this is a good idea,
I'm just saying this is what we did.
We turned into this thing where, you know,
the way that we do things intersubjectively on the team
is someone writes a little Java main function and then they check it into a branch and they're like, here's this thing I, you know, the way that we do things intersubjectively on the team is someone writes a little Java main function
and then they check it into a branch
and they're like, here's this thing I'm trying out.
And sometimes we actually wind up copying
and pasting that code.
It's like, I want to try this over here.
I'm just going to copy and paste it and run it,
which is a little more complicated
than copying and pasting the command line args,
but it's maybe, I don't know.
But the sort of like environmental shift that we had to be able to develop on Macs has sort of forced us into this mode, which I've never done this before.
But it works okay.
Like, I don't have any major complaints about it um but one of the things that it does do is it allows us to lever all of the regular environmental tools and libraries and every and checks and everything else that we have
where it's like if you make a configuration for one of these scripts it's real clear what it has
access to and what it doesn't right yeah like that's pretty bulletproof yeah yeah no you just
from trigger to memory actually like at google there were
definitely some tests that would like take the string and run it through the command line parser
and then make sure that the output was what you expected kind of level things for testing things
like this and it kind of flavors similar flavor but like some of these things have complicated
interdependencies that you would be like well you know even if you got it right the the you might be
using the right channel but you're using the wrong broker and it's hard to write test. I don't know.
I maybe I'm making an excuse. I am making excuses for myself, but, um, but you've definitely given
me a lot to think about. There's some definite improvements we can make. I mean, as there always
is with these types of things, there's pretty much an infinite number of improvements that you can
make to these things yeah so i think
given the time we should probably leave it at that um this has been incredibly useful and i think you
know i can definitely claim this back this time back because i'm gonna you know as in like company
time this is a perfectly good use of company time to to teach me some ideas about how to improve my
setup and uh and then hopefully to tell our listener the virtues the many virtues of
deploying the hello world app before you've even written the rest of your code yes and then make
it fail and then make it fail yeah cool cool all right well um i guess we'll leave it there
until next time until next time you've been listening to two's compliment a programming podcast by ben
rady and matt godboll find the show transcripts and notes at www.twoscomplement.org contact us
on mastodon we are at twos compliment at hackyderm.io our theme music is by inverse phase find out more at inversephase.com