RailsConf 2020 CE – Measure Twice, Cut Once by Alec Clarke

– [Alec] Welcome to this couch edition of RailsComf 2020 My name is Alec I’m a senior software developer at Clio Where we work on transforming the practice of law for good Now early on I was introduced to woodworking I’d spent many afternoons in my grandfather’s workshop, watching him turn these rough cuts and material into beautiful pieces of furniture That ability to make that type of transformation really stuck with me So it was no surprise that a number of years later, I also took up woodworking I started with the beginner projects like picture frames and building this box and eventually after honing my skills for some time, I graduated to more advanced topics like building this rocking chair Now as I graduated school and I moved away to start my career as a developer, I lost touch with woodworking And it wasn’t until this past year that I was able to rekindle that passion, when I decided to enroll in furniture building courses at my local community college Now the focal point of the first course was building this shaker table The shaker design is relatively simple It has nice clean lines and tapers cut on each leg and small details that give it a very classic look It’s a relatively simple build itself, but it does a great job of introducing beginner woodworkers, to the basic techniques required to make quality furniture Now going into this, I expected to learn plenty about woodworking But I was surprised that I also learned quite a bit 6about writing better code and being a better developer So in this talk, I wanna walk you through the journey of learning to build this table and three of the lessons it taught me about being a more impactful developer And no better place to start, than with safety first The first lessons in the workshop are always around safety A6t least they should be if you wanna end up with as many fingers as you started with And that was no different than the course that I was taking We took time to understand how each tool worked, what its purpose was, the risks associated with using them and how to protect ourselves from hitting critical issues, and being able to recover quickly when things do go wrong As an example, this is the table saw It’s usually the workhorse of most shops It’s used for making straight accurate cuts and doing it very efficiently It’s an important tool to get used to because it’s used in at least one step of most projects But it’s also responsible for the most workshop accidents However many of these accidents can be prevented when we use the standard safety mechanisms for operating the saw These are things like using a blade guard whenever possible The blade guard simply a plastic shield that sits over the blade It allows the material being cut to slide under the blade, keeping your hand from accidentally contacting it On a similar note, this is a push stick Well, it might seem like a very primitive tool and it is, the push stick keeps your hand at a safe proximity from the plane when making narrow cuts It also provides extra leverage to push that material completely through the cut without having to lean over the blade to do so So these safety mechanisms and others like them are easy to build into a checklist each and every time we use the saw They prevent us from hitting critical issues and they also ensure that we have a long and safe career in the workshop Now when we chip code, we might not be at risk of losing our finger However, that doesn’t mean we shouldn’t take, shouldn’t and can’t take steps to prevent ourselves from hitting critical issues, and being able to recover from them if and when they do pop up So to see what that could look like, let’s introduce the contrived Rails App example for this talk It’ll be an online store front

for Artisan Tree-To-Table furniture store Where our customers can go ahead and place orders for finely crafted handmade furniture And in our app we have a material purchaser class As the orders are placed, we need to go ahead and actually purchase the material to build them So the purchase material for method, accepts an order and a list of project materials as its agreements It then iterates through the materials, sending request to the O.K lumber supplier, to make sure we purchase what we need After that’s all done, we go ahead and Mark the order as having material purchased Now lately we’ve had a requirement change, the lumber we’re getting back from our suppliers, just okay, what we really want is the best lumber Now lucky for us both these suppliers have a consistent API and this is really the only change we need to make So let’s go ahead and chip that Then our error monitoring starts to detect some exceptions and we quickly realized we just shipped a bug Each new order that’s being created, is failing to have material purchased for it So as fast as we can, we get a revert going, get sign off on that revert, wait for our painfully slow CI process to finish, then we can merge it, go ahead and deploy it Now that’s a pretty painful process to get a quick fix back into production Yet, if we had taken a safety first mindset upfront, things could have gone a lot differently Ideally we could have wrapped our change in some sort of conditional that basically said, hey, well everything is going as expected Continue to use this new code, and if it isn’t, go back to using the old code that we already trusted But really this condition, could take a step forward by making sure that it only allowed a subset of accounts to actually access that new code and it would incrementally rollout to the remainder, over time Giving us the ability to test the waters before we exposed our entire user base to a bug We’d also want it to be consistent so that once a customer was using the new code path, it would continue to do so and it wouldn’t flip flop between the two And lastly, and most importantly, we would want the ability to roll back instantly No need to go through the deploy process at all So let’s go ahead and build that We start by creating a new resource called a stage rollout Stage rollout is going to handle all of this type of incremental rollout logic for us And to start there, we’ll work with the migration In the stage rollouts table, we’ll define two columns of interest First being the name column This will basically give us an understandable name to give us some context to the code we’re rolling out Cause we might wanna to use this in many places, not just for one specific change Then we define a started at column to indicate when that change has started to rollout to customers Jumping to the model, we’ll implement a ramp up of eight hours, indicating how long it takes to go from 0% of user traffic to 100% Then we build the percent enabled method , simply returns a percentage of how far into that ramp up process we are for a given rollout, once it’s been started But we need a kind of better interface to use it than just calling the model itself So we go ahead and build a rollout service The rollout service has an enabled for method that given the name of a rollout and an account ID will return a bullion indicating whether or not, that account has access to the new code we’re rolling out But we want that response to be consistent So once it’s true, it’ll always be true To do that, we take two steps First is we create or find the rollout, based on the name we’re given Then using some modular arithmetic,

we’ll define a formula to give us that consistent response back Now, I’m not gonna dive into the math of exactly what’s going on here, so I invite you to, after finishing this talk, come back, pause it, take a look through yourself However, I do wanna point out that this type of strategy, has a major benefit of not relying on any sort of persistence to keep the state of whether or not account has access to a given stage rollout Now, one last step for us here, is that we need to be able to roll back instantly And to make that happen, we’ll connect our stage rollout resource to the admin panel, and we’ll provide a simple UX that allows us to start, stop or pause the rollout without having to change any code at all So coming back to our hypothetical goal we set earlier, really all we need to do to make that reality, is change the condition to call the rollout service, enabled for method And there we go Now when we go ahead and ship that change, we’re inevitably gonna hit the same bug we did earlier, but that bug is only gonna affect a subset of accounts, because of her rolling it out and getting back to a stable state is just one click away So now we’re finally back to normal, except for all those orders that are now missing material to be built from Well, let’s fix that by jumping into the production rails console We’ll work our magic here, find all the orders that are missing material and go ahead and repurchase that I mean, nothing could go wrong with that approach, except this isn’t testable And there’s no way to really undo that Once you’ve pressed enter, there it goes Not really reviewable unless someone’s either screen-sharing or standing over your shoulder, but that’s still not a great approach there And most importantly, or at least equally as important, is that to run this, developers have to have access to the production environment Meaning that they also have access to all customer sensitive data So if we can’t do this safely, how can we get a quick fix out and do it safely? Well we know it would need to be testable We also know that it would be a one off fix This isn’t code that would be run many times and each fix is kind of unique and definitely you would have to be hands off, no production access required A good starting place might be, creating a new application job to run the fix for us We would use the same fix we wanted to run in the production console, add into an application job and you know that’s very easily testable No problem testing that As far as one off though, the application job doesn’t really give us any functionality for that So why not inherit from a new type of job, call them maintenance job In the maintenance job, we’ll define a version to be re-later defined by any subclass of the maintenance job and this version will give us a timestamp to provide some sort of one off running capability Then we’ll create a maintenance run record that simply stores that version for us and make sure it’s persisted Now back in the maintenance job class, before we ever en queue any new maintenance jobs, we make sure to record that maintenance run, We had to runnable check then that basically says if this maintenance around has already been recorded for a given version, it’s no longer runnable And so now we have that one off functionality we’re looking for, but we still need to make it hands-off So there’s two more steps to do that The first, is we define a run pending jobs method for the maintenance job It finds all descendants of the class that are still runnable and then it queues them up to be performed later And to tie it all together, as part of our deploy script, we simply kick off that round pending jobs method each time

That means that as soon as we ship a new maintenance job fix, we can ensure that it will automatically be en queued and run for us So coming back to our fix for the orders that are missing material, we’ve already inherited from the maintenance job All we need to do now is to find a new version and that code will be fixed for us So by taking a safety first mindset and building in safety mechanisms, we save ourselves a lot of time from having to scramble and figure out how to fix them later And we also prevent a lot of pain from our customers as they wait for us to get those fixes out Lesson two, Solid foundations After learning how to safely conduct ourselves in the workshop, it was actually time to now move to learn to build the table And our starting point was with the rough material So if we were to go ahead and actually start cutting pieces of the table out of this material and slapping them together, we’d probably end up with something resembling this An important step in woodworking , is the lumber prep process This process aims to eliminate the different types of deformations that naturally occur in wood Things like bows or twist in the lumber itself We need to remove those defects so that we have a solid state to build from in each subsequent step And to help us do that, there’s really two machines that are really useful in getting us there The first is the jointer A jointer has a long flat bed, and in the middle there’s a rotating cutter head So each time the material or the Lumber’s passed over it, it removes a very small amount of material So if a board has a few high points that need to be removed, to get to that flat state, it slowly removes those high points until the lumber is exactly and perfectly flat With the jointer, we aim to get one face of the material flat and one edge straight From there, we move to the thickness planer Now the thickness planer basically works as an inverse to what the joiner did It removes a small amount of material from the top of the lumber itself Now that we’ve already have a flat bottom for the material, it will make sure that that top is parallel too And as we run it through, we also get the added benefit of getting the exact thickness for that lumber that we’re looking for So as I started with this rough material, after I’d taken time to prep it, I got a nice solid flat and straight foundation to go ahead and start to cut out the table legs Now there’s no way I could’ve gotten four straight and consistent table legs without having to go through that lumber prep process Oftentimes when we’re writing code, we also start in a rough state It’s rare that we get a chance to start from a new clean spot, so why not take time to prep that code before we add any additional changes to it Coming back to the material purchaser that we looked at earlier, we know that even though we wrapped that change in a rollout, we were still hitting a bug So let’s open up that method and take a bit of a more detailed look to see where that could come from And as we do that, we see maybe that method was in a pretty rough state to start with And digging in a bit more, we see the bug and this came from human error with the best lumber supplier, We made a mistake And we used the wrong index to access the type value from the material array Now ideally what we would have done in the first place, is take time to prep the material purchaser class before we even added this rollout and this new lumber provider So let’s rewind and get back there Now back at the state where we haven’t added any rollout yet We know that on the first pass of our code prep,

we wanna go ahead and address the most glaring issue And that’s this material array and understanding the index structure around it To start to clean that up, we’ll introduce a new class for the material itself This will hold onto the details about the thickness, width, length, and type of material being purchased Then in the purchase material for method, we’ll map all of the incoming materials, to a new material instance That allows us to then update the parameters to use the kind of semantic value from that material instance, instead of relying on the index structure Next, using some of act of support magic, we can actually just code through json on the material instance itself, saving us from having to explicitly define each parameter Then we’ll move the supplier URI out of the loop because it really only needs to be defined once and at that point our code’s already in a much more stable state with a flat surface So when we come in to add Riad or rollout, really all we need to do is wrap the URI itself and the rest remains consistent Taking the time to make sure that the code we’re working with has a solid foundation, has many benefits It gives us the understanding of what the code was doing in the first place and it gives us a clear and consistent structure to build from Lesson three, quality control Now after cutting out the table legs on our previous step, we knew that the shaker design required that these legs have tapers cut in them So there’s a number of ways we could do that One being that you would trace out the taper in pencil on each leg, then move to the bandsaw and do our best free hand cut to get us kind of a rough cut of the actual tape itself But we would want to stay on the outside because we would want to get to that final dimension using the sander Now this isn’t a bad approach, but it would only really be useful for making maybe one or two cuts But each table we were working with needed eight tapers cut and there were 20 students in the class So if you were to use that, we would be in the class for a long time and the variance between the quality of each taper would be all over the place Alternatively, we could spend some time up front and create a table sled jig This jig would hold each table leg at a consistent angle corresponding to the taper we’re cutting On one pass of the saw, you could cut the exact taper we’re looking for and it would cut that exact taper consistently for each table leg Resulting in quality, resulting in quality and also resulting in a reduction of overhead of trying to maintain that quality Now, just like the tapers that we’re cutting on the table legs themselves, the projects teams we work with, there’s usually patterns that we want to be able to reproduce, but we want to be able to make sure that that quality, isn’t always consistent Coming back to the maintenance job we created earlier, this is a great structure for us to be able to reuse, but we haven’t taken any steps to actually make it reusable If we wanted a new maintenance job, might have to look at a previous example, then go ahead and do some coffee pasta, see if it works for our next quick fix and we might not realize that that version number has a lot of meaning, and if we went out and chip that, it would never be run for us So luckily rails comes to the rescue here Out of the box we hit the rails generator generator, and with that we can build a maintenance job generator What that gives us is the boiler plate, to create a new generator for every new maintenance job And to make that actually into flash it out, we really don’t need that many steps We’ll create a template for the maintenance job itself

and we’ll take the overhead or reduce the overhead of understanding that version number and specifying a new one, away from the developer, and we’ll define it for them Simply grab the current timestamp, give it a specific format and what we’re looking for , that will make sure that we also add a template to create a test for the maintenance job We hope that everyone is testing every new fix they’re sending out, but maybe they won’t, so we’ll ensure that that test is there and to ensure that it’s really tested, we’ll make it fail by default so they have to come back and address this Then the maintenance job generator really only needs to do two things, given a new maintenance job name, it simply creates the maintenance job file using the template and the maintenance job tests using the other template So firing that up to fix things, we get exactly what we were looking for, consistent quality where the version is defined for us, we know that that’s a valid version that won’t be run and we ensure that every fix is going to be tested Ensuring quality is a important step in any project that we work on But making sure that we can reduce the overhead of actually making that quality happen, is also an important step and it frees us up to focus on the new and exciting challenges ahead Now, while this wraps up, the three lessons that I’ll talk about today The journey to build that table didn’t stop here After cutting out the table legs and making sure those tapers were consistent, the rest of the pieces started to come together and after some time the table started to take shape and eventually, we had our table come to fruition Now, there’s no way I would have gotten to this final state without having taken a safety first mindset the entire time, making sure that I have a solid foundation with every piece that I was working with and having mechanisms in place, to ensure that the quality was kept consistent as well As my own woodworking journey continues beyond this table, I’m not gonna be as surprised at its ability to influence the way I write code Thanks for your time