The Bike Shed

thoughtbot
undefined
Jun 29, 2021 • 43min

298: Jawsification

Chris gives some small updates on working with Svelte. He really likes Svelte so far. Svelte's great. Modals are complicated. He also talks about using a little JavaScript library, called Quicklink. Steph talks about sending data to a third-party system and using feature flags to help deprecate some code. Finally, they both riff on a listener question on consulting. Said listener asked, "Do you think about your work as 'consulting first' or as 'building great software first and then good experiences for your clients will follow naturally?'" Find out their take and give us your own, here on this episode of 'The Bike Shed!' Bike Shed "Nate Berkopec" Episode Svelte Quicklink Boring Rails: Skip the bullshit and ship fast mtime (File) - APIdock Transcript: STEPH: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Steph Viccari. CHRIS: And I'm Chris Toomey. STEPH: And together, we're here to share a bit of what we've learned along the way. So hey, Chris, happy Friday. How has your week been? CHRIS: Happy Friday. My week's been great, yeah. I've been writing a lot of code, moving things around, planning some features, and all that fun stuff that goes into building an app, so I'm enjoying that process. I'm also halfway through listening to your recent episode with Nate Berkopec, which was absolutely delightful, well, at least the first half that I've listened to so far. I assume the rest will continue to be absolutely delightful, but it does remain to be seen. So I'll report back next week when I've listened to the whole thing. But yeah, that's great. And I'm glad that Nate got to come on, and we got to share a little bit of his story as well. STEPH: I like how clear you are in terms of like, "The part that I've listened to so far is great, but I reserve judgment until I've heard the rest of it." [chuckles] But that's awesome. CHRIS: The thing about being a developer is it has broken my brain such that I am overly specific all the time because I just argue with a computer all day. It's what I do. So then I start talking to humans, and I'm like, wait, I should probably behave differently now. And I got to unwind some of those computer fights. But anyway, and let's see, small updates working with Svelte, really like Svelte. I'm leaning into it more and more and embracing...I think I'm starting to understand the aspects of it that I really like. And one of the things that I really like about it is that it is somewhat underpowered. And what I mean by that is working on React applications, I find that I can do some fancy stuff, and I can express it really well in TypeScript. And I can really go for it and create some components that are wildly variable and configurable and can take in any combination of props and do all sorts of things. And I can slice out tiny, little components and do all of this. When I'm doing that, I enjoy it. But in Svelte, I have a little bit less power in my control. Svelte is closer to HTML, CSS, and JavaScript fundamentally. So you can make components, and I really like that. You can bundle up the pieces of functionality and display and formatting, and all of that, but it's not quite as powerful. It's not quite as expressive. And I've actually found that to be a useful limitation, which is an interesting frame. It's not something that I thought I would say, but I'm finding that the code that I'm authoring in my editor is so much closer to the code that's actually going to be presented to the end-user. That is really useful in my mind. I find that to be really valuable. There are small things like in Svelte; you can actually say class equals when you're trying to define a class on an HTML element. It turns out I really like that one instead of having to say class name or similarly HTML for. There is a handful of them in React that you have to change the name of. So if you copy a snippet of HTML from the web, and then you dump it into your editor, if you're working in React, you have to change a bunch of stuff. It doesn't work right away. And it's a small thing, but I found that I really seem to care about it. But there's the “it's nice that it just works” version. But I feel like there's also an actually practical, meaningful edge of it is so much closer to the thing that's actually going to be in the browser, and I like that. STEPH: I liked the phrasing that you used just a moment ago where you said, "Useful limitation." Since I haven't used Svelte myself, one of my understandings is that you like the fact that it is that low JS in terms that we are introducing this framework, but it's not as heavy-handed as React or another framework that you could retour. But then you also said you're running up to areas where you feel like you're missing some stuff from React, is what I'm hearing. Is there a particular feature, or do you have a concrete example to help me understand some of the stuff that you are really missing? CHRIS: It's not so much that I feel like there are specific features missing, but as a pointed example, I am not able to pass in the DOM element that I would like the component to render as. That's a weird thing, but often, component libraries will do this. So you have a button component, but the button can render either as a literal HTML button element or an anchor element. And you can pass in as equals and then button as the string there. And in React, you can do that, and then you can actually do some type inference across it and say, "Okay, now the rest of the props that you can pass in are button props.” And if you pass in as equals a, so implying that you want it to be an anchor or a link, then it will constrain you to the link properties and say, "Oh, you must have a HREF now." That's really cool that you can do that. It's also super complicated, and the TypeScript representation of it, while it works, is very, very complicated and the types of errors that you get. The complexity of what you can build with React is really interesting. But I worry now that I've spent a good bit of time in Svelte, I worry if it's overpowered. I've worked on plenty of applications where the system as designed in React, all the set of different components is very, very complicated. And you sort of have to learn that system in order to be able to work in it, whereas in Svelte, you just start, and you're writing in HTML and CSS. And then, as you need more fancy stuff, you can slowly layer it in. And to be clear, Svelte definitely has plenty of power. This past week, actually, we were working on a modal component, but we were really focused on accessibility, which is probably a good thing that you should do, but it turns out modals are very hard to get right. The dialogue component that should exist in HTML is not complete, and it's not a thing that we can rely on. So we have to do certain things ourselves. So the idea of focus trapping when the model pops up, we need to say, oh, okay, the focus should be trapped inside of here, so you can tab forward and back, but it's going to stay within that modal component. There's actually a way that you're supposed to portal it. So you move it outside of the documents so that you can make the rest of the document...I want to say aria-hidden is the property, but you're basically saying the entire rest of the document that's behind this modal component should be inert to a screen reader essentially or invisible to a screen reader while the modal is up. And doing all of those sorts of things is super complicated. After you close the modal, you're supposed to refocus the button that opened it, the triggering element, and that's a tricky one where you have to pass down a reference to something. And that was all very expressive, actually, very straightforward in Svelte in a way that I was really impressed by. So it definitely has all the power that you need but not any more than what you need. Or there is a small line of it's just right. STEPH: So we should just scrap modals. That's one of the things that I'm hearing from you. So I just want to clarify because I do feel a little confused because in the beginning, it sounded like you were saying that Svelte is wonderful, but you do feel like you're missing a little bit of functionality there that you do receive with other frameworks like React. But then that last thing you said where “it's just right” sounds like it's the Goldilocks. So I'm a little confused as to exactly how you're feeling about Svelte in the moment. CHRIS: Yeah. I'm probably not being as clear as I should. I am a big fan of Svelte, so as the first answer, a big fan of Svelte. I'm recognizing that, strictly speaking, it is somewhat less powerful than React. But I'm also trying to say, perhaps failing at saying, but trying to say that I like that, that I'm finding its constraints are useful. React can do a ton of stuff. You can represent a real impressive array of component functionality and have components that take 17 different props that covary in different ways, and it's very complicated. And I've worked on plenty of React applications where I just have to stare very hard at the component library for a while. And I'm like, ugh, I still don't know how this works. And it's this custom bespoke language where Svelte feels like it is much closer to the thing that we're actually doing, which is rendering HTML and CSS and JavaScript and whatnot, and I like that. I'm finding that very useful. I'm finding that lack of power not to be a hindrance but, in fact, to be useful. STEPH: Hmm. Okay. I like that last part. Yeah, there are often times where I feel like the less powerful something is, even if it means a little extra work on my end but it's clear as to the work that's being done...I'm going to take it back a couple of years to when I was first learning Elixir because that's how I felt jumping from Ruby to Elixir and from Rails to Phoenix, where suddenly I felt like I had more clarity. There were some things that I had to do more on my own, but I felt more clarity as to what exactly was being done versus Ruby and Rails doing a lot on my behalf. So I can certainly relate to that. CHRIS: Yeah, I think that captures it well, that the expressive power of React can perhaps lead to somewhat more confusing code, and the small handful of cases where I need to be slightly more verbose in Svelte I actually find really useful. Like, Svelte is making sure that I'm writing components that are clear and easy to work with, but it still has all of the power that I need, and I can do everything I want in it. And yeah, overall, just yeah, Svelte's great. Modals are complicated. And that's my story. But yeah, that's a little bit of what's up with me. What's going on in your world? STEPH: Before we switch gears. I want to add on a little bit more to what you just said because something that I have noticed with me is that the longer that I've been a developer, the more I want that lower-level control and understanding as to what is happening. And it sounds like that is very much what you're saying that you're enjoying with Svelte is even if it does require a little more extra effort, that then at least I have that ability to exactly control what's happening versus if you're using higher-level obstructions, you're stuck with the API that's been designed for you. And that API works 98% of the time, that's wonderful but then that 2% of the time you're in trouble. So I've definitely noticed that trend, that over time, I want that lower-level control over everything that I'm working with and building, although not all the way to C, let's not go that far. CHRIS: I mean, there's Assembly underneath C. We can keep going, and we can just manually manipulate transistors as well if we really want to get after it. [laughs] STEPH: Next week on The Bike Shed. [laughs] CHRIS: Much, much higher level of abstractions are interesting to me, but yeah, there is a sweet spot. Svelte seems like it's the one for me. STEPH: Nice. So then switching back to what's new in my week, it's been a little bit of a weird week in terms of there's been a lot of focusing on sending data to a third-party system. So we had a lot of data that they needed in their system. So I have been focused on running a number of processes that are then sending that data over and then essentially babysitting processes, making sure everything is going smoothly. Also communicating with their team to understand okay, what's being received? Do we have any errors? Is there any sort of miscommunication between our systems, and that's why we're needing to resend this data to you? So it's been very different in terms that it wasn't a typical feature development week. It was more, hey, I sent you some data. What did you receive? And then let's fine-tune both of our systems on each end, which that part I always enjoy. As soon as I can get to that level of collaboration with someone, I very much enjoy that part because initially, it felt like a stressful task of like, hey, we've got this giant CSV. We need to process and send data. But then as soon as I have someone else to work with, then I'm like, yeah, okay, this is great. They can update their system. We can fine-tune ours as well in case there's something that's not communicating properly, and that part I really enjoy. I really enjoy collaborating with someone else so then we can both improve our systems together, so that part was a little different. But the actual weird thing that I did this week is we have feature flags, and we are using those feature flags to help us sunset and deprecate some code. So we have a controller path that is pretty gnarly. It is one of the more dense, difficult areas of our codebase to understand. And so we are refactoring it and creating a new green space for it so we can start to pull in some of that behavior and then also refactor as we go. So we essentially have class version one, and we now have class version two, which is always something. And we want to be able to feature flag this because, with our deployment workflow, we need the ability one; we want to be able to switch back quickly. So that way, if something goes awry, we can switch back to the original code if we've made some misassumption in our V2 version. And then we want to leave that on for a while to make sure things are running smoothly, and then we can go back and actually remove that class. But then the question came up is like, well, if we have these two files, how do we tell the team not to touch this particular file but only contribute or make a change to this other file? Because we have a sizeable team, and we work in different time zones. And there is a very reasonable answer that we communicate with the team that other folks are aware because they've seen the PR. There's a whole self-discipline of we review PRs and make sure stuff wasn't changed. All of that stuff is fine. It's reasonable. But I wanted to do something a little less reasonable [chuckles] that would still fail loudly in case someone changed a file. So the question was presented is there a way that we could fail loudly if someone changed this file? And there's a fun thing that we'll do at some of our daily syncs where someone will say, "That's a good idea. I have a bad idea." There's a fun thing that happens at our daily syncs where someone will often ask a question, and someone will provide an idea. And then someone else will say, "That's a good idea, but just to throw it out there, I have a bad idea. So let's just explore all of the ideas." And one of them was like, "Could we write a test around this? So if the file hash or something about that changed, then could we alert the team so then we know that this file changed and you're not supposed to change this file?" And essentially, having that discussion of like, well, then we're reimplementing Git because we're trying to track file changes. That seems like a bad idea but still a novel one to talk about for a few minutes. The implementation that I landed on and then shared with a person that's working on this is you do have the ability with Ruby, the file class itself; you can open a particular file. And for this one, select class one, and then you can use the function mtime, which returns the modification time for a file. So you can check the last time that a file was changed. So I wrote a test that says that "This file was last altered at…" and I grabbed that file's last altered at time with mtime. And then, I compared that to a particular DateTime. And then that DateTime could be any DateTime in the future once we deploy this class version two, so we don't expect that file to be altered. So this test will always pass until someone changes that file. And then Ruby is going to say, "Oh, your time is now greater than that other time you said." And so it's going to fail, which actually works pretty well. It's not as ugly as I thought it was going to be. [chuckles] As to whether it's a good thing to add to the codebase, I don't know, but it was a fun thing to write. CHRIS: I like it. I've definitely written things like that in the past, and I guess; therefore, I'm biased. [chuckles] I'm a fan of this sort of thing. But when you can take that group knowledge that is just shared in communication or via code review and you can capture it in the code, especially if you can do it in a stable, robust way…In particular, the first thing that comes to mind with that is like, well, are there going to be different representations of the timestamp on your system versus CI? Will that ever change over time? Like, Linux versus OS X or things like that. I actually have reached for Git in situations like this in the past. So, in particular, the one that I found myself doing a few times is trying to instrument code generation. So say we're working with Apollo, and we are generating the TypeScript types associated with a GraphQL request. I wanted to put something into CI to say, "If we haven't committed those changes," because we're supposed to be committing those files alongside, "then warn." And so the idea was take a snapshot of what things look like right now, run the command that does the code generation, and then check after that. I've done different versions where it's like, hey, Git, is the working directory dirty at this point? That's a version. I've also done one recently where I got the checksum of the file but again, asking Git. Because you're totally right that a lot of this...this is what Git does, and we don't want to rewrite Git. But I did feel okay reaching out and being like, "Hey, Git, can you help me understand the word?" But I like these sorts of things, particularly if you can do it in a way that won't ever require someone turning it off. I don't know if you've worked on projects where ESLint is enabled, but every third line has an eslint-disable-next-line. And it's just like, well, we have a bunch of rules, but we ignore them in a lot of cases. And those sort of...the like trust scenario with an automated tool I think is so important. If it's ever giving you false positives, false negatives, whichever it is, then it immediately, I think, loses so much of its utility. But if you can do it in a way that is stable and robust, then I am a huge fan. STEPH: Well, we'll see if the person decides to include it in their PR or not. But I do like that idea of where we can take away the idea that we're going to catch it if it changes in a PR because then we're just going to end up in a bad place that if we fix a bug in the class V1 but don't apply that to class V2, we're just going to be in a bad spot. And it's likely we'll forget about it when we go back to then delete class version one. There is something that you said that has reminded me of a very small change that I made to my process, but I feel like it had a big impact. And it's specific to working with feature flags, how often you'll have your tests where it's like if feature flag is on, this behavior should happen, if it's off, this behavior. And I often would wrap my test in the default path where the feature flag is off, and then I'd have my other if the feature flag is on; this is the behavior. But as we are migrating with the intent that this feature flag at some point in the near term future is going to always be on, so we know we're going to come back and remove all of the other code. I switched those two paths and treat the default happy path as the new if the feature flag is on; this is the new world. So then when folks are going back to say, "Okay, I just need to delete everything that represents when the feature flag is off," suddenly, it's just very easy to find that context to say, "Hey, feature flag is off and then boom, delete all of those tests." And that's been really nice. CHRIS: I really like that lens of designing or coding for deleteability. How easy is it to just rip this thing out? It's one of the things that I love about Tailwind, or one of the purported facets of Tailwind that makes it really nice is when you're looking at a given template, you can just rip it out. You don't have to worry about it because there's no associated CSS that you need to think about because the CSS is sort of generated available, whatever you want to call it with Tailwind. But I really like that idea of making it easy to delete stuff. Because it's so easy to just have your codebase slowly grow over time and look at files and be like, "I don't know if we're using that, but better to be safe." Cool. I'm excited to hear if that does land in the codebase and how folks respond to it. What did you phrase the message as? So if there's a test failure, did you give a particular like a special RSpec formatted message to be like, "Hey, friend, you're not supposed to touch this file. I know you're well-intentioned, but…" or is it just like, "Failure, bad. Mtime is different." Which end of the spectrum are we on there? STEPH: I love that you asked that question because I almost went down that path, and I was like, well, this should really have its own custom failure message because it's odd enough that I want to tell someone a little bit of a story when it fails. But I didn't because this was something that one; I just want to see if I could do. So I initially started looking at standard rb in RuboCop because at first, I was wondering if this was something I could solve via linting if it was something that RuboCop…if I could say, "Hey, RuboCop, if you notice that this file changed…" I didn't know if they had a hook into Git as they're looking for files to analyze. So I first leaned on RuboCop standard rb, which essentially then uses RuboCop under the hood, and I didn't find anything there. So then that's when I was like, okay, maybe Ruby has something, and that's when I found the file mtime. So at that point, once I'd gotten the test to pass, I'm like, you know, this is good. There's a very nice, friendly test description that goes along with if this fails; this is the reason why. But I do think that would be like cherry on the top addition to the test to have a very nice error message that goes along with this. So if I were the one that was adding this to the codebase, I would take a few more minutes to do that myself. It definitely felt like one of those moments where I had gone far enough into an experimental mode, and I felt like I had just reached that point where this is useful, and I want to share it with the person who's actually working on this. But then I pulled back going further because I'm like, I don't actually know if they want to use this and if they're going to implement it. So it felt like that right friendly balance of like, here's something that works. Feel free to use as is, make it better, don't use it, totally up to you. CHRIS: Yeah, I think given that context, that's definitely I feel like a good line to draw, not like, “Here's fully completed code that you can now just drop in. I did all the work, but here it is.” Versus like, “Oh, here's a kernel of an idea if you want, run with it, but if not...”But yeah, [chuckles] if you went to the length of writing a nice paragraph summary message to the end-user, that feels like you're really taking over the show. So cool. Well, yeah, interested again to hear how that goes and hear if it does, in fact, stop. That's the other thing. It's like, if it never actually fails, then everybody was just fine with the human process. But I'm intrigued to see how many times it actually does stop unwanted modifications of the file. So that's an interesting measure to track. STEPH: Yeah, that would be an interesting thing to track because if we do have it, then we may have less visibility into knowing if it failed because then someone will see it fail locally, but then we will have prevented it from getting to that PR state. It is one of those “did someone not change it because we added the test, or could we have skipped that process?” It feels like one of those nice safety measures, but that would be a fun thing to measure, I agree. CHRIS: Yeah, especially if it's a small change; in this case, I think it's totally worth it. But now, as I said it, I didn't mean it to be more of a thing. But now that I think about the question, I wonder if all tests should fail at some point. Like, all tests have a cost, both in terms of development and then thinking about them in runtime and all of that. And a good test is one that eventually fails because you change the system in a way that broke some constraint. And so, therefore, I'm now asking the question, like, should every test fail at some point? Are tests that only ever pass actually not that useful? I don't think so. Now there's a story running in the back of my head that's like, I kind of want to look at the CI stats. And feature specs will occasionally fail for unrelated reasons. But unit-level tests that never break, that never fail and catch something that was broken…I don't know that I actually believe this, but I'm just intrigued. As I asked the question, I was like, huh, should all tests fail? Sort of like one hand clapping kind of thing, anyway. STEPH: I like the question, or it's making me stop and think because my initial answer is yes, as long as it's failing for a meaningful reason, as long as it's not a flaky test or something along those lines. But otherwise, as you're working on the system and you're making changes, then I'm inclined to say that yes, every test should fail at some point. But I agree, if we're getting into existential test area, then I don't have concrete feelings about this yet. CHRIS: Yeah, and I feel like it's one of those sorts of questions. So pivoting off of that ever so slightly to bring us to something much more practical, I have a tiny utility that I want to chat about. And then I think we have a listener question that we want to discuss. But the utility, I think I brought this up on a previous Bike Shed episode, but the tool it's a little JavaScript library, but it's called Quicklink. And so the heading is instant next-page navigations. And so the way it works is it's just a little snippet of JavaScript that you'll include from a CDN, or you can NPM install it or any number of ways. But it's a tiny, little one kilobyte JavaScript thing that basically what it does is it attaches to every link on the page whenever you use that link. So you click on it or if you're on mobile if you tap, or however you're interacting with it, if it's an internal link, so not external to your site and not going to a different domain, but if it's internal to your domain, what it's going to do is it's actually going to prefetch in the background as you hover on that link. So it's going to say, "Hover is a good indication of intent to follow this link. So we're going to prefetch it in the background." And then when the user actually subsequently clicks it, which is often a couple of 100 milliseconds later, that's often enough time actually for the page to load in the background. And then, when they click the link, it almost feels like instant navigation. There's a similar thing that happens based on when you tap and when the actual firing of the link happens on mobile. So there's another delay that they can take advantage of there that's not quite the same as hover. But overall, it just takes basically any webpage, any website, and makes it feel very much faster. And it's cheap, easy, just kind of works. I really like it. It's a very interesting little project. STEPH: I'm fascinated by how that would feel as a user because if I'm hovering over a link, I'm thinking through my specific navigation habits. So if I'm going to a link, like, I don't hover very long. I don't think of myself as a hovering internet user. [laughs] I'm probably going to click on it right away. So I wonder if I would still feel that same speediness versus...yeah, I am interested in the metrics if they have something around like...I don't know why they would know this or have this, but like, most people hover for this long. And so then it speeds up their feeling of the page load. I'd be interested in that. CHRIS: I like the idea that you're bracketing yourself into the quickest click of a link in the west. I'm looking around on their website, seeing they have a quote from NewEgg at the top, which is, "We implemented Quicklink and saw a 50% increase in conversions and 4x faster page transitions." So it sounds like I'm reading an ad for this now, which I'm not because it's a free project. So you can use it or not and pay the $0. They have a demo, and then they have a measure page. So I think you can actually get to...I think they're just talking about how to measure it. But I've definitely seen another page where you can click on a link, and it will tell you what was the difference between hover and active when you actually interacted with it. And it turns out the bounding box for a link is bigger than what you see. And you're often moving your mouse not entirely to the center, but you're not just getting to the edge of it and clicking. And so that period of time where you're moving your mouse onto the link, there's actually often a couple of hundred milliseconds, which is enough to really make a difference if you've got a speedy site. You can take what feels like a couple of hundred milliseconds and turn it into nothing. STEPH: All I can think of right now is the image of a little mouse that's moving closer to a link with the Jaws' theme song playing. So it's ta-dum ta-dum. [vocalization] And this whole time, Quicklink is getting ready to then load as soon as the mouse reaches that perfect zone to then start loading. That's what I'm getting is Jaws and Quicklink. [laughs] CHRIS: I like the...it's not personification, but it's jawsification that you're doing of this JavaScript library where it's like, I just imagine them hovering on the side really watching intently. But on the sites that I've used it, it does make a noticeable difference. I feel the difference even with very active clicking. STEPH: That sounds really neat. I'll have to look into it. Maybe I think I'm the quickest click in the west. That's very hard to say. And it turns out that I'm actually quite slow, who knows? CHRIS: You might just be average; that’s fine. STEPH: No way. CHRIS: Most people are, mathematically speaking at least. [laughter] STEPH: Not possible. I'm certain that I'm special. I hope listeners get a kick out of my oddities, [laughs] my very honest self that's coming through on the mic today. CHRIS: We're all a little special. But pivoting one more time… STEPH: That means no one's special. [laughs] CHRIS: Are you just doing the quote from Incredibles, or are you actually trying to say that? [laughs] STEPH: I wasn't intentionally quoting The Incredibles, but I did just watch that movie recently, and you're totally right. I am quoting The Incredibles. CHRIS: This is our second episode in a row then with a Pixar theme, which is always fun. But pivoting ever so slightly, I think our final pivot for the episode, we have a listener question today. So this question comes in from Matt Swanson, and he is asking about consulting first versus software first. So his question is, "One of the biggest turning points in my career was realizing that software consulting is, well, consulting. Do you think about your work as 'consulting first' or as building great software first and good experience for your clients will follow naturally?" So, Steph, what do you think? STEPH: I liked this question because it really made me stop and think about the differences in how I approach my client work. So I will say that I do think it varies slightly for each client, but most of the time, I do think of my work as first building great software. And then, once I've had time to understand how the team works and then identify opportunities for improvement, then I'll put on my consultant blazer and start scheduling meetings. I'm just kidding. I don't like meetings, so I don't do that part. But I do find that most of my engagements are looking for initially a strong developer to help contribute to the team and mentor. And then, I find that a lot of my consulting skills can then start to shine once I have that opportunity to build trust and then share outsider views with the team and then coach them in other directions. So I do take the approach of building great software first. Although this question really made me pause and think about it because I do think of the consulting and building software as so tightly coupled. It's a little hard for me to define when am I switching from my developer hat over to more of my consulting hat. CHRIS: Yeah, I think my initial reaction to the question was similar where I don't view these as two different modes that I'm fundamentally operating in. It's a continuum, or it's like a two by two grid thing, and I'm sort of moving around between the different spaces, but there's always a little bit of both. And I think if I were to answer the question directly, I would lean towards building great software. That's always the thing that I'm trying to do but often that requires some other more human-centric interactions. So having a difficult discussion around a feature and why we may not reach a deadline that we're going for or talking about ways in which the workflow is not necessarily going as well as it could, and we're ending up losing information along the way or different process things, all of that is a little bit removed from building great software. But at the same time, it's...actually, this is true of me now. I'm not technically a consultant anymore. I've stopped doing that, and I'm now full-time at an organization. And I'm not imagining my role changing fundamentally. I was consulting with them. I've now come on as a full-time employee, and I'm still viewing my work as very much the same thing. Maybe that's because I spent so long consulting that that's sort of the mode that I think of as how I work. But I think yeah, it's not necessarily two different modes. It's definitely a continuum that I'm operating across. STEPH: Yeah, I think that's why for me, it often varies. I like that word that you're using around how it's a continuum and that you're constantly sliding back and forth between one mode and the other. And if I think back to earlier days when I was working specifically with product teams before then, I joined thoughtbot and trying to think, well, what are some of the differences? How would I define what is more of my consulting mode versus then the building great software mode? Although I think the latter does encompass the consulting skills. But thinking back to when I was working on a product team, I found...and this may also just be because I was new in my career. But I found that I often referred to whoever was more senior on the team to handle a lot of those more human-centric topics, as you phrased it earlier. So if there was some communication that we needed to share in regards to why we were delayed on implementing a feature, if we needed to run a retro, if there were some meetings that needed to be scheduled, it always felt something like, oh, this leader of the team is going to take care of that. I am more in the development role, so I will do my job but then defer a lot of that to them. And then since joining thoughtbot with the way that we operate, I feel like I have more ownership in the process, and I want more ownership in the process. I want to be someone that is very aware of what are the specific goals that we're looking to reach? What are the deadlines? What's behind those deadlines that's encouraging us to push hard? And then also understanding how is the team functioning? What's something that we could do to improve the team's efficacy? Is the team happy? Are there areas there that we could improve? So I think for me, that is one of the core parts where I feel like I transitioned from being more focused on development to being more...you know, I don't have a great word for it. I often referred to it as being more of like a product owner. And since then, I feel like I have more ownership around the code that I'm working with and the team, and then the processes and the decisions for the product. But I actually don't have a great word that encompasses that sense of I want to be part of this and help make decisions and look out for everyone else that's around me. Does that resonate with you? Do you have any particular way that you would describe that or a word for it? CHRIS: I don't have a specific word for it. In my mind, this is just how we build software. But I think that that speaks to the culture that we grew up in as software developers. It's so strongly in our minds to think this way. A thing that we've talked about in the past is encouraging software developers to observe the sales demo, to see what it looks like when we're talking to end-users, or, similarly, to sit on customer support calls or listen to user interviews or things like that. And the reason for that is we want...I believe strongly that developers will do better work if they understand the context of the end-user of the application. But I think fundamentally, that sort of loads things up in someone's mind that might encourage them to push back or to suggest a different way of working down the road, and I think that's a good thing. I think every software developer should have some amount of that going on. And so that idea that consulting is this other thing that you sometimes do I feel like that stuff fits under the umbrella of consulting and, therefore, I think it's just part of how we build good software, but maybe it's a nomenclature thing, and I'm just thinking about it wrong. STEPH: Well, I want to pull at that thread a little bit because I was having that internal discussion with myself when I was thinking about this question is in regards to that being more aware of how the other teams are working to then help inform our decisions around the software that we're helping build, and implement advocating for a new process or advocating for how to build great software, is that consulting? I think you and I fall more into the camp of that's just how you build great software is; you have to be part of those decisions to be able to have more insights into the work that's being done. So I don't know if I could even really classify that as a consulting skill. CHRIS: Yeah, that matches my thinking. There is a distinction between consultant and contractor that I'll sometimes push on a little bit where I see consultants as being perhaps a bit more strategic and not necessarily being handed the work to do. I see that perhaps more on the contractor end. It's like, "We need a website built. Here are the specs. Here's the design mock-up. Please build it," and that's that. Versus a consultant being like, "We need a website, but we're not even sure exactly what that means. Can you help us think about the features and prioritize? Do we need a mobile app or not?" And a consultant potentially working more in that space of helping to determine what is the work that we're even going to do. But again, that's a question of like, how do we build good software? We have to answer those questions, and maybe not everyone on the team is always answering those questions. But the more people feel empowered to and feel like they've got the context to be able to make those sorts of at least suggestions around those sort of decisions, I think the better. STEPH: Yeah. I agree with the distinction in regards to being a consultant or a developer versus being a contractor because one definitely feels more removed from that decision or with that team collaboration process where you are more handed work, and then you implement that work, but you don't necessarily ask questions and be like, "Well, what are the benefits of adding this particular feature? Are we tracking to know that we've added the right thing?" those types of things that I would naturally include as part of my work. Versus if you're doing more of the contract work, then maybe you just implement and then don't ask those questions. Thinking back to then, what's different about being a consultant versus then doing development work…and I'm totally sidestepping all the financial stuff here. Like, if you're a consultant, then your world may be very different in terms of how you are acquiring jobs and then your marketing. So I am sidestepping that big conversation there but then focusing more on your day-to-day, how it may be different. And the times that I do feel that I'm wearing more of my lower-casey consulting hat is where I am joining teams that have a very specific goal that they have brought thoughtbot on to help with. So maybe there is a particular certification that they want their software to achieve, or maybe they're looking to level up their team and a particular tech stack, maybe it's Rails, maybe it's testing. And that one feels more focused on I am here to help provide an outsider opinion, to help evaluate your team, to help you provide advice, to communicate more with leadership that's on the team so then they know how things are going. That feels more like a consulting engagement that is less focused on building great software. But I feel like that often still starts with we want that stuff, but we also still want great software. So I always feel like I'm in both, and I really can't be as effective at the consulting part without actually working with the team and understanding the struggles that they're going through. So I still feel like they fit very hand in hand, but I do find that there are certain engagements that do require more external communication versus the others are often more internal with the team that I'm helping build software with. CHRIS: Well, I like that as a framing, the internal versus external communication and sort of the ratio of those. That's an interesting one. STEPH: To me, that's really what then sort of differentiates the consulting versus the just focused on building great software is if I'm doing more external communication, I'm focused less on the building part of the software but more on the guidance part. CHRIS: Yeah, I think that's a really good encapsulation or perhaps a way to differentiate the two ends of this. But I think both you and I probably feel that this just varies project to project. In some cases, we need more of what would fall into the consulting bucket, and other days, it's just nope, we got to go in. We got to implement. We got to build a bunch of features. We've got to get to the MVP launch and whatnot. And that often requires a little bit less on the consulting or the external communication side. But I think it's a case-by-case thing. And it's not that I think of myself as one or the other; it's I'll scale up or down as necessary based on the context of the situation. So I am both, I think. STEPH: Two for one, consulting and building great software. [laughs] CHRIS: One-stop shopping, everything you need. STEPH: So, I do have a couple of examples that I can provide that may provide some insight as to how we view consulting a little differently than necessarily focusing on implementation. I feel that I'm still reaching for that separation between consulting and developing. So I'm going to focus on the external communication and the implementation. I feel like those are the two areas that are trying to be divided in this particular question. But I do have some examples from thoughtbot discussions around consulting. So every so often, we get together at thoughtbot, and we have these internal discussions where we talk about the different consulting challenges that we have faced. And it's a really nice time where we get together, and we may discuss ongoing active consulting challenges and questions that we have, or it may be scenarios that have happened in the past. And so then we present that scenario to groups, and then we break off into smaller groups, and then everybody has an opportunity to talk through how they would react, what advice they would give, how they would approach it. And I have found those sessions to be incredibly helpful, but I think it could be fun to share some of those examples. Folks can think about them as to how they would react to them. But I think this helps highlight why those consulting skills and then also building great software are so tightly coupled together. So this first example focuses on building MVPs. So let's say that you're working with a client, and you've been focused on building an MVP, and the engagement is coming to a close in a few weeks. But the client is disappointed that there is a particular feature that they're really excited about that's not being included in the MVP, and they'd really like to know why that particular feature was cut. And they are worried that that will actually cause the business to fail if they don't have that feature in the MVP. So that's something that often comes up when we are focused on scoping MVPs to make sure that we are aligned with the client team to understand what is very important for the MVP and what can be a fast follow. And that can be a thorny one, especially if someone feels emotionally attached to a feature that is something that can be tricky to navigate. And how do you help the team reach a consensus that this feature really does need to be in the MVP, or it's okay that it doesn't need to go out now, and it can be in a future iteration? And for another example, this one is more focused on communicating the progress of particular work and how it's going. So you can imagine this scenario coming from the client saying that they have been working with you for a few weeks and you've made good progress, but it feels like the last week things have stalled. And they don't understand why a particular feature is taking longer than expected to ship. And they haven't had any communication from the team regarding what's taking that feature a longer time to get out. So, again, these are just some scenarios that you can think through and imagine how then you would respond or handle each of these situations. But I think both of those are really great examples that focus on the more consulting aspect of our work and then when we need to have more external communication with teams, so then they feel confident that we are developing great software. CHRIS: I think this is the first time that I've observed us giving homework to the listeners. But I think one thing that I'll highlight is we are talking about this in the context of consulting or being a consultant. But I think both of those examples that you gave, and more generally, most of these sort of conversations, actually apply pretty equally to working within an organization as an employee. You're still working on projects. You still have deadlines. You still need to ship things. You maybe aren't shipping as quickly as you need to; that maybe needs to get communicated to both internally within your team and externally within your larger organization. So yeah, I think these are broadly applicable, and I think, yeah, rolling them around in your head, let us know if you come up with any great solutions. STEPH: And if folks are interested in these types of scenarios, then I'm happy to share some more of them. I could share them on Twitter or anywhere else that folks find helpful. But I really like that nuance where I feel like is a nuanced discussion between building some great software and then those consulting skills. So thanks, Matt, for submitting such a great question. CHRIS: And as an aside, just to give a little more context on Matt, he runs a blog called the Boring Rails, which,, if you are not following it, it is a wonderful, straightforward summary of small, useful tidbits of information in the Rails world that are boring, but that's part of what we love about Rails. So I highly recommend that as well, and we'll include a link in the show notes. But yeah, thank you so much, Matt. And on that note, shall we wrap up? STEPH: Let's wrap up. Show notes for this episode can be found at bikeshed.fm. CHRIS: This show is produced and edited by Mandy Moore. STEPH: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or a review in iTunes as it helps other people find the show. CHRIS: If you have any feedback for this or any of our other episodes, you can reach us @_bikeshed on Twitter. And I'm @christoomey. STEPH: And I’m @SViccari. CHRIS: Or you can email us at hosts@bikeshed.fm. STEPH: Thanks so much for listening to The Bike Shed, and we'll see you next week. All: Bye. Announcer: This podcast was brought to you by thoughtbot. thoughtbot is your expert design and development partner. Let's make your product and team a success.Support The Bike Shed
undefined
Jun 22, 2021 • 49min

297: We’re Making A Pixar Movie!

Chris gives the deets on that new new – (he joined a startup!) and laments about the back button being so complicated. Steph talks about extracting an untrustworthy service and likens the scenario to making a Pixar movie. You don't wanna miss this hero's journey! Eric Bailey's bunny updates Katrina Owen's Therapeutic Refactoring Talk EnjoyHQ User research platform Aurelius also a user research platform Dry Monad (part of dry-rb) Previous Bike Shed discussing dry-monads Railway Oriented Programming Bike Shed "Seeking Calm" Episode Previous Episode Discussing Multi-Step Forms Discussion thread on Inertia repo re: back button cache Transcript: STEPH: Yes, I was getting text messages from you where you were like, “Go on without me.” CHRIS: [laughs] Leave me behind! STEPH: [laughs] No developer left behind!! CHRIS: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Chris Toomey. STEPH: And I’m Steph Viccari. CHRIS: And together, we're here to share a bit of what we've learned along the way. So, Steph, how's your week going? STEPH: Hey, Chris. It's been a very busy week. There's been a lot going on. But the most delightful part of my week has been that Eric Bailey, another thoughtboter and also a former guest on this show, has a tiny, little baby bunny living in his backyard, and so he has been sharing updates about this little baby bunny. In fact, he's been sharing some pictures on Twitter as well. So I'll include a link in the show notes so other people can experience the joy. Also, the name of the bunny gets me every time. But they have named the bunny “Corndog.” CHRIS: Checks out. It seems like a very obvious name for a small bunny. STEPH: It gets me because that's such a big name. I don't know why it's a big name, but it feels like a big name for a little bunny. CHRIS: I can say yeah, it's a cornball. Yeah, that's a large name. And so a tiny bunny is a...it's like Little John from Robin Hood. It's perfect. STEPH: [chuckles] I kept referring to him as Corn Nugget, I guess, because of size. But yes, it’s not corn nugget; it’s Corndog. [chuckles] So watching Eric's little bunny has been delightful and a wonderful addition to the week. How about you? How has your week been? CHRIS: My week has been great. I was off on vacation last week (so you had a guest on), which was fun to just take a week off and reset the system. But actually, this week has been interesting. It was my first full-time week with a new startup that I have joined. I think, yeah, that seems to be the truth in the world. So a bit of a shift from what I've been doing for the last year and a half, almost something like that. The reason there's hesitance in my voice is because I've actually been working with this organization for six months-ish, depending on how you count it. I've been having conversations, and then it’s slowly grown over time where it was just conversations, and then it was an afternoon a week, and then one day a week, and then two, and three. And finally, we decided we think we've got an idea. We've got a thing that we want to build. And so I am the developer on this team, but we are an early-stage startup trying to build something. I'm now full-time on the project. I rotated down the other projects that I was working on from a freelance consulting perspective, and now I'm trying something new. So it's a very different vibe. Even though I'd been working with the organization for a long time, this week just felt so much more real. And there was so much more space, so much more room for activities, having a full week to actually work on things. So yeah, it's very exciting, it’s very new, it's very early stage, so all of those things are true. But there are a lot of great aspects to that, and I'm super excited about it. STEPH: That is some big news. That's a big change too. Well, I guess with consulting, there are the stresses that go into consulting and then changing projects and managing the projects that you're taking on. But then to joining a team and such an early startup team too...anytime, someone says startup life, I'm always like, well, tell me more. How calm is the startup life, or how uncalm is this startup life? CHRIS: It's somewhere between calm and uncalm, I would say, but in a, I would say purposeful and intentional way. I was looking for...this has largely been true over the entire time that I was freelancing, but freelancing was a way for me to keep the lights on, and stay engaged with tech, and continue working, and frankly, have more conversations and meet more organizations. But I was looking for something that I could engage with a bit more. I was looking for, largely, something like this. So it definitely is occupying a different space in my head than, say, any individual consulting client where with consulting, I was pretty rigid, you know, these are the hours that I'm working. When I'm off the clock, I'm really not thinking about it too much. I'm responsive if I see an incident or something like that or if the database falls over; I’m going to look at that on the weekend but otherwise, largely not doing anything. Whereas with this project, I'm somewhat purposefully allowing it to have a little bit more space in my head off-hours, that sort of thing. And I'm more invested in the work. It's not just a thing that I'm doing, but it's a project that I believe in. It's something that I want to exist in the world. And so, I'm engaged with it in a different way in that manner. I'm also engineer number one, so I'm choosing all of the technologies and setting the standards. Thankfully, there's a lot of good thoughtbot material out there that I can link to, which is great. But yeah, so it's mostly within the context of what I think startups can be. The expectations and the way that the team is working is very reasonable. And I think it's more for my own self. I'm allowing it to occupy a little bit more of my space, but in a fun way so far. STEPH: Well, along that line, in terms of choosing the tech stack and starting greenfield, I am curious to hear more about the type of project that you're going to be working on. But I'm also recognizing y'all may be in stealth mode. Is that where you're at, or can you talk a bit more about the type of work you'll be doing? CHRIS: We're stealth-ish right now, I would say, partly because we're likely in the process of rebranding, and renaming, and things like that. So partly it's just like, oh, I probably shouldn't say that. But at some point, this will become public, and so at that point, I can probably be a little bit more open about it. But at the end of the day, we're building a financial product, FinTech sort of thing. And the tech stack is relatively straightforward. I'm actually using my preferred tech stack is...I got to choose, so it's Rails, Inertia, and Svelte with some TypeScript because why not? And I love it, and it's fantastic. I continue to believe deeply in that tech stack. So, yeah, that's most of what I think is good to say now. But I think over the coming weeks, I'll be able to say more and share more. And I certainly will be able to talk about the details of building and growing a team and things like that. STEPH: Awesome. Yeah, you answered my other question too. I was going to ask what tech stack you chose. CHRIS: I chose the tech stack, the one with the acronym, which I don't even know...the STIR stack I think we went with or something. STEPH: I was about to say I don't remember the acronym. [laughs] CHRIS: I think I never committed to an acronym previously, and then that was the one that got thrown around on the internet. I think I just was like, in the next episode of The Bike Shed, I'll choose an acronym so STIR, why not? STEPH: I like it, causing a stir. CHRIS: But yeah, so it's a pretty sizable shift in my life. But frankly, I don't even know exactly the shape that the coming weeks will have. So it will be interesting to report back as things evolve and as new concerns and considerations come up. But, yeah, we'll save that for future weeks. For now, what else is up in your world? STEPH: Yeah, it's been an interesting week. There have been really two things on my mind, so one of them has been focused on writing a task that's going to process a sizable CSV. And then it's going to essentially enqueue a bunch of jobs and send off a bunch of data to other third-party systems. So that's been a big focus of the week. The other topic is what I'm going to call extracting an untrustworthy service into its own service. And I know that’s a bit vague, but I've got both of those topics. So which one would you want to hear about first? CHRIS: I definitely want to hear about both. But because you veiled it in mystery and said, “An untrustworthy,” that one's just going to call to me a little bit more. So yeah, what about this extracting and untrustworthy service? What more can you say there? STEPH: Good question. I'm glad that you picked the mysterious one and started there. That feels right. So this is a part of our codebase, and it's very related to also the task that I'm writing. So to provide a bit of context, this particular portion of the codebase manages a big part of where we are sending data from our application over to third-party systems. And it's a very important feature of our application. And it's also probably one of the gnarliest sections of our application in terms of there are tons of conditionals based on which type of service we're sending to or the discreet customer that we're sending it to, and any particular preferences that they need and how we're sending that data. And then there's also just a lot of room for ambiguity and errors. And when we are sending that data, was it actually successful? And what if it was successful, but we still got back error messages? What does that mean? Is that successful with warning? And so there are just a lot of unknowns. It's also one of the less tested areas of the codebase. So even though it's important, we really don't feel confident making changes at this point until we've added some more test coverage. And testing it can be a beast because right now, we really just want to add some security around that section of the codebase. So we're often going for high-level tests, which are then our slower tests, but then also means it's hard to test the more granular aspects of that code. This is that untrustworthy section of the code in terms that we're a bit skittish to make changes, but yet it's a very active part of the codebase, so not the best place to be. But we also recognize that this part of the codebase would be really well-fitted to live outside of the application. It really doesn't need to live with the rest of the application. And there are other services that need to be able to talk to the service as well. So instead of having it grouped together, which -- It's funny. I see your eyebrows go up when I talk about -- For people who can't see, Chris raised his eyebrows when I talked about extracting this to another service. [chuckles] CHRIS: That doesn't sound like me at all. I don't ever… STEPH: [chuckles] And since we do have other services that need to be able to pull data or to talk to this particular portion of the codebase, we are looking to then move it out into its own application, so that way, it can stand alone. It can focus on this one task, and then other services can benefit from it as well. And there's been an interesting discussion around, well, we need to make changes to this codebase. And we also have some recognition that we need to make improvements. Do we go ahead and go heads down for a bit and improve the section of the codebase, add more test coverage, get to understand more of what this code does, where the risks are? Or do we go ahead and extract it in its current form to the new greenfield space and just essentially port it, and then we work on it from that space? And so, there's been a conversation around which one do we do first? And I'll tell you my thoughts, and then I'd love to hear yours. As one of the primary individuals that's been working in this codebase, my stance has been let's leave it in place for now because I want to build some confidence around what this does. So I really want to have some confident understanding about the requirements, about when we extract this, what is that going to look like? But also, I feel like I'm in a place where I'm starting to understand the beast enough that I want to continue that progress and add some testing around it before then we just move it to this new location. And I can't decide if that's one of those decisions where like, I just feel too close to it, and extracting it feels risky to me. So I feel like we're adding on this extra level of complexity. Like, this is already code that's hard to understand. And then we're going to add this network connection on top of that where then we have to talk to it in a different way. And in my mind, that's adding another level of risk and another level of having to debug this service. So my current approach is let's leave it in place. Let's try to identify some low-hanging fruit. Let's go ahead and add some more tests. And I feel pretty good about that decision. I'm curious, what are your thoughts? CHRIS: I have a bunch of them. The first is that the story that you're telling here feels like the hero's journey of software development. Like, all right, we got this gnarly bit of the code. It's super important. It's super complicated. It doesn't really have any test coverage for historical reasons that are complicated, but here we are. What do we do? That story feels so true. It feels like there are nine Pixar movies about it if Pixar made movies about writing code, and they would be great movies. STEPH: That's amazing. [laughs] I would watch those movies. CHRIS: I think of it like Katrina Owen’s therapeutic refactoring, which I feel like is probably my most referenced...It's one of my two most referenced talks that I bring up on the show all the time, but it is almost exactly about that sort of thing. We've got this gnarly piece of code. It's super important, but nobody really knows how it works. But we know it does work, which is an interesting bit. And so to the question of would you extract as is or would you try and shore it up before you extract it? I am 100% on the side that you are on, which is let's shore this thing up before we move it over. Because moving it over, like you said, that's going to add the additional complexity and failure modes of network latency, network timeouts, async disconnects, whatever, any of those complexities. That's another set of failure modes that you'll be introducing or just complexity and things that you have to think about. So that feels complicated. Also, there's probably a poor analogy that I have in my head. But imagine that you're moving, and your bedroom is just a complete mess, and you're like, oh, there are some old to-go food containers over there. And I haven't done my laundry in a couple of weeks. I'm just going to throw it all on a blanket and take it to the new house, and I'll figure out what I want to keep on the other side. It's like, that doesn't feel like the right move. I would definitely throw some things out before I move to a new house. So I definitely lean in to let's clean this up and understand it so that when it's in the new place, we have a slightly more contained, understood, manageable version of the software to try and extract to a service. STEPH: I feel very judged for my moving style. CHRIS: [laughs] I mean, obviously, with software, you're doing the one thing. But did I just describe exactly how you move house? STEPH: [laughs] CHRIS: To each their own now, you know, whatever works for you. STEPH: No, I'm with you. I'm definitely the person that's going to clean up first before I put stuff in boxes. I'm going to try to give away as much stuff as possible. CHRIS: It's a great time to just figure out what's true in your life or what's true in your software. I am intrigued. So yes, I did raise my eyebrows when you mentioned extracting a service and other services talking to each other. In particular, the way you described this piece of the system, I would be surprised if there weren't data requirements and/or transactional consistency things that you wanted to uphold. And that's one of the main things that causes me concern when we're extracting services is if this thing still needs to know about a bunch of different pieces of data and if it's going to make multiple updates to different records where if one succeeds and the other doesn't, we should roll back the whole thing. You lose all of that by moving to a service. And so that's where my broad…like, I'm always going to question if we're going to surface this. So I'm intrigued. Is this thing a very functional piece of your system where some data comes in, some stuff happens, and you get data out at the end of the day? Or is it more operating on related data within your system and potentially updating records after the fact? STEPH: Yeah, that's a great point. For this area of the codebase, it does feel more functional in terms that we have data, and we essentially want to notify other people that we have this data, and then we want to share it with them. So there is still that coupling of where we need access to those values. So if we're sending it over to the new system, either that new system needs to be able to read from the same database, or we have to send all of those details over to the new system. So then it can build up the message and then send it over to the other third-party systems. So it feels more functional, but there are still some of those requirements that we need to think about. CHRIS: Okay. That definitely clarifies things. And I wouldn't say that I have a unified theory of services. But what you're describing feels like the type of service that I'm more open to. It sounds almost like a SendGrid where I want to deal with all of my application data. And then I send a bunch of structured data over to SendGrid, and their job is to send an email and retry as necessary or send a text message or even do a voice call if it's Twilio or something like that. And so they're really good at those weird things and the failure modes that exist in those communication channels. But that's not logic that I need to live in my app. And so what you're describing there definitely makes sense as something that could comfortably be extracted to a service and not have more complexity be introduced by that. You did mention something about services talking to services and other things. So is the idea that this would be extracted, then other parts of the system would also use it to communicate out messages or something like that. STEPH: Yeah, one of the motivations for extracting this is because we have another application that also wants to perform similar behavior. So now we have two applications that need to do similar work, and they feel more in that line of functional work where it would be great if we could share this. But it doesn't fit in the space that we want to extract it in regards to extract it to a gem and make it shareable. It feels more appropriate for it to be its own service and then also capture. Because the other nice thing that we want to include that we're doing now as well is we want to capture feedback from whenever we are sending that data over to other systems. We want to know, hey, how did it go? Did you give us back that successfully, but maybe with some warnings or some errors? Maybe you accepted the data, but then you also gave us a response about something else. I think one really important question to consider is when is it trustworthy enough to extract? Because we know we're headed down this path. So at what point are we ready to then go ahead and extract this over to its own service? And that was the more interesting conversation because I think those who were in favor of extracting it now had the concern that we can't add test coverage in its current form. So my first response was if I need to make changes and I can't add test coverage, I will sound the alarm, and we will reconsider. But my goal right now is to turn this untrustworthy service into a little more trust. Just dial up the trust a little bit further, and then we can port this over. So then, as we do add some network complexities on top of this, we will at least have more faith and understanding the underlying behavior of the system. But then we still want to understand that it's not going to be perfect. And we're not going to wait until it's perfect before we do extract it. But that's the tale or the mysterious extracting an untrustworthy service. So I think it will be an interesting journey. And it was a very interesting conversation that I was excited to have your thoughts because I know you and I often lean so far away from extracting stuff to a service that it was an interesting conversation to have around; well, this code is a bit of a mess. When do we start to tackle that mess? CHRIS: I like that you didn't even frame it necessarily in terms of that, but I still definitely got there. I was like, wait, wait, wait, but let's actually talk about whether or not. But this is definitely the sort of thing that I think makes sense to consider as a service extraction. I think the question that you're asking around when do we feel good enough in its current state to do the extraction? That's right on the line of art in the software world as opposed to the science of this is how we connect HTTP. So I'm very interested to see where you get to both with that question and how you actually make that decision and then how the extraction goes. And I imagine this will be the sort of thing that goes on for a bit of time. So it feels like we could make a mini-story arc that'll span a couple of episodes, and you can follow the characters on their journey. This is the Pixar movie. We're making a Pixar movie. STEPH: We're making a Pixar movie. They're missing an entire genre for their Pixar movies. If they just appeal to developers, that'd be wonderful. I’m so in for that. We should write Pixar. CHRIS: There are more developers every day, so think Hackers meets Up. That's what we're going for. We're just going to fuse those two together. It's going to pull at your heartstrings, but it's also going to talk about hacking the Gibson. It's going to be great. STEPH: Oh man, you reached for the most heartfelt one going for Up. That one has the toughest beginning. [laughs] CHRIS: That's what I'm going for here. STEPH: For anyone that hasn't seen Up, you can go watch the beginning of it. Just be prepared. CHRIS: And if anyone hasn't seen Hackers, also be prepared. [laughs] STEPH: Which is me. I haven't seen Hackers. CHRIS: All right. You still haven’t. All right, that's a thing we need to work on. STEPH: [chuckles] CHRIS: But cool. Okay. So we're going to work on the Pixar movie. You're going to update us because we need to actually gather the information. But yeah, we'll come back to that in future episodes. But shifting gears just a little bit, actually, I have a couple of things, two small things, and then one more sizable thing that is more just like, I'm confused. So yeah, we're going to go in that order. Thing number one is, we are, again, it's a very early-stage startup that I'm working with. And part of what we're doing that I really like is that we are talking to potential customers, potential end-users of the application doing lots of user interviews, which is a thing that I have more from a distance seen often. But now, because we're actually working as a distributed team, we're remote because that's the nature of the world right now. We'll probably meet each other in person at some point, but that's down the road. But all of these conversations are happening over Zoom calls, all of these user interviews. And so I made the suggestion that we use a tool to actually manage those. And so we're using a tool called EnjoyHQ, I think is the name of it. There's another similar tool called Aurelius. We can put the links in the show notes for both of those. But what it does is it basically makes the video available after the fact. I think it automatically transcribes it, and then it allows you to annotate and add notes and things like that, which is great for aggregating this body of information that we're collecting over time as we do all of these user interviews and start to tag common themes that we're seeing. And bringing them together will also allow us to revisit them. But for me as the developer, I've been to a few of them, but not as many as the rest of the team. And what's great is I've now taken to...as I'm doing more mundane…cleaning up email or whatever sort of tasks, I will just put on one of these videos in the background at 2X. And what's great is I can now just hear literally the voice of the users of the application. What are the words that they're choosing? How are they talking about it? What matters to them? What doesn't matter to them? What do they get really passionate about? And it's been just such a wonderful thing to have available. It's almost like a podcast of our app that we're building, and it's like, that's awesome. STEPH: I love that. Yeah, I would love to be able to hear from people that are using the application. And like you mentioned, just turn it on in the background so that way I can process what they're saying. But then, I don't know, depending on what they're saying, maybe it needs full attention or otherwise, maybe you're able to just absorb little bits and pieces while you're hacking away on something else. And now I've got the word hacking stuck in my mind. [laughs] CHRIS: It's the best word to describe what we do. Yeah, there's definitely a version of someone should be reviewing...someone's actually doing the interview, so they're going to be very close to it. And then there maybe is a secondary someone's watching it closely and trying to glean, and categorize, and all of that. And I could potentially be any one of those, but I really like this version of this is just a background soundtrack that I'm exposing myself to so that I'm all the more immersed in the problem space that we are working on. And it's one of the things that I fundamentally believe about software development is developers shouldn't be hidden in the corner just writing code. We should always care about what the end-user wants, and what better way to get there than to actually hear their voice and hear the words that they're using. So this is a magical little trick that I have now found that I'm like, oh my God, this is amazing. STEPH: Funny enough, I had a similar experience this past week where I realized I was feeling very disconnected from the people that are using the application and also the people that are setting priorities for the work that our team is doing. And that is something that I'm very accustomed to with thoughtbot that we always want to be part of the team. We're not necessarily just we can churn through a backlog. But we also really want to be in touch with product decisions, and share opinions, and then also be in touch with users too. So I had some similar revelations this week where I realized I was feeling very disconnected where I was picking up tickets, and I was like, I don't really understand why this is great or how this is helpful. And so, I shared that with the team, and someone encouraged me to attend a specific meeting. And that was wonderful because then I got to hear from the people who were creating those tickets and then giving them a high priority because something was urgent and why it was urgent. And having that insight was huge to me. And I realized that it was incredibly motivational as well. Because then I'm like, yes, okay, I understand how this is going to impact someone. And I'm now very encouraged to get this done. CHRIS: I think that idea, that ethos of wanting to get into the user persona and understand that better is a very strong thoughtbot ideal. So it’s unsurprising both of us share that. But yeah, that was a really great thing and particularly a tool that facilitated that in a really straightforward way, which I appreciated. Another thing that I used this week, which I've talked about at length in a previous episode, so we can link to that episode, but it's a project called dry-monad. So there's dry-rb is the collection of, I think, a set of gems, but dry-monad is one that allows for defining sequential tasks, so tasks that you have to do a bunch of steps in order and the outcome of a previous step will be the input of the next part of the process. So it can fail in a bunch of ways like, okay, fetch this thing from the network and then look up a user based on that. And then get the user's profile, which may or may not exist, and then assuming that all of that's gone well, actually persist to this new record, to the database. And they're really finicky to write that sort of sequential processing. And so I actually had written that thing manually. And part of it was I'd wrapped the whole thing in a database transaction, but I was trying to make it so that if something went wrong, I would manually roll back the transaction. And then I wanted to return an object to the caller that indicated that things had failed and an error message or something like that. And that was actually really hard to do because of the way transactions work. The mechanism that I was using was apparently deprecated in Rails. And so the whole thing was just kind of confusing, and it was a bit messy in the code. And I knew in the back of my mind that dry-monad exists. I've used it before. I've really enjoyed it. But I was trying to minimize the amount of new technologies that I'm bringing in this early on in the project. It's like, yeah, I'll bring that in when I need it. But finally, I was like, you know what? I think I've reached that point. I grabbed it, brought it in, and I haven't worked with it in a while, but I was very quickly able to refactor my class to use dry-monad. It cleaned it up immensely. The tests remained identical, which was really interesting. I didn't have to change anything on the test side. And one of my tests was failing before and then passed because of the introduction of dry-monad. And yeah, it was just like a win-win-win, and also the fact that I was able to revisit dry-monad as a library and just get running with it again was really interesting to me because it is a bit complicated and interesting in how it works. But again, I was able to just sort of pick it up and run with it. So that was wonderful. And I will now all the more staunchly suggest that folks reach for that when they have more complex, procedural type code that they need to write. STEPH: I remember you highlighting dry-monad before in previous episodes and talking about the pain of writing that sort of procedural code, but then we also want to return something helpful. And I looked at it briefly, but I haven't used it. But now that you are reminding me of it, I'm very interested in it because I agree that process is difficult to write, at least in Ruby it's difficult to write. I understand the hesitancy that you have around bringing something in that's new. But then if you recognize that it's going to be a theme in your application around this is something that we're going to do a fair amount, and we want to do it in a clean, efficient way, then it starts to feel more reasonable to say, “Okay, I'm bringing in something new, but it is representative of how we want to handle this step or this type of process in our application.” So it's not just bringing in a gem to handle one small area of the code, something that we could have written, but it is elevating our process and our system. CHRIS: Yup. Indeed. In this case, these are command objects within the system. That's actually the name that I got from the creator of the project. That was his suggestion on Twitter as to what to call these objects. And it's a pattern that I do want to encode and has become the standard within the application for any of these more complex processing tasks. So, again, we'll link to the previous episode. I talked about it in more depth and the ideas behind it. Railway Oriented Programming is a phrase that's used, which talks about how to sequence failures or successes and whatnot together. And there's some good material behind it, more general, but yeah, wonderful, little library. STEPH: What is Railway Oriented Programming? I'm not familiar with that term. CHRIS: That refers to the sequential processing that I was describing. So imagine that you have a bunch of different steps where first you fetch from the network to get this record, then using what you got back, you look up a user in your database, then you fetch that user's profile. Then you do something else. Each of those steps along the way could fail. And so the railway metaphor is the track is going forward, but if at any point you branch off the track because of a failure, then you're in the failure track, and that's a different thing. And so it's a very...the dry-monad or other similar Railway Oriented Programming or monads generally I think is the actual...it's the words in there. And I wish it wasn't in there because it's such a complicated word. But that idea is the fundamental, underlying thing that's going on there. And it is conceptually somewhat complicated, but if you don't try and think about the category theory behind it, and you're just like, well, I want to do a bunch of stuff, and it may fail at any point, and I want to return either a success message with everything having gone well or an error message at the point that it failed and stopped processing, then that's what this thing does, and it's fantastic at it. STEPH: Okay, cool. Thank you. CHRIS: You are welcome. And I think there's a bit more in the previous episode as well. So if that sounded interesting to anyone, I think I rambled more in a previous episode about it and probably better because I feel like I was more prepared that time than this time. STEPH: Well, along those lines of running a process and then being able to fail at any moment, I'm going to circle back to that other topic that I highlighted where most of my week has been focused on writing a task that is processing a CSV, something probably a number of us have done at some point in our career but processing a number of rows, and then sending and queuing jobs to then send data to a third party system. And it was really interesting less so because of the processing of the CSV and then enqueuing jobs. But it was more of the reporting that went behind it and the process that went into writing this task. So Joël and I were pairing on this task. Joël being another thoughtboter and also a former guest on this show. And we had an interesting process of where we started with one, let's do the simplest thing. Let's get it done. Let's also check through the CSV because you're often going to find stuff that doesn't align with what you expect it to when it's a CSV that's provided from an external source. One of the risks that we highlighted right away was how are we going to get the CSV on the server? Because we just have this one CSV that we need to run. We don't want to add it to the repo, and we can't generate it ourselves. So how are we actually going to get the CSV in a place that we can run this in a production mode? I learned that I could pass a CSV as standard input into the Rake task. So then I could actually run it locally because we're using AWS. So I could inform AWS to run this task, but then I could actually stream the CSV into the task that way. And that was really nice because then we no longer had the question of how are we going to get this file on the server? CHRIS: That's interesting. I didn't know...Yeah, the streaming of it from local to remote is an interesting one. On Heroku, I will typically open up a bash prompt, so Heroku run bash. And then, I will curl the file down onto the server and then run it locally. But that’s an ephemeral dyno. It may die at any point. There are various things that could go wrong there. So that's always interesting. I imagine a similar thing could be done, but I don't know, actually, if you can directly stream into a Heroku dyno like that, which is an even more straightforward one because I end up having to bounce a file off of a random. Like, I'll often put it in a Gist or a Pastebin or something like that. And then I'll curl it down to the server, and yeah, this is interesting. STEPH: Yeah, I'm also not sure the specifics of how it would work with Heroku. But it was a really nice process for us to be able to use versus having to then read the file from, like you mentioned, curl it from somewhere else and then be able to parse it that way. Two other things that were top of mind for working on this task is one, idempotency. You're going to rerun it, friends. At some point, your task is either going to bomb, and it's going to err. And then you're going to have to triage and run it again. Or whoever requested that you run this task and they said, “Oh, it's just temporary. We're just going to run the once,” that's not true. You're going to run it again. So keep in mind how to make that safe, that you can rerun it. And then that won't be its own scenario that then you have to triage and figure out. CHRIS: Idempotency is one of those critical ideas, and I just wish the word were different. I feel ridiculous every time I say it. And I feel like I have to push my glasses up on my nose, and I'm like, well, have we considered idempotency for this? But it's such a good idea. And it's the sort of thing that...you're totally right. Every time you're doing this sort of thing, it is something that you should consider. And we use GET requests, and they have rules about it. And it's such a good idea and such an important idea. And I just wish the word were different so that I felt more comfortable using it in polite conversation. STEPH: [laughs] I don't know why… and this may be sharing too much of myself. But the song Under Pressure by Queen and David Bowie the Under Pressure song has been in my head. But I've been replacing the under pressure part with idempotent. So it's [singing] under pressure, and I've been [singing] idempotent. [laughter] And that has just been my song for the week. CHRIS: You've normalized it enough for me now that I'll just hear you singing it every time, and I'll be like, this is a nonsense word. We're fine. We can just go – [laughs] STEPH: That's what I'm here for, to turn technical terms into nonsense. [laughs] CHRIS: It's really what this show is about at the end of the day. So you are our hero. STEPH: I just have to work on more lyrics for the song. I really just have that one line, that one hook. [laughs] CHRIS: Now I just want to scrap the rest of the episode and just come up with lyrics to idempotent. [chuckles] But maybe we don't do that. STEPH: [laughs] CHRIS: Maybe that'll be after the credits B-roll, something like that. STEPH: The other way I do phrase that question is I'm like, what happens when it fails? And that always feels like a safe way. Because if I ask someone like, “Hey, is the idempotent?” It feels more natural for people to be like, “Oh, it's fine. It doesn't need to be.” But if you say, “What happens when it fails?” It's harder for someone to say, “Oh, it's never going to fail. [laughs] There is nothing that could go wrong.” So it feels like a more intentional question in regards to how are we going to handle this when we need to rerun it? The other part that really came in handy was the fact that we spent more time on the reporting as well. So we really wanted to know what happened when we are processing all of these rows. So were there any invalid rows? And if we do encounter an invalid row, do we want to just stop processing and stop right there, or do we want to keep moving? Do we have any rows that didn't match, a row in our database, and how do we capture those? And because it's idempotent, maybe we want to capture skipped rows so then that way when we rerun it, we can see okay, well, we skipped, you know, a thousand rows because we'd already run them before. And all of that reporting has been so handy because we're also using this to triage. Like, hey, we're sending a bunch of messages to this third-party system. We reach out to that third-party group, and we say, “Hey, we sent you all of this. This is how it went. Let us know how it went on your end.” And then, we can have a more collaborative discussion around what happened on their end versus what happened on our end, and then we can make tweaks to each system. So overall, it felt more of that run-of-the-mill task where you're going to write a Rake task, you're going to parse a CSV. But something about the reporting really resonated with me because in the past, when I've written Rake tasks, I've leaned more on the this is temporary, so it's okay if it's not great. But the reporting has been so crucial that from now on, I always want reporting from any Rake tasks that I run, and I want to know what happened. And then I also want to be able to rerun it. And I'm very wary of any time that someone says, “Oh, this is temporary,” because then I also think that leads to interesting discussions around testing. Because initially, when we started this, we were under some pressure. Hey, that goes back to my song. We were under some pressure for writing this particular task. And then the question came up: do we want to test it? And to be frank, testing a Rake task isn't great; it’s not fun, which is one of the reasons we get out of a Rake task as quickly as possible and put it into a class. So that was also one of the drivers or one of the conversations that went against, like, oh, this is temporary. So it's okay if it's not the best code. It's okay if it's not tested. And then I was more of an advocate for, like, well, I don't feel good about this. And I'm rerunning the Rake task every time I want to confirm that the changes that I've made are correct. And so once I hit that manual labor point where I'm like, okay, I'm testing this. I just don't have automated tests for this, that then I actually started adding test coverage around it. CHRIS: I'm so excited that we have transcripts, particularly for that last minute that you were just talking about, because I feel like that was a mini master class in software development. And more generally, there's been almost like a poetic something to the two different topics that you’ve brought up today are the sort of mundane, very real things that actual software development is made almost entirely of. It's not often that we're just starting with a greenfield app and building a new thing. I happen to be doing that this week, but it's rare. It's going to be over very soon. And then I'm going to be in the world of oh; we have to backfill a bunch of data. How are we going to do that? Or we have this portion of the code that, frankly, we should have been testing more, but we didn't. How do we deal with that? And these murky, gray areas where there isn't a clear answer and you have to go with intuition, and you have to...a bunch of the things that you just listed as these good heuristics that you have around how you think about software. I'm just really excited for the transcript to that because that was awesome. STEPH: I'm so glad you enjoyed it because I think it's not until right now where I'm processing this and talking about it with you that it is...I was trying to think earlier, like, why is this so interesting? Why am I so excited to bring this here to this conversation? And I think it is for the reasons exactly that you said, that it does feel like one of these...this is a mundane task. We write a task; we process some things; we send some data. We do that all the time. But then it's all the other bits around it, and the other ways that we've been bitten, and how we avoid those scenarios, and then how we identify a risk like when someone says, “Oh, it's temporary. It's fine.” That part, I think, is always the very interesting aspect of writing software. CHRIS: Do you consider this sort of stuff the distraction from the work or actually the work? And in my experience, this makes up a lot of the work. And treating it like what you were saying about testing like, “Yeah, that thing where you're telling me that it's going to be temporary and we probably don't need to test it, I've been told that before,” and I just want to spot-check that real quick. Or what you said of the when I was manually testing, and I crossed a threshold where I'd done that enough, that now adding a test harness around that totally makes sense. It's worth the investment at that point. Those little heuristics that we build up over time are the things that are hard to get. And so, yeah, I love that conversation. STEPH: I really like how you also asked and then responded to that question around is this distraction, or is this the work? And I am wholeheartedly with you that this is the work. This is the part of the work that I do find interesting, and knowing when to make those trade-offs, and when you've hit a decision point, and which direction you're going to go, and being able to recognize something that otherwise could have been a fire. It could have been much worse in terms of if we'd built a task that wasn't robust. Because of course, then the second time that I ran it, you know, emphasis on the second time that I ran it because we needed to do it again to process more data. It erred halfway through, and I panicked in the moment. But then I was like, oh yeah, this is fine. We planned for this. This was in the game plan. So it goes back to that we want the calm execution. We want to plan so we are back in that calm state. We want calm software. And this feels very in line with how do we make this more calm? CHRIS: I love that theme that you're bringing up there, which I think is a theme that we've touched on a bunch of times. I think we actually have an episode called Seeking Calm. And I think that little title there, as much as I love the nonsense titles that we have for most of the episodes, that one I think really captures the theme that a lot of what we talk about is in orbit around yeah, we want it to be calm. We don't want things to be on fire every day. And what does it look like to build software with that in mind? STEPH: Yeah. I also love that theme. And I like that it's something that we have surfaced and then really stuck to because it resonates deeply with me. But that's pretty much all I have for my Rake task adventure. What else is new in your world? CHRIS: Well, I have one more hopefully quick thing. I'm going to try and boil this down to its essence, but I ran into, let's call it, a subtlety. It's not an issue. It wasn't a bug per se. But looping back to the previous episode that you and I recorded together, we talked a bunch about multi-step forms, which was a great conversation in and of itself. But I eventually completed the feature that I was working on, put it up for acceptance. And the product manager who was reviewing it highlighted a couple of different things. They recorded a video which, as an aside, I love that as a way to do acceptance and show what's going on and talk through it. There were a couple of smaller issues, which I was able to resolve very quickly, but there was one more interesting one that I've extracted this as future work because it was too complex to try and solve in the moment. But basically, what's happening is imagine that we have a two-step form. So there's the first page of the form. The first form that you see is for your name. So it's just an input that says, “Name,” and you fill in your name and then you hit continue. That posts to the server. We save off that data. And then, we redirect you to the next page on the form, which is, say, phone number. So two steps. We start with name; we go to phone number. What happens if you type in your name, you hit continue, everything processes correctly. You end up on the phone number page, but you hit back. What do you think happens? STEPH: I would expect to go back to the name field and probably expect my name to be populated but would also be fine if it's not. CHRIS: I like that you would be fine with the fact that it's not, if it's not, because it's not is the answer. And what's unfortunate is so if someone goes back, they will see the unpopulated form, so not filled out. But if they reload at that point, we will serialize down the value and pre-fill the input with their saved data. And so that inconsistency is not great. It's all the more unfortunate because as I tried to resolve it, I'm like, oh, okay, this feels like a solvable thing. I just need to tell Chrome, “Hey, if someone hits the back button, do a better thing than what you're doing.” I needed a way to instruct Chrome or whichever browser because this should be a standards-level thing. And there are things in the HTTP spec about this. So there's the Cache-Control Header is one of the headers that you can send down with a response. And there's a bunch of different values that can be in there, no-cache, no-store. There's also the…I want to say it's the max-age, or I think it's Expires. That's a different header. But you can set it to have an expiry that's just already expired. There's also a Pragma, which you can say no-cache. Some of these are standard. Some of these are not standard. Chrome ignores all of them. Chrome’s just like, “Nevermind.” So the idea is that those headers are intended to inform intermediate proxies. Say you have a caching layer, so you're using Fastly or CloudFront or something like that. When that service fetches the page from your backend, from your actual, say, Rails app, then it will look at that header and say, “Should I hold onto this for a little while or not? Is it public, or is it private? What should I do as an intermediate caching proxy?” Ideally, Chrome would also look at those and say, like…there should be a version of me being able to tell Chrome, “Listen, if someone hits the back button, please go to the server and ask for it.” Like, I'll take the second of latency that that introduces in navigating back because I always want to show them the correct data. Unfortunately, I have not found a way to do that. There's a bunch of things on Stack Overflow and other places of JavaScript solutions where I can listen to the window.popstate event and then force location.reload. But that feels like a pile of hacks that I don't want to get into. It feels like it will be very inconsistent between browsers. So I am still searching for a solution. But I would like to figure something out here. As a more pointed version of this to try and explain an example where this could happen, imagine that you've got the header of your application, and in it, you have a sign-out button. And so that sign out is going to delete to the session's endpoint. So you're deleting your session. And after that, you get redirected to the login form. If you then hit back, you will be taken back to the browser’s cached version of the previous logged-in state page that you were at. This is probably fine in a lot of cases. If you reload, you can't do any nefarious action at that point because you are logged out. But you are seeing potentially sensitive information. So imagine that you log in in a cafe, you go through Gmail, or whatever, or your bank, then you log out, you walk away. If you leave that page up and someone hits back, they can now see what was on the page. And part of that particular version, I read a bunch of backstory about that on the Inertia repo because someone posted this as an issue against Inertia as a framework. And the Inertia team...and I really love how they handle these sorts of things. So they were very kind, very welcoming to the issue but also said, “Actually, we're doing...like, this isn't us. But let's talk about it,” and gave a ton of detail and went through the HTTP spec. And it's a fantastic issue as a read. It's like a fun bedtime reading sort of thing to learn about how the internet works. But the Inertia crew really, really cares about being spec-compliant and doing the right thing. So, unfortunately, this is outside of their purview as well. But yeah, I don't have a solution, and it makes me sad. STEPH: I liked that second example that you provided because I feel like I see that one more commonly when I'm on an application, and I don't know why. But I hit back, and then it shows that I'm signed in, and I'm like, that's a lie, I'm not signed in. I also really appreciate how Inertia is responding so kindly and welcoming to folks and then providing such thoughtful responses. That sounds immensely helpful. I don't know, yeah, I am also interested in this. It's something that I haven't worked with in a while, so I don't have any grand ideas at the moment. So I'm also curious if other people have run into this and how they've approached it. CHRIS: Yeah. If we're being honest, partly I wanted to share this with you, but also I wanted to say this into a microphone, and then hopefully someone out there on the internet knows an answer. I've tried, I think, all of the normal things, all of the different variations of headers. I haven't actually poked at the JavaScript things yet, but that's probably the direction I'm going. But if anyone out there has an idea, I would absolutely love it. I think in my mind, the ideal version of this is if I'm making GET requests and I'm clicking around on a page, it's perfectly fine for Chrome to use its cache version of the previous page because, sure, that's fine. It may actually be stale just based on it's been a few seconds, and something's changed on the server, but I'm willing to accept that. But if I've posted, or patched, or deleted, or done any action that by definition should be changing data on the server, then I would love for a way to invalidate Chrome's back cache, so its version of the pages that it's restoring when I'm hitting back. I'd love that as the heuristic to get to. I don't know if I can get there. My sense says chrome’s like, “No, I want to go fast. That's all I care about.” [chuckles] I’m like, all right. Well, I get that vibe but -- STEPH: Yeah, that’s a nice, succinct way to say that if I've changed data, then I want to invalidate that browser cache, so then we don't show them a fresh page and we actually show them the name that they entered on the form. CHRIS: As we know, though, cache invalidation is one of the very easy things to do in software development. So I'm sure my naive, quick idea is very easy to implement and will have no edge cases of its own. STEPH: Well, this will be our parallel Pixar movie. We have one that we highlighted earlier, and this will be the other one, The Cache Buster. I'm not great with titles. [laughs] This will be our other Pixar movie. CHRIS: Buster the Lonely Cache. Yep. STEPH: All right. Well, in parallel, we'll work on Buster the Lonely Cache. Is that the name of this? CHRIS: Yep. STEPH: Cool. We'll work on that script. And in the meantime, I'll also think about it if I encounter this or come up with some ideas and share them with you. And then also if other people have any ideas, that'd be fantastic. CHRIS: That would be fantastic. STEPH: Yes. Please write in to help Buster with the lonely cache, which, wait; I don't get it. Why is it the lonely cache? CHRIS: Because the cache has been busted and evicted. So he's got no friends. There's nothing...There's no data left. I don't know. STEPH: [laughs] CHRIS: I came up with it real quick. I don't stand by it. It's not a great idea, but we'll workshop it. It'll be fine. STEPH: That's true. Yeah, we'll go through it. I'm asking too many questions for a very quick creative. We're in the creative space, not the critical space. But please write in to help Buster figure out [laughs] the lonely cache or how to bust the cache. Oh, goodness. I'm done with my jokes for today. I'll try to stop. CHRIS: I believe that's a perfect note. Shall we wrap up? STEPH: Let's wrap up. Show notes for this episode can be found at bikeshed.fm. CHRIS: This show is produced and edited by Mandy Moore. STEPH: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or a review in iTunes as it helps other people find the show. CHRIS: If you have any feedback for this or any of our other episodes, you can reach us @_bikeshed on Twitter. And I'm @christoomey. STEPH: And I’m @SViccari. CHRIS: Or you can email us at hosts@bikeshed.fm. STEPH: Thanks so much for listening to The Bike Shed, and we'll see you next week. All: Bye. Announcer: This podcast was brought to you by thoughtbot. thoughtbot is your expert design and development partner. Let's make your product and team a success.Support The Bike Shed
undefined
Jun 15, 2021 • 1h 4min

296: Speedy Performance with Nate Berkopec

Nate Berkopec is the author of the Complete Guide to Rails Performance, the creator of the Rails Performance Workshop, and the co-maintainer of Puma. He talks with Steph about being known as "The Rails Speed Guy," and how he ended up with that title, publishing content, working on workshops, and also contributing to open source projects. (You could say he's kind of a busy guy!) Speedshop Puma The Rails Performance Workshop The Complete Guide to Rails Performance How To Use Turbolinks to Make Fast Rails Apps Sidekiq Follow Nate Berkopec on Twitter Visit Nate's Website Sign up for Nate's Speedshop Ruby Performance Newsletter Transcript: STEPH: All right. I'll kick us off with our fancy intro. Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I’m Steph Viccari. And this week, Chris is taking a break. But while he's away, I'm joined by Nate Berkopec, who is the owner of Speedshop, a Ruby on Rails performance consultancy. And, Nate, in addition to running a consultancy, you're the co-maintainer of Puma. You're also an author as you wrote a book called The Complete Guide to Rails Performance. And you run the workshop called The Rails Performance Workshop. So, Nate, I'm sensing a theme here. NATE: Yeah, make code go fast. STEPH: And you've been doing that for quite a while, haven't you? NATE: Yeah. It's pretty much been since 2015, or so I think. It all started when I actually wrote a blog post about Turbolinks that got a lot of pick up. My hot take at the time was that Turbolinks is actually a good thing. That take has since become uncontroversial, but it was quite controversial in 2015. So I got a lot of pick up on that, and I realized I liked working on performance, and people seem to want to hear about it. So I've been in that groove ever since. STEPH: When you started down the path of really focusing on performance, were you running your own consultancy at that point, or were you working for someone else? NATE: I would say it didn't really kick off until I actually published The Complete Guide to Rails Performance. So after that came out, which was, I think, March of 2016…I hope I’m getting that right. It wasn't until after that point when it was like, oh, I'm the Rails performance guy now. And I started getting emails inbound about that. I didn't really have any time when I was actually working on the CGRP to do that sort of thing. I just made that my full-time job to actually write, and market, and publish that. So it wasn't until after that that I was like, oh, I'm a performance consultant now. This is the lane I've driven myself into. I don't think I really had that as a strategy when I was writing the book. I wasn't like, okay, this is what I'm going to do. I'm going to build some reputation around this, and then that'll help me be a better consultant with this. But that's what ended up happening. STEPH: I see. So it sounds like it really started more as a passion and something that you wanted to share. And it has manifested to this point where you are the speed guy. NATE: Yeah, I think you could say that. I think when I started writing about it, I just knew...I liked it. I liked the work of performance. In a lot of ways, performance is a much more concrete discipline than a lot of other sub-disciplines of programming where I joke my job is number go down. It's very measurable, and it's very clear when you've made a difference. You can say, “Hey, this number was this, and now it's this. Look what I did.” And I always loved that concreteness of performance work. It makes it actually a lot more like a real kind of engineering discipline where I think of performance engineering as clarifying requirements and the limitations and then building a project that meets the requirements while staying within those limitations and constraints. And that's often not quite as clear for other disciplines like general feature work. It's kind of hard to say sometimes, like, did you actually make the user's life better by implementing such and such? That's more of a guess. That's more of a less clear relationship. And with performance, nobody's going to wake up ten years from today and wish that their app was slower. So we can argue about the relative importance of performance in an application, but we don't really argue about whether or not we made it faster because we can prove that. STEPH: Yeah. That's one area that working with different teams (as I tend to shift the clients that I'm working with every six months) where we often push hard around feature work to say, “How can we measure this? How can we know that we are delivering something valuable to users?” But as you said, that's really tricky. It's hard to evaluate. And then also, when you add on the fact that if I am leaving that project in six months, then I don't have the same insights to understand how something went for that team. So I can certainly appreciate the satisfaction that comes from knowing that, yes, you are delivering a faster app. And it's very measurable, given the time that you're there, whether it's a short time or if it's a long time that you're with that team. NATE: Yeah, totally. My consulting engagements are often really short. I don't really do a lot of super long-term stuff, and that's usually fine because I can point to stuff and say, “Yep. This thing was at A, and now it's at B. And that's what you hired me to do, so now it's done.” STEPH: I am curious; given that you have so many different facets where you are running your consultancy, you are also often publishing a lot of content and working on workshops and then also contributing to open source projects. What does a typical week look like for you? NATE: Well, right now is actually a decent example. I have client work two or three days a week. And I'm actually working on a new product right now that I'm calling Sidekiq in Practice, which is a course/workshop about scaling Sidekiq from zero to 1000 jobs per second. And I'll spend the other days of the week working on that. My content is...I always struggle with how much time to spend on blogging specifically because it takes so much time for me to come up with a post and publish that. But the newsletter that I write, which I try to write two once a week, I haven't been doing so well with it lately. But I think I got 50 newsletters done in 2020 or something like that. STEPH: Wow. NATE: And so I do okay on the per-week basis. And it's all content I've never published anywhere else. So that actually is like 45 minutes of me sitting down on a Monday and being like rant, [chuckles] slam keyboard and rant and then hit send. And my open source work is mostly 15 minutes a day while I'm drinking morning coffee kind of stuff. So I try to spread myself around and do a lot of different stuff. And a lot of that means, I think, pulling back in terms of thinking how much you need to spend on something, especially with newsletters, email newsletters, it was very easy to overthink that and spend a lot of time revising and whatever. But some newsletter is better than no newsletter. And especially when it comes to content and marketing, I've learned that frequency and regularity is more important than each and every post is the greatest thing that's ever come out since sliced bread. So trying to build a discipline and a practice around doing that regularly is more important for me. STEPH: I like that, some newsletter is better than no newsletter. I was listening to your chat with Brittany Martin on the Ruby on Rails podcast. And you said something very honest that I appreciated where you said, “Writing is really hard, and writing sucks.” And that made me laugh in the moment because even though I do enjoy writing, I still find it very hard to be disciplined, to sit down and make it happen. And then you go into that editor mode where you critique everything, and then you never really get it published because you are constantly fixing it. It sounds like...you've mentioned you set aside about 45 minutes on a Monday, and you crank out some work. How do you work through that inner critic? How do you get past it to the point where then you just publish? NATE: You have to separate the steps. You have to not do editing and first drafting at the same time. And the reason why I say it sucks and it's hard is because I think a lot of people don't do a lot of regular writing, maybe get intimidated when they try to start. And they're like, “Wow, this is really hard. This is not fun.” And I'm just trying to say that's everybody's experience and if it doesn't get any better, because it doesn't, [chuckles] there's nothing wrong with you, that's just writing, it's hard. For me, especially with the newsletter, I just have to give myself permission not to edit and to just hit send when I'm done. I try to do some spell checking,, and that's it. I just let it go. I'm not going back and reading it through again and making sure that I was very clear and cogent in all my points and that there's a really good flow through that newsletter. I think it comes with a little bit of confidence in your own ideas and your own experience and knowledge, believing that that's worth sharing and that's worth somebody's time, even if it's not a perfect expression of what's in your head. Like, a 75% expression is good enough, especially in a newsletter format where it's like 500 to 700 words. And it's something that comes once a week. And maybe not everyone's amazing, but some of them are, enough of them are that people stay subscribed. So I think a combination of separating editing and first drafting and just having enough confidence and the basis where you have to say, “It doesn't have to be perfect every single time.” STEPH: Yeah, I think that's something that I learned a while back to apply to my coding process where I had to separate those two steps of where I have to let the creator in me just create and write some code and make it work, and then come back to the editing process, and taking a similar approach with writing. As you may be familiar with thoughtbot, we're big advocates when it comes to sharing content and sharing things that we have learned throughout the week and different projects that we're working on. And often when people join thoughtbot, they're very excited to contribute to the blog. But it is daunting for that first post because you think it has to be this really grand novel. And it has to be something that is really going to appeal to everybody, and it's going to help everyone. And then over time, you learn it's like, oh well, actually it can be this very just small thing that I learned that maybe only helps 20 people, but it still helped those 20 people. And learning to publish more frequently versus going for those grand pieces is more favorable and often more helpful for people. NATE: Yeah, totally. That's something that is difficult for people at first. But everything in my experience has led me to believe that frequency and regularity is just as, if not more important than the quality of any individual piece of content that I put out. So that's not to say that...I guess it's weird advice to give because people will take it too far the other way and think that means he's saying quality doesn't matter. No, of course, it does, but I think just everyone's internal biases are just way too tuned towards this thing must be perfect. I've also learned we're just really bad judges internally of what is useful and good for people. Stuff that I think is amazing and really interesting sometimes I'll put that out, and nobody cares. [chuckles] And the other stuff I put out that's just like the 45-minute banging out newsletter, people email me back and say, “This is the most helpful thing anyone’s ever read.” So that quality bias also assumes that you know what is good and actually we're not really good at that, knowing every time what our audience needs is actually really difficult. STEPH: That's totally fair. And I have definitely run into that too, where I have something that I'm very proud of and excited to share, and I realize it relates to a very small group of people. But then there's something small that I do every day, and then I just happen to tweet about it or talk about it, and suddenly that's the thing that everybody's really excited about. So yeah, you never know. So share it all. NATE: Yeah. And it's important to listen. I pay attention to what people get interested in from what I put out, and I will do more of that in the future. STEPH: You mentioned earlier that you are working on another workshop focused on Sidekiq. What can you tell me about that? NATE: So it's meant to be a guide to scaling Sidekiq from zero to 1000 requests per second. And it's meant to be a missing guide to all the things that happen, like the situations that can crop up operationally when you're working on an application that does a lot of work with Sidekiq. Whereas Mike Sidekiq, Wiki, or the docs are great about how do, you do this? What does this setting mean? And the basics of getting it just running, Sidekiq in practice, is meant to be the last half of that. How do you get it to run 1,000 jobs per second in a day-to-day application? So it's the collected wisdom and collected battle scars from five years of getting called in to fix people's Sidekiq installations and very much a product of what are the actual problems that people experience, and how do you fix and deal with those? So stuff about memory and managing Sidekiq memory usage, how to think about queues. Like, what should your queue structure be? How many should you have? Like, how do you organize jobs into queues, and how do you deal with problems like some client is dropping 10,000, 20,000 jobs into a queue. And now the other jobs I put in that queue have 20,000 jobs in front of them. And now this other job I've got will take three hours to get through that queue. How do you deal with problems like that? All the stuff that people have come to me over the years and that I've had to help them fix. STEPH: That sounds really great. Because yeah, I find that teams who are often in this space with Sidekiq we just let it run until there's a fire. And then suddenly, we start to care as to how it's processing, and we care about our queue structure and how many workers that we have that are pulling from that queue. So that sounds really helpful. When you're building a workshop, do you often go back to any of those customers and pull more ideas from them, or do you find that you just have enough examples from your collective work with clients that that itself creates a course? NATE: Usually, pretty much every chapter in the workshop I've probably implemented like three-plus times, so I don't really have to go back to any individual customer. I have had some interesting stuff with my current client, Gusto. And Gusto is going through some background job reorganization right now and actually started to implement a lot of the things that I'm advocating in the workshop actually without talking to me. It was a good validation of hey, we all actually think the same here. And a lot of the solutions that they were implementing were things that I was ready to put down into those workshops. So I'd like to see those solutions implemented and succeed. So I think a lot of the stuff in here has been pretty battle-tested. STEPH: For the Rails Performance Workshop, you started off doing those live and in-person with teams, and then you have since switched to now it is a CLI course, correct? NATE: That's correct. Yep. STEPH: I love that very much. When you’ve talked about it, it does feel very appropriate in terms of developers and how we like to consume content and learn. So that is really novel and also, it seems like a really nice win for you. So then other people can take this course, but you are no longer the individual that has to deliver it to their team, that they can independently take the course and go through it on their own. Are you thinking about doing the same thing for the Sidekiq course, or what are your plans for that one? NATE: Yeah, it's the exact same structure. So it's going to be delivered via the command line. Although I would say Sidekiq in practice has more text components. So it's going to be a combination of a very short manual or book, and some video, and some hands-on exercises. So, an equal blend between all three of those components. And it's a lot of stuff that I've learned over having to teach; I guess intermediate to advanced programming concepts for the last five years now that people learn at different paces. And one of the great things about this kind of format is you can pick it up, drop it off, and move at your own speed. Whereas a lot of times when I would do this in person, I think I would lose people halfway through because they would get stuck on something that I couldn't go back to because we only had four hours of the day. And if you deliver it in a class format, you're one person, and I've got 24 other people in this room. So it's infinitely pausable and replayable, and you can go back, or you can just skip ahead. If you've got a particular problem and you're like, hey, I just want to figure out how to fix such and such; you can do that. You can just come in and do a particular thing and then leave, and that's fine. So it's a good format that way. And I've definitely learned a lot from switching to pre-recorded and pre-prepared stuff rather than trying to do this all live in person. STEPH: That is one of the lessons that I've learned as well from the couple of workshops that I've led is that doing them in person, there's a lot of energy. And I really enjoy that part where I get to see people respond to the content. And then I get a lot of great feedback from people about what type of questions they have, where they are getting stuck. And that part is so important to me that I always love doing them live first. But then you get to the point, as you'd mentioned, where if you have a room full of 20 people and you have two people that are stuck, how do you help them but then still keep the class going forward? And then, if you are trying to tailor this content for a wide audience…so maybe beginners could take the Rails Performance Workshop, or they could also take the Sidekiq course. But you also want the more senior engineers to get something out of it as well. It's a very challenging task to make that content scale for everyone. NATE: Yeah. What you said there about getting feedback and learning was definitely something that I got out of doing the Rails Performance Workshop in person like three dozen times, was the ability to look over people's shoulders and see where they got stuck. Because people won't email me and say, “Hey, this thing is really confusing.” Or “It doesn't work the way you said it does for me.” But when I'm in the same room with them, I can look over their shoulder and be like, “Hey, you're stuck here.” People will not ask questions. And you can get past that in an in-person environment. Or there are even certain questions people will ask in person, but they won't take the time to sit down and email me about. So I definitely don't regret doing it in person for so long because I think I learned a lot about how to teach the material and what was important and how people...what were the problems that people would encounter and stuff like that. So that was useful. And definitely, the Rails Performance Workshop would not be in the place that it is today if I hadn't done that. STEPH: Yeah, helping people feel comfortable asking questions is incredibly hard and something I've gone so far in the past where I've created an anonymous way for people to submit questions. So during class, even if you didn't want to ask a question in front of everybody, you could submit a question to this forum, and I would get notified. I could bring it up, and we could answer it together. And even taking that strategy, I found that people wouldn't ask questions. And I guess it circles back to that inner critic that we have that's also preventing us from sharing knowledge that we have with the world because we're always judging what we're going to share and what we're going to ask in front of our peers who we respect. So I can certainly relate to being able to look over someone's shoulder and say, “Hey, I think you're stuck. We should talk. Let me walk you through this or help you out.” NATE: There are also weird dynamics around in-person, not necessarily in a small group setting. But I think one thing I really picked up on and learned from RailsConf2021 which was done online, was that in-person question asking requires a certain amount of confidence and bravado that you're not...People are worried about looking stupid, and they won't ask things in a public or semi-public setting that they think might make them look dumb. And so then the people that do end up asking questions are sometimes overconfident. They don't even ask a question. They just want to show off how smart they are about a particular issue. This is more of an issue at conferences. But the quality of questions that I got in the Q&A after RailsConf this year (They did it as Discord chats.) was way better. The quality of questions and discussion after my RailsConf talk was miles better than I've ever had at a conference before. Like, not even close. So I think experimenting with different formats around interaction is really good and interesting. Because it's clear there's no perfect format for everybody and experimenting with these different settings and different methods of delivery has been very useful to me. STEPH: Yeah, that makes a ton of sense. And I'm really glad then for those opportunities where we're discovering that certain forums will help us get more feedback and questions from people because then we can incorporate that and to future conferences where people can speak up and ask questions, and not necessarily be the one that's very confident and enjoys hearing their own voice. For the Rails Performance Workshop, what are some of the general things that you dive into for that workshop? I'm curious, what is it like to attend that workshop? Although I guess one can't attend it anymore. But what is it like to take that workshop? NATE: Well, you still can attend it in some sense because I do corporate bookings for it. So if you want to buy 20 seats, then I can come in and basically do a Q&A every week while everybody takes the workshop. Anyway, I still do that. I have one coming up in July, actually. But my overall approach to performance is to always start with monitoring. So the course starts with goals and monitoring and understanding where you want to go and where you are when it comes to performance. So the first module of the Rails Performance Workshop is actually really a group exercise that's about what are our performance requirements and how can we set those? Both high-level and low-levels. So what is our goal for page load time? How are we going to measure that? How are we going to use that to back into lower-level metrics? What is our goal for back-end response times? What is our goal for JavaScript bundle sizes? That all flows from a higher-level metric of how fast you want the page to load or how fast you want a route to change in a React app or something, and it talks about those goals. And then where should you even start with where those numbers should be? And then how are you going to measure it? What are the browser events that matter here? What tools are available to help you to get that data? Because without measurement, you don't really have a performance practice. You just have people guessing at what stuff is faster and what is not. And I teach performance as a scientific process as science and engineering. And so, in the scientific method, we have hypotheses. We test those hypotheses, and then we learn based on those tests of our hypotheses. So that requires us to A, have a hypothesis, so like, I think that doing X makes this faster. And I talk about how you generate hypotheses using profiling, using tools that will show you where all the time goes when you do this particular operation of your software—and then measuring what happens when you do that? And that's benchmarking. So if you think that getting rid of method X or changing method X will speed up the app, benchmarking tells you did you actually speed it up or not? And there are all sorts of little finer points to making sure that that hypothesis and that experiment is tested in a valid way. I spend a lot of time in the workshop yapping about the differences between development/local environments and production environments and which ones matter. Because what differences matter, it's not often the ones that we think about, but instead it's differences like actually in Rails apps the asset packaging and asset pipeline performs very differently in production than it does in development, works very differently. And it makes it one of the primary reasons development is slower than production, so making sure that we understand how to change those settings to more production-like settings. I talk a lot about data. It’s the other primary difference between development and production is production has a million users, and development has 10. So when you call things like User.all, that behavior is very different in production than it is locally. So having decent production-like data is another big one that I like to harp on in the workshops. So it's a process in the workshop of you just go lesson by lesson. And it's a lot of video followed up by hands-on exercises that half of them are pre-baked problems where I'm like, hey, take a look at this Turbolinks app that I've given you and look at it in DevTools. And here's what you should see. And then the other half is like, go work on your application. And here are some pull requests I think you should probably go try on your app. So it's a combination of hands-on and videos of the actual experience going through it. STEPH: I love how you start with a smaller application that everyone can look at and then start to learn how performant is this particular application that I'm looking at? Versus trying to assess, let’s say, their own application where there may be a number of other variables that they have to consider. That sounds really nice. You'd mentioned one of the first exercises is talking about setting some of those goals and perhaps some of those benchmarks that you want to meet in terms of how fast should this page load, or how quickly should a response from the API be? Do you have a certain set of numbers for those benchmarks, or is it something that is different for each product? NATE: Well, to some extent, Google has suddenly given us numbers to work with. So as of this month, I think, June 2021, Google has started to use what they're calling Core Web Vitals in their ranking of search results. They've always tried to say it's not a huge ranking factor, et cetera, et cetera, but it does exist. It is being used. And that data is based on Chrome user telemetry. So every time you go to a website in Chrome, it measures three metrics and sends those back to Google. And those three metrics are Largest Contentful Paint (LCP), First Input Delay (FID), and Cumulative Layout Shift (CLS). And First Input Delay and Cumulative Layout shift are more important for your single-page apps kind of stuff. It's hard to screw those up with a Golden Path Rails app that just does Turbolinks or Hotwire or whatever. But Largest Contentful Paint is an easy one to screw up. So Google's line in the sand that they've drawn is 2.5 seconds for Largest Contentful Paint. So that's saying that from clicking on your website in a Google search result, it should take 2.5 seconds for the page to paint the largest component of that new page. That's often an image or a video or a large H1 tag or something like that. And that process then will help you to...to get to 2.5 seconds in Largest Contentful Paint; there are things that have to happen along the way. We have to download and execute all JavaScript. We have to download CSS. We have to send and receive back-end responses. In the case of a simple Hotwire app, it's one back-end response. But in the case of a single-page app, you got to download the document and then maybe download several XHR fetches or whatever. So there's a chain of events that has to happen there. And you have to walk that back now from 2.5 seconds in Largest Contentful Paint. So that's the line that I'm seeing getting drawn in the sand right now with Google's Core Web Vitals. So pretty much any meaningful web application performance metric can be walked back from that. STEPH: Okay. That's super helpful. I wasn't aware of the Core Web Vitals and that particular stat that Google is using to then rank the sites. I was going to ask, this kind of blends in nicely into when do you start caring about performance? So if you have a new application that you are just starting to get to market, based on the fact that Google is going to start ranking you right away, you do have to care some right out of the gate. But I am curious, when do you start caring more about performance, and are there certain tools and benchmarking that you want to have in place from day one versus other things that you'll say, “Well, we can wait until we have X numbers of users or other conditions before we add more profiling?” NATE: I'd say as an approach, I teach people not to have a performance strategy of monitoring. So if your strategy is to have dashboards and look at them regularly, you're going to lose. Eventually, you're not going to look at that dashboard, or more often, you just don't understand what you're looking at. You just install New Relic or Datadog or whatever, and you don't know how to turn a dashboard into actual action. Also, it seems to just wear teams out, and there's no clear mechanism when you just have a dashboard of turning that into oh, well, this has to now be something that somebody on our team has to go work on. Contrast that with bugs, so teams usually have very defined processes around bugs. So usually, what happens is you'll get an Exception Notification through Sentry or Bugsnag or whatever your preferred Exception Notification service is. That gets read by a developer. And then you turn that into a Jira ticket or a Kanban board or whatever. And then that is where work is done and prioritized. Contrast that with performance; there’s often no clear mechanism for turning metrics into stuff that people actually work on. So understanding at your organization how that's going to work and setting up a process that automatically will turn performance issues into actual work that people get done is important. The way that I generally teach people to do this is to focus instead of dashboards and monitoring, on alerts, on automated thresholds that get tripped and then sends somebody's an email or put something in the Kanban board or whatever. It just has to be something that automatically gets fired. Different tools have different ways of doing this. Datadog has pretty much built their entire product around monitoring and what they call monitors. That's a perfectly fine way to do it, whatever your chosen performance monitoring tool, which I would say is a required thing. I don't think there's really any good excuse in 2021 for not having a performance monitoring tool. There are a million different ways to slice it. You can do it yourself with OpenTelemetry and then like statsD, I don't know, or pay someone else like everyone else does for Datadog or New Relic or AppSignal or whatever. But you got to have one installed. And then I would say you have to have some sort of automated alerting. Now that alerting means that you've also decided on thresholds. And that's the hard work that doesn't get done when your strategy is just monitoring. So it's very easy to just install a dashboard and say, “Hey, I have this average page time load dashboard. That means I'm paying attention to performance.” But if you don't have a clear answer to what number is good and what number is bad, then that dashboard cannot be turned into real action. So that's why I push monitoring so hard is because it allows people to ignore performance is all that matters, and it forces you to make the decision upfront as to what number matters. So that is what I would say, install some kind of performance monitoring. I don't really care what kind. Nowadays, I also think there's probably no excuse to not have Real User Monitoring. So there's enough GDPR compliance Real User Monitoring now that I think everyone should be using it. So for industry terms, Real User Monitoring is just performance monitoring in the browser. So it's just users’ browser APIs and sends those back to you or your third-party provider, so having that so you actually are collecting back-end and front-end performance metrics. And then making decisions around what is bad and what is good. Probably everybody should just start with a page load time monitor, Largest Contentful Paint monitor. And if you've got a single-page app, probably hooking up some stuff around route changes or whatever your app...because you don't actually have page loads on every single time you navigate. You have to instrument whatever those interactions are. So having those up and then just drawing some lines that say, “Hey, we want our React route changes to always be one second or less.” So I will set an alert that if the 95th percentile is one second or more, I'm going to get alerted. There's a lot of different ways to do that, and everybody will have different needs there. But having a handful of automated monitors is probably a place to start. STEPH: I like how you also focus on once you have decided those thresholds and have that monitoring in place, but then how do you make it actionable? Because I have certainly been part of teams where we get those alerts, but we don't necessarily...what you just mentioned, prioritize that work to get done until we have perhaps a user complaint about it. Or we start actually having pages that are timing out and not loading, and then they get bumped up in the priority queue. So I really like that idea that if we agree upon those thresholds and then we get alerted, we treat that alert as if it is a user that is letting us know that a page is too slow and that they are unable to use our application, so then we can prioritize that work. NATE: And it's not all that dissimilar to bugs, really. And I think most teams have processes around correctness issues. And so, all that my strategy is really advocating for is to make performance fail loudly in the same way that most exceptions do. [chuckles] Once you get to that point, I think a lot of teams have processes around prioritization for bugs versus features and all that. And just getting performance into that conversation at least tends to make that solve itself. STEPH: I'm curious, as you're joining teams and helping them with their performance issues, are there particular buckets or categories of performance issues that are the most common in terms of, let's say, 50% of issues are SQL-related N+1 issues? What tends to be the breakdown that you see? NATE: So, when it comes to why something is slow in a Ruby application, I teach a method that I call DRM. And that doesn't have anything to do with actual DRM. It's just memorable because it reminds me of things I don't like. DRM stands for Database Ruby and Memory and in that order. So the most common issue is database, the second most common issue is issues with your Ruby code. The least common issue is memory. Specifically, I'm talking about allocation of objects, creating lots of objects. So probably 80% of your issues are in some way database-related. In Rails, it's 50% of those are probably N+1. And then 30% of database issues are probably what I would call unnecessary SQL. So it's not necessarily N+1, but it's a SQL query for information that you already had, or you could do in a more efficient way. So a common thing for unnecessary SQL would be people will filter an ActiveRecord::Collection like ten different ways when they could have just loaded the whole collection, filtered it with Ruby in the ten different ways afterwards, and that works really well if the collection that you're loading is like 10, 20. Turning that into one database query, plus a bunch of calls to innumerable methods is often way faster than doing that as ten separate database queries. Also, that tends to be a more robust approach. This doesn't happen in most companies, but what could happen is the database is like a shared resource. It's a resource that everybody is affected by. So a performance degradation to the database is the worst possible scenario because everything is affected. But if you screw up what's happening at an individual Rails process, then only that Rails process is affected. The blast radius is tiny. It's just that one request. So doing less stuff in the database while it can actually seem like, oh, that doesn't feel right. I'm supposed to do a lot of stuff in the database. It actually can reduce blast radiuses of performance issues because you're not doing it on this database that everyone has to have access to. There are a lot of areas of gray here. And I talk a lot in all my other material like why -- There's a lot of nuance here. So database is the main stuff. Issues in how you write your Ruby code is probably the other one. Usually, that's just what I would call code that goes bump in the night. It's code that you don't know is running but actually is. Profilers are what help us figure that out. So oftentimes, I'll have someone open up a profiler on their controller action for the first time. And they're like, wait a minute, I had no idea that such and such was running during this controller action, and actually, we don't need to do that at all. So why is it here? So that's the second most common issue. And then the third issue that really doesn't actually come up all that often is object allocation, numbers of objects that get created. So primarily, this is a problem in index actions or actions transactions that deal with big collections. So in Ruby, we often get overly focused on garbage collection, but garbage collection doesn't take any time if you just don't create objects. And object creation itself takes time. So looking at code through the lens of what object does this code create? And trying to get rid of those object allocations can often be a pretty productive way to make stuff faster. STEPH: You said a lot of amazing things there. So I'm debating on which one to follow up on. I think the one that stuck out to me the most where I have felt pain around this is you mentioned identifying code that goes bump in the night or code that is running, but it doesn't need to be run. And that is something that I've run into with applications where we have a code path that seems important, but yet I can't prove that it's being executed and exactly why it's there and what flow it's supporting. And I'm curious, do you have any tips or tricks in how you’ve helped teams identify that this code path isn't used and it's something that we can remove and then that itself will help speed up the performance of that particular endpoint? NATE: Like, there's no performance cost to like 100 models in an application that never actually get used. There's really no performance downside to code in an app that doesn't actually ever get run. But instead, what happens is code gets added into callbacks that usually is probably the biggest offender that’s like, always do this thing after you do X. But then, two years later, you don't always need to do that thing after you do X. So the callbacks always run, but sometimes requirements change, and they don't always need to be run. So usually, it's enough to just pop the profiler now on something. And I have people look at it, and they're like, “I don't know why any of this is happening.” Like, it's usually a pretty big Eureka moment once we look at a flame graph for the first time and people understand how to read those, and they understand what they're looking at. But sometimes there's a bit of a process where especially in a bigger app where it's like, “Such and such is running, and this was an entire other team that's working on this. I have no idea what this even does.” So on bigger apps, there's going to be more learning that has to get done there. You have to learn about other parts of the application that maybe you've never learned about before. But profiling helps us to not only see what code is running but also what that relative importance is. Like, okay, maybe this one callback runs, and you don't know what it does, and it's probably unnecessary. But if it only takes 1% of the total time to run this action, that's probably less important than something that takes 20% of total time. And so profilers help us to not only just see all the code that's being run but also to know where that time goes and what time corresponds to what parts of the code. STEPH: Yeah, that's often the code that makes me the most nervous is where it's code that I suspect is being run or maybe being run, but I don't understand why it's there and then figuring out if it can be removed and then figuring out ways to perhaps even log when a call is being made to that code to determine if it's truly in use or not or at least supported by a code path that a user is hitting. You have a blog post that I read recently that I really appreciated that talks about essentially gaming benchmarking where you talk about the importance of having context around benchmarks. So if someone says, “I've improved something where it is now 10% faster.” It's like, well, what is that 10% relative to? And if it's a tool that other people are using, what does that mean for them? Or did you improve something that was already very fast, and you made it 10% faster? Was that a really valuable use of your time? NATE: Yeah. You know, something that I read recently that made me think of that again was this Hacker News post that went viral. That was like, how I optimize an AWS EC2 instance to take 1.5 million requests per second on my JSON API. And out of the box, it was like 500 requests per second, and then he got it to 1.5 million. And the whole article was presented with relative numbers. So it was like, “I made this change, and things got 33% faster. And if you do the whole thing right, 500 to 1.5 million requests per second, it's like my app is three times faster now,” or whatever. And that's true, but it would probably be more accurate to say, “I've taken three-millionth of a second out of every request in my app.” That's two ways of saying the same thing because latency and throughput are just related that way. But it's probably more accurate and more useful to say the absolute number, but it doesn't make for great blog posts, so that doesn't tend to get said. The kinds of improvements that were discussed in this article were really, really low-level stuff. That was like if you turn off...I think it was like turn off iptables or something like that. And it's like, that shaves a microsecond off of every time we make a syscall or something. And that is useful if your performance goal is to serve 1.5 million requests per second Hello World responses off of my EC2 instance, which is what this person admittedly was doing. But there's a tendency to walk that back to if I do all things in this article, my application will be three times faster. And that's just not what the evidence says. It's not what you were told. So there's just a tendency to use relative numbers when absolute numbers would be more useful to giving you the context of like, oh, well, this will improve my app or it won't. We get this a lot in Puma. We get benchmarks that are like, hey, this thing is going to help us to do 50,000 requests per second in Puma instead of 10,000. And another way of saying that is you took a couple of nanoseconds off of the overhead of every single request to Puma. And most Puma applications have a hundred millisecond response time. So it's like, yeah, I guess it's cool that you took a nanosecond off, and I’m sure it's going to help us have cool benchmarks, but none of our users are going to care. No one that's used Puma is going to care that their requests are one nanosecond faster now. So what did we really gain here? STEPH: Yeah, it makes sense that people would want to share those more...I want to call them sparkly stats and something that catches your attention, but they're not necessarily something that's going to translate to us in the way that we hoped that they will in terms of it's not going to speed up our app 30% or have those same rewards or benefits. Speaking of Puma, how is it being a co-maintainer of Puma? And how do you balance that role with all of your other work? NATE: Actually, it doesn't take all that much of my time. I try to spend about 15 minutes a day on it. And that's really possible because of the philosophy I have around open-source maintenance. I think that open source projects are fundamentally about collaboration and about sharing our hard-fought extractions and fixes and knowledge together. And it's not about a single super contributor or super maintainer who is just out of the goodness of their heart releasing all of their incredible work and time into the public domain or into a free software license. Puma is a pretty popular piece of Ruby software, so a lot of people use it. And I have things on my back burner of if I ever got 20 hours to work on Puma, here’s stuff I would do. But there are a lot of other people that have more time than me to work on Puma. And they're just as smart, and they have other tools they've got in their locker that I don't have. And I realized that it was more important that I actually find ways to recruit and then unblock those people than it was for me to devote as much time as I could to Puma. And so my work on Puma now is really just more like management than anything else. It's more trying to recruit new contributors and trying to give them what they need to help Puma. And contributing to open source is a really fraught experience for a lot of people, especially their first time. And I think we should also be really conscious of that. Like, 95% of software developers have really never contributed to open source in a meaningful way. And that's a huge talent pool of people that could be helping us that aren't. So I'm less concerned about the problems of the 5% that are currently contributing than I am about why there are 95% of us that don't do anything. So that's what gets me excited to work on Puma now, is trying to change that ratio. STEPH: I really like that mindset of where you are there to provide guidance but then essentially help unblock others as they're making contributions to the project but then still be there to have the history and full context and also provide a path forward of a good direction for Puma to head. In regards to encouraging more people to contribute to open source projects, I've often heard people say how challenging that is, where they have an open-source project that they would really love people to contribute to but finding people is really hard or just letting people know that they're interested in contributions. Have you had any strategies that have been successful for you in encouraging people to contribute? NATE: Yeah. So first thing, the easiest thing is we have a contributing.md file. So that's something I think more projects should adopt is have an actual file in your project that says everything about how to contribute. Like, what kinds of contributions do you want? Different projects have different things that they want. Like, Rails doesn't want to refactor PRs. Don't send a refactor PR to Rails because they'll reject it. Puma, I'm happy to accept those. So letting people know like, “Hey, here's how we work here. Here's the community we're creating, and here's how it works. Here's how to get involved.” And I think of it as hanging out the shingle and saying, “Yes, I want your contributions. Here's how to do it.” That alone puts you a step above other projects. The second thing I would say is you need to have contributor-only communication channels. So we have Matrix chat. So Matrix is like this successor to IRC. So we have a chat channel basically, but it's like contributors only. I don't enforce that, but I just don't want support requests in there. I don't want people coming in there and being like, “My Puma config doesn't work.” And instead, it's just for people that want to contribute to Puma, and that want to help out. If you have a question and come in there, anyone can answer it. And then finally, another thing that I've had success with is doing one-on-one stuff. So I will actually...I have a Calendly invite that I think is in contributing.md now that you can just book 30 minutes with me anytime about contributing to Puma. And I will get on a Zoom call with you and talk to you about what are your concerns? Where do I think you can help? And I give my time away that way. The way I see it is like if I do that 20 times and I create one super contributor to Puma, that is worth more than me spending 10 hours on Puma because that person can contribute 100, 200, 1,000 hours over their lifetime of contributing to Puma. So that's actually a much more higher leverage contribution, really from my perspective. It's actually helping other people contribute more. STEPH: Yeah, that's huge to offer people to say, “Hey, you can book time with me, and I will walk you through and let you know where you can start making an impactful contribution right away,” or “Here are some areas that I think you'd be interested, to begin with.” That seems like such a nice onboarding for someone who says, “I'm interested, but I'm nervous,” or “I'm just not sure about where to get started.” Also, I love your complaint department voice for the person who their Puma config doesn't work. That was delightful. [chuckles] NATE: I think it's a little bit part of my open-source philosophy that, especially at a certain scale like Puma is at that we really kind of over-prioritize users. And I'm not really here to do support; I'm here to make the project better. And users don't actually contribute to open source projects. Users use the thing, and that's great. That's the whole reason we're open-sourcing is so more people use it. But it's important not to prioritize that over people who want to make the project better. And I think a lot of times; people get caught up in this almost clout chasing of getting the most GitHub stars that they think they need and users they think they need. And you don't get paid for having users, and the product doesn't get any better either. So I don't prioritize users. I prioritize the quality of the project and getting contributors. And that will create a better project, which will then create more users. So I think it's easy to get sidetracked by people that ask for your time when they're not giving anything back to the project in return. And especially at Puma’s scale, we have enough people that want my time or the time of other maintainers at Puma so that they can contribute to the project. And putting user support requests ahead of that is not good for the project. It's not the biggest, long-term value increase we could be making, so I don't prioritize them anymore. STEPH: Yep. That sounds like more the pursuit of sparkly stats and looking for all those GitHub stars or all of those likes. Well, Nate, if you're game, I have two listener questions that I'd like to run by you because I shared with some folks that you are going to be on The Bike Shed today. And they're very excited and have two questions that they'd like me to run by you. How does that sound? NATE: Yeah, all right. STEPH: So the first question is, are there any paradigms or trends in Rails that inherently hurt performance? NATE: Yeah. I get this question a lot, and I will preface it with saying that I'm the performance guy, and I'm not the software design guy. And I get a lot of questions about does such and such software design...how does that impact performance? And usually, there's like a way to do anything in a performant way. And I'm just here to help people to find the performant way and not to prescribe “You must always do X, Y, or Z,” or “ActiveRecord is bad. Never use it.” That's not my job here. And in my experience, there's a fast way to do almost anything. Now, one thing that I think is dying, I guess, or one approach or one common...I don't know what to call it. One common mistake that is clearly wrong is to not do any form of server-side rendering in a web application. So I am anti-client-side app. But there are ways to do that and to do it quickly. But rendering a basically blank document, which is what most of these applications will do when they're using Rails as a back-end…you'll serve this basically blank document or a document with maybe some Chrome in it. And then, the client-side app has to execute, compile JavaScript, make XHR requests, and then render the page. That is just by definition slower than serving somebody a server-side rendered page. Now, I am 100% agnostic on how you want to generate that server-side rendering. There are some people that are working on better ways to do that with Rails and client-side apps. Or you could just go the Hotwire Turbolinks way. And it's more progressive enhancement where the back-end is always just serving the server-side rendered response. And then you do some JavaScript on top of that. So I think five years from now, nobody will be doing this approach of serving blank documents and then booting client-side apps into that. Or at least it will be seen as outdated enough that you should never design a project that way anymore. It's one of those few things where it's like, yeah, just by definition, you're adding more steps into a rendering flow. That means, by necessity, it has to be slower. So I think everybody should be thinking about server-side rendering in their project. Again, I’m totally agnostic on how you want to implement that. With React, whatever front-end flavor of the month you want to go with, there's plenty of ways to do that, but I just think you have to be prioritizing that now. STEPH: All right. Well, I like that five-year projection of where we're headed. I have found that it's often the admin-side where people will still bring in a lot of JavaScript rendering, just to touch on a bit of what you're saying, in terms of let's favor the server-rendered HTML versus over-optimizing a space that one, probably isn't a profitable space in terms that we do want our admins to have a great experience for our product. But if they are not necessarily our users, then it also doesn't need to be anything that is over the top or fancy or probably uses a lot of JavaScript. And instead, we can start simple. And there's a number of times that I've been on projects where we have often walked the admin back to be more server-rendered because we got to a point where someone was very excited to make the admin very splashy and quick but then couldn't keep up with the requests because then they were having to prioritize the user experience first. So it was almost like optimizing the admin, but then it got left out in the cold. So then it's just sort of this poor experience. NATE: Yes. Shopify famously walked back their admin from I think it was Backbone to Turbolinks. And I think that that has now moved back to React is my understanding. But Shopify is a huge company, so they have plenty of time and resources to be able to do that. But I just remember that happening at the time where I was like, oh wow, they just rolled the whole thing back to Turbolinks again. And now, with the consolidation that's gone on in the React world, it's a little bit easier to pipe a server-side rendering into a React app. Whereas with Backbone, it was like no one knew what you were doing. So there was less knowledge about how to server-side render this stuff. Now it doesn't seem to be so much of a problem. But yeah, I mean, Rails is really good at CRUD apps, and admin is like 99% CRUD. And adhering as closely as possible to the Rails Golden Path there in an admin seems to be the most productive way to work on that kind of feature. STEPH: All right. Ready for your second question? NATE: Yes. STEPH: Okay. This one's a bit more in-depth. They also mentioned a particular project name. So I am going to swap it out with a different name. So on project cinnamon roll, we found a really gnarly time-consuming API endpoint that's getting hammered. And on a first pass, we addressed a couple of N+1 issues and tuned the performance, and felt pretty confident that they had addressed the issue. But it was still fairly slow. So then they took some additional incremental steps. So they swapped out to use OJ for serialization that shaved off an additional 10% but was still slow. They also went the route of going straight to Rails cache with a one-minute expiration. So that way, they could avoid mucking with cache busting because they confirmed with the client that data could be slightly stale. And this was great. It worked out well. So it dropped their average response time down to less than 70 milliseconds. With all that said, that journey took a few hours over a few days, and multiple production deploys. And had they gone straight to the cache, then they would have had a 15-minute fix with a single deploy. So this person's wondering, are there any other examples like that where, rather than taking these incremental seemingly obvious performance whims, there are situations where you want to be much more direct with your path? NATE: I guess I'd say that profiling can help you to understand and form better hypotheses about what will make things faster and what won't. Because a profiler can't really lie to you about where time goes, either you spent 20% of your time in this method, or you didn't. So I don't spend any time in any of my material talking about what JSON serializer you use. Because really that's actually never...that's really never anybody's bottleneck. It's never a huge proportion of people's total percentage of time. And I know that because I've looked at enough profiles that the issues are usually in other places. So I would say that if your hypotheses that you're generating are not working, it's because you're not generating good enough hypotheses. And profiling is the place to do that. So having profilers running in production is probably the biggest level-upscale-like that most teams could take. So having profilers that you can access as on production servers as a user is probably the biggest level up that you could make to generating the hypotheses because that'll have real production data, real production servers, real production environment. And it's pretty common now that pretty much every team that I work with either has that already, or we work on implementing it. It's something that I've seen in production at GitHub and Shopify. You can do it yourself with rack-mini-profiler. It's all about setting up the authorization, just making sure that only authorized users get to see every single SQL query generated in the flame graph and all that. But other than that, there's no reason you shouldn't do it. So I would say that if you're not generating the right hypotheses or you don't...if the last hypothesis out of 10 is the one that works, you need better hypotheses, and the best way to do that is better profiling. STEPH: Okay, better profiling. And yeah, it sounds like there's also a bit of experience in there in terms of things that you're used to seeing, that you've noticed that could be outliers in terms of that they're not necessarily the thing that you want to improve. Like you mentioned spending time on how you're serializing your JSON is not somewhere that you would look. But then there are other areas that you've gained experience that you know would be likely more beneficial to then focus on to form that hypothesis. NATE: Yeah, that's a long way of saying experience pays off. I've had six years of doing this every single day. So I'm going to be pretty good at...that's what I get paid for. [laughs] So if I wasn't very good at that, I probably wouldn't be making any money at it. STEPH: [laughs] All right. Well, thanks, Nate, so much for coming on the show today and talking so much about performance. On that note, I think it's a good place for us to wrap up. If people are interested in following along with what you're working on and they want to keep up with your latest and greatest workshops that are coming out, where can they find you on the internet? NATE: speedshop.co is my site. @nateberkopec on Twitter. And speedshop.co has a link to my newsletter, which is where I'm actively thinking every week and publishing stuff too. So if you want to get the drip of news and thoughts, that's probably the best place to go. STEPH: Perfect. All right. Well, thank you so much. NATE: No problem. STEPH: The show notes for this episode can be found at bikeshed.fm. CHRIS: This show is produced and edited by Mandy Moore. STEPH: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or a review in iTunes as it helps other people find the show. CHRIS: If you have any feedback for this or any of our other episodes, you can reach us @bikeshed on Twitter. And I'm @christoomey. STEPH: And I’m @SViccari. CHRIS: Or you can email us at hosts@bikeshed.fm. STEPH: Thanks so much for listening to The Bike Shed, and we'll see you next week. Together: Bye. Announcer: This podcast was brought to you by thoughtbot. thoughtbot is your expert design and development partner. Let's make your product and team a success.Support The Bike Shed
undefined
Jun 7, 2021 • 36min

295: To the Left, to the Left

After the last episode where database switching was discussed, a number of listeners reached out with thoughts. In particular, one listener gave a reproducible example of how to make things better. Chris talks about why he always moves errors to the left, and Steph gives a hot take where she admits that she is not a fan of hackathons and explains why. Steph and Chris also share exciting Bike Shed show news in that we now have transcripts for each episode, and tackle another listener question asking, "How do you properly implement a multi-step form in a boring Rails way?” Chris talks about his experiences with multi-step forms and gives his own hot take on refactoring: he doesn't until he feels pain! Database Switching in Dev Mode Gist In Relentless Pursuit of REST – Derek Prior Transcript: CHRIS: Happy Friday or whatever day it happens to be in your future situation. STEPH: Happy day. [chuckles] CHRIS: Happy day or night. I'm sorry, I'm done. [laughter] STEPH: Shut up. [laughs] Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I’m Steph Viccari. CHRIS: And I’m Chris Toomey. STEPH: And together, we're here to share a bit of what we've learned along the way. Hey, Chris, happy Friday. How's your week been? CHRIS: Happy Friday to you as well. My week's been good. It's been busy. I am taking next week off for a quick vacation. So it’s that…I think I've talked about this every time before I go on a vacation on the podcast, that focusing lens that going on vacation gives you. I want to make sure everything's buttoned up and ready to hand off, and I'm not going to be blocking anyone. And so, I always like the clarity that that brings. Because a lot of times I can look at well, there are infinity things to do, how do I pick? And now I'm like, no, but really, if I'm going to be gone for a week, I must pick. And so yeah, I'm now very excited to lean into vacation mode and relax for a bit. STEPH: Yeah, that's awesome. I hear you. I always go into that same mode pre-vacation. CHRIS: But in tech news, after the most recent episode that was released where we talked about the database switching stuff, a number of listeners were very kind and reached out with some thoughts. In particular, Dan Ott is one listener who reached out not only with just some generic thoughts, but he also gave a reproducible example of how to make things slightly better. So the particular thing that a few folks honed in on was the idea that I was describing the feeling of in production; we can occasionally run into these ActiveRecord read-only errors, which is a case where you have a GET request that happens to try to create or update a record. And as a result, you're going to get this ActiveRecord read-only because you're using the follower database, which has a read-only connection. All of that is fine, but ideally, we would want to catch those before production. We want to catch them in development. And broadly, the issue that we have here is that in production, our system is running in a different way. It's running with two different database connections, one for read-only, one for writing, and that's different than in development, where we're running with a single connection. As an interesting thing, a lot of the stuff that I see on the internet is about using SQLite in development and then Postgres in production. And so that's an example of development production parity that we've really...I think thoughtbot is definitely a place that I internalize this very strongly. But you've got to have the same database, and especially because it's relatively straightforward to run Postgres locally, I'm always going to be running the same version of the database locally as in production. But in this case, I'm now getting this differentiation. And so what Dan and a handful of other folks highlighted was you can actually reproduce this functionality in development mode with a fun little trick where you end up creating a secondary connection to your development database, but you mark it as replica:true. And so, by doing that, Rails will establish a read-only connection. And then, all of the behavior that you configure for production can also be run in development. So now, as you're building out a new feature, and if you happen to implement a GET request that does some side effect in the database, that'll blow up in development as opposed to production, which is very exciting. STEPH: Yeah, that’s awesome. I love that Dan reached out and shared this example with us. I actually haven't read through all of the details just yet. In fact, I just opened it up, and I started going through it, and there's a lot of really...it looks like a lot of great notes here and a really nice example that walks you through how to have that production parity locally. So this is really neat. I appreciate Dan sending this to us. CHRIS: Yeah, this is a wonderful little artifact actually that’s interesting just in and of itself. We'll certainly include a link in the show notes to the gist that Dan shared. What's interesting...I think I knew of this, but I've never actually seen it before. This is a single-file Rails application, which is a very novel concept, but it's got a bundler/inline call at the very top. And then there's an inline gemfile block, and then a set of requires to pull in the relevant Rails stuff. And then it configures the database connection, configures a single controller or actually a handful of controllers, it looks like, and then it renders inline HTML. And so it has all of the pieces. And I didn't realize that at first, but then I pulled it down and I just ran it locally. So it’s just Ruby and then the file that this just represents, and suddenly I had a reproducible Rails app. I believe this is used in reporting issues to Rails so you can get the minimum reproducible test case. And that's why this works is, I think, the Rails core team, over time, has pushed on any of the edges that wouldn't have worked and made it so that this is possible. But it's a really neat little thing where it's this self-contained example. And so running this file just via Ruby does all of this stuff, installs everything that's necessary. And then, you can click around in the very minimal HTML page that it provides and see the examples of the edges that it's hitting. And again, this is in development mode, so it's pushing on that. But yeah, it's both a really interesting tip as to how to work with this and a really interesting way to communicate that tip—so double points to Gryffindor, aka Dan Ott. STEPH: Double points to Gryffindor. I love it. CHRIS: I’m cool. [laughs] STEPH: That's very charming. [laughs] I've never seen anything like this either, in terms of one file that then can reproduce and run in a Rails app. Agreed, double points to Gryffindor, aka Dan. CHRIS: Aka Dan. STEPH: [chuckles] I hope Dan’s a Harry Potter fan. CHRIS: I hope so. And I hope he's a Gryffindor, who knows? Maybe Ravenclaw. It's really up in the air. But the other thing that is interesting that I haven't yet figured out here is this works for development mode. I've tested it in development. It's great. I was able to remove the fix line that I had in my code where I had one of these breaking controller actions and run with this configuration in development mode. And then boom, it blew up in development, and I was like, yay, this is great. Move those errors to the left, as they say. But I realized there are some other edge cases, known ones actually. Another developer on the team mentioned something where he knows of a place that this is happening, but that code path isn't running right now just because it's a seasonal thing within the app. And I was like, oh, that's really interesting. I wish there were a way to test all the behavior. Oh, tests, that's what I need here. And so I tried to configure this in test mode, but I wasn't exactly clear on what was failing. But at a minimum, I know that the tests run in transaction, so I think that might make this more complicated because if you have two connections to the same database, but you have transactions, I feel like that might be conflating things, and it wouldn't necessarily map perfectly in. But if we could get that, that would be really great. Moving forward, any new development the development configuration will cover what I need. But retroactively, as I'm introducing the database switching to the application, it would be great if the test suite were a way to find these edge cases. So that's still an open question in my mind. But overall, the development fix is such a nice little addition to this world. And again, thank you so much to Dan for sending this in. STEPH: Yeah, I agree; having this in tests would be wonderful. I am intrigued not having read through the full example that's been provided. But I'm wondering if this is one of those we default to read-only mode, although that feels like too much because we're often creating data for each test. So maybe we default to...yeah, you have to have both because you have to have your test set up where you're going to write data. So you can't default to just being in read-only mode. But then say you want to run a controller action or something else in a read-only mode. So then you would have to change your database connection for that action, and that sounds complicated. You also said something else I'm intrigued by. You said, “Move errors to the left.” CHRIS: Yes. Now that you're asking me, I'm trying to remember the exact context. But it's the idea that there are different phases in your development and eventually getting to production life cycle, and so a bug that a user sees that's all the way to the right. That's as far along the development pipeline as it can be, and that's the worst case. You don't want a user to see a bug. So QA would be a step right before that. And if you can catch it in QA, you've moved it to the left, which is a good thing. But even better than that would be to catch it in your automated tests, and maybe even better than that would be static analysis that's running in your editor, and maybe even better than that is a type system or something like that. So the idea of moving to the left is to push those errors or when you're catching the errors closer to the point where you're actually introducing them. And that's just a general theme that I like or a Beyoncé song. STEPH: I was just going to say, all right, move over, Beyoncé. There's another phrase in town, moving to the left. [laughs] CHRIS: I'm really going for a lot of topical pop culture references today. That's what I'm about. STEPH: We’ve got Harry Potter, Beyoncé. We've got to pull out one more at some point. CHRIS: We'll see. I don't want to stretch myself too thin right before vacation. But yeah, thanks again to Dan and the handful of other folks that reached out either on Twitter or via email to point me in the right direction on the database switching stuff. At some point, I should definitely do a write-up on this because I've now collected together just about enough information that it feels like it's worthy of a blog post, or at least that's the story in the back of my head. I got to cross a certain threshold before I'm probably going to write a blog post. But yeah, that's a bit of what's up in my world. What's going on in your world? STEPH: I love it. You're saying write a blog post into the mic, so then that way you know it's going to encourage you to write it later. CHRIS: That’s the trick right there. [chuckles] STEPH: Let's see, today's been a lovely day. It's been a lovely week in general. Today is especially lovely because it is thoughtbot’s Summit, and Summit is where we all gather. We do this once a year. So the whole team, all of us across all of our...I was going to say offices but now just across all of our home offices. And we get together, and we have a day filled with events, and we usually have a wonderful team that helps organize a bunch of events that then we get together for. So a number of those fun events are like paired chats, which is one of my favorites because I often talk to people that I haven't talked to in a long time or perhaps people that I haven't even met yet that have just recently joined the company. We also have lightning talks, and I know I'm very biased, but I think we have some of the best lightning talks. They are just hilarious. So I love our lightning talks. We're also doing escape rooms. Oh, speaking of which, there's a Harry Potter-themed murder mystery that's happening. We have Nintendo Switch parties and a professional tarot card reading, which I've never done, but I'm actually doing that later today after we're done chatting. CHRIS: Wow, that is an adventurous day. And I like that it's fun, and it's connecting people and getting to know your teammates and all those nice things. STEPH: Very much. I also have a hot take. I don't know if I've shared this with you, so I'm going to share it here with you on the mic in regards to this. So previous years, for Summit, we used to have more coding projects, too. They were often opt-in, but that's something that happened. And specifically, we have Ralphapalooza, which is our hackathon. And it recently came up where a number of us were talking about Ralphapalooza, and I have come to the potentially contentious point of view that I don't like hackathons. I'm not a fan of them. CHRIS: Hot take. I like that you led in calling it a hot take, and then you provided said hot take, so I have to respond as if it's a very hot take. STEPH: That's true. Maybe it's not a hot take. Maybe people disagree. What do you think? Do you like hackathons? CHRIS: I have enjoyed them in the past. But I will say, particularly within the context of Summit or Ralphapalooza, I always felt a ton of pressure. It's so hard to right-size a project to that space, to that amount of time. You want to do something that's not trivial. You want to do something that at the end of it you're like, oh cool, I did that. Either it's like a novel thing that you're creating, or you're learning something new or whatever, but it's so hard to really do something meaningful in that amount of time. And often, people are shooting for the moon, and then they're just like, “Ah, so it's just a blank page right now. But behind the scenes, there's a machine learning algorithm that is generating the blank page. And we think with enough inputs to the model that it'll…” and it is actually super interesting work they did. But there's the wonderful pressure at the end to present, which I think is really useful. I like constraints. I like the presentations; they’re always enjoyable, even in a case where it's like, this project did not go well, let's talk about that. That's even fun. But it really is so hard to get right. I've never gone to a hackathon outside of thoughtbot, so I can't speak to that, but I know that I have heard folks having a negative opinion of them. And I don't know that I'm quite at the hot take level that you are, but it's complicated, if nothing else. It's a lot of fun sometimes. I particularly remember the Elm project that you and I worked on. Well, we worked in the same group. We didn't actually work together, but same idea. That was a lot of fun. I liked that. STEPH: That’s a good point. Even within the context of Ralphapalooza, our hackathons are more...I’m going to use the word sustainable because they're nine-to-five hackathons where we are showing up; we are putting in the work. There is pressure, and we do want to present. But it's not one of those stay up all night and completely leave your family for a day or two to hack on some code. [laughs] Sorry, I'm throwing some shade right now. But even with that sustainable approach, I've always felt so much pressure. I enjoyed that green space and then getting to collaborate with people I don't typically collaborate with, but it still felt like there was a lot of pressure there, especially that presentation mode always made me nervous. Even if it is welcoming to say, “Hey, this didn't go well,” that doesn't necessarily feel great to present unless you are comfortable presenting that scenario. And I also really look forward to these company events as a way to connect and have some downtime and to just relax because then the rest of our days are often more stressful. So I want more company time for me to connect with colleagues but then also feel relaxed. So I was always, in the beginning, I was like, yeah, Ralphapalooza, woo, let’s go. And now I'm just like, nah, I'm good. I'd really just want a chill day with my colleagues. CHRIS: Is there an option to go for a walk with friends? Because if so, I will be taking door number two. STEPH: Cool. Well, I feel better having gotten that out into the ether now. But switching just a bit, there is something that I'm very excited about where we now have transcripts for each episode. This is something that you and I have been very excited about for a while and wanted to make happen but just weren't able to, but we now have them. And so people may have noticed them as we're adding them to the show notes. And I'm just so excited for a number of reasons, one, because there are a number of times that I have really wanted to search the shows or an episode for a particular topic and couldn't do so. So I'm just sitting there listening, trying to find a particular topic. There's also the fact that it will make the episodes more accessible. So for anyone that is hearing impaired or maybe if English isn't their first language, having it written down can make the episode more accessible. And there’s the massive SEO boost that's always a win. And then I don't know if this is going to happen, but I'm excited that transcripts may help us repurpose content because there's a number of our topics that I would love to see turned into blog posts, and I think having the transcript will make that easier. CHRIS: Yeah. I'm equally super excited about the addition of transcripts, and across the line, SEO is cool, I think. Yeah, that sounds nice. Being able to reuse the content is very interesting to me because this is definitely my preferred medium. I find that I can just show up on the microphone, and it turns out I have opinions about a lot of stuff but trying to write a blog post is incredibly difficult for me. The small handful of good things that we might have collectively said over the years if we can turn those into more stuff that sounds great and honestly, just the ability to search for and find older episodes now based on like, I know we talked about inbox zero. I remember that was an episode, but I don't know which one, now that’s searchable, and that's a thing that we can find. I actually still use the Upcase search for…I know I said something. I know there was a weekly iteration where I talked about some topic. And I built the search on Upcase for me as the primary user because one, I'm often referencing content on Upcase, and I want to be able to find it more easily, so I made the search. I also put a SQL injection vulnerability into the search in my first implementation so, go me. But then I got rid of it shortly after. STEPH: I love when people bring that energy of “I introduced this issue, go me,” because I find that very fun and also just very healthy in terms of we're going to make mistakes. And I have noticed a number of times at thoughtbot standup that whenever we make a mistake, or it’s like, I accidentally sent out real emails on production for a job that I thought I was testing on staging. Sharing those mistakes in a very positive light is a very honest way to approach it. So I just had to comment on that because I'm a big fan of that. CHRIS: I'm glad you enjoyed my framing of it. I really enjoy that type of approach or way to communicate, although I think it is a delicate line. Like, I don't want to celebrate these sorts of things because an SQL injection vulnerability is a non-trivial thing. It shows up in tons of applications, and we need to take security seriously and all of that sort of stuff. But I think the version that I think is good for that type of thinking or communication is the psychological safety. If we're scared of admitting that we introduced a bug, that's bad. That's going to lead to worse outcomes longer term. And so having the shared communication style openness to like, yep, that happened yesterday. And there should be a certain amount of contrite in this where it’s like, I feel bad that I did that. I even feel worse because when it happened, I recognized that it happened, and then I tried to exploit it in development mode to prove it to myself, and I couldn't exploit it. So I was like, I feel doubly bad as a programmer today. I both introduced a bug, and I'm not even smart enough to exploit it. But I know that an uber lead hacker out there could, and so I got to fix it. But that sort of story is part of the game. It's a delicate equilibrium, but having the ability to talk about that and having a group that can have a conversation, I do think that's very important. STEPH: Yeah, well said. I do think there's an important balance to strike there. Pivoting just a bit, we have a listener question, and this question comes from Benoit. Benoit wrote in to the show, “How do you properly implement a multi-step form in a boring Rails way?” I'm very interested in this question because I am working on a project that has a multi-step form. There are probably about maybe six, seven steps, and those steps can change based on different configurations. And our form is not implemented in a boring way at all. It's a very intricate, confusing design, I would say, which I think is fairly common when it comes to multi-step forms. I'm curious, what experience do you have with multi-step forms, and what's your general feeling with them? CHRIS: Well, I happen to be working on one right now. So generally, I don't have an oh, I got this, I know the answer. This is one of those that I'm like; I feel like each time I reinvent it a little bit. But the version that I'm working on right now is an onboarding flow. So we create a user record, which at this point I only have email associated with, and then from there, when a user lands, they need to provide a bunch of profile information, and it is a requirement. They have to fill it out. We need to have all of it before we can actually start doing the real stuff of the application. And so, the way that I've ended up modeling it is interesting. I'm going to use the word Interesting. I think I like it, but I'm not sure. So I have this model; let’s call it a profile that we're going to associate with the user. And the profile has a bunch of fields: first name, last name, address, phone number, and a handful of other things. And again, I need to have these pieces of information. So I want those to be non-nullable columns. But as someone is walking through this form, I'm not going to have all the information. So there's going to be a progression. We'll get first name, then we get last name, and then we get the next piece of information. So I need a nullable storage, but I don't want to just put it into the session or something like that, which I think would be an option. So what I've done is I've introduced a secondary model. So this is a full ApplicationRecord database-backed model called partial profile. And it is almost identically the same interface as the profile, but each field is nullable. There's also a slight difference in that the profile field has an additional status column that talks about once we've gone through all of this, we can add some status and track other things. But yeah, that main difference of in the profile, everything is non-nullable, and the partial profile is nullable. So then there's a workflowy object, a command object, as I like to have in my systems these days that handles the once they've gathered all their information, turn the partial profile into a profile, send it out to an external system that does some verification and some other lookups and things like that. And then, based on the status of that, mark the status of the profile. But one of the things that I was able to do is make that transition from partial profile to full profile. I'm doing that within a transaction. So if at any point anything fails within all of this, I can roll the whole thing back, and I'll be back to only having the partial profile, which was a very important thing. I would not want to have a partial profile and a profile because that's a bad state. But a lot of this for me is about data modeling and wanting to tell truths with the database and constrain what are the valid states of my application? So one solution would be to just have a profile model that has nullable columns for all of these fields. But man, do I hate that answer. So I went what feels like an extreme take of having two fundamentally different models, but that's where it's actually working out well. I'm able to share validations across them. So as new data is added, I can conditionally validate as new things are shared, and I'm able to share that via concern in the two models. So it's progressively getting more constrained as I add data to this thing. And then, in the background, there is a single controller that skips through all of the steps and has an update action that just keeps pushing data into this partial profile until, eventually, it becomes a profile. So that's focused specifically on the data model stuff. I think there are other aspects of a more workflowy type thing in Rails, but that's our thing. What do you think, good idea, bad idea, terrible idea? STEPH: [chuckles] One, I love that you have this concrete example because I have some higher-level ideas around this particular question, but I didn't have a great example that I wanted to share. So I love that you have that, that we can talk through. I really, really like how you have found a way to represent the fact that each valid state of your application as you refer to it….so you have this concept of someone's going through the flow and their address can be nullable at this stage, but by the end of this flow, it shouldn't be nullable anymore. So you have that concept of a partial profile, and then it gets converted into a profile. I am intrigued by the fact that it's one controller because that is where I am feeling pain with the multi-step form that I'm working on where we have one very large controller that handles this entire...I'm going to call it a wizard since that's how it's referred to, and there are seven or eight different steps in this wizard. And the job of this controller is each time someone goes to a new step; this controller is trying to figure out okay; what step are you on based on the parameters that you have, based on some of the model attributes that are set? What step are you on, and what should we show you? And that has led to a very large method and then also complex, lots of conditional-based code. And instead, I would really like to flip that question around or essentially remove the what step am I on? And instead, ask what step is next? So instead, take the approach that each step of the form should have a one-to-one mapping to another controller. And that can get really hard because we're often conditioned to the idea that we should have a one-to-one correlation between each controller and an ActiveRecord model, but that's not necessarily what happens in our form. You have the concept of a partial profile versus being able to map to a full profile. So I am very much in favor of the idea of trying to map each step of the form to a controller. So that, to me, makes the code more boring. It makes it more understandable. I can see what's happening for each step. But then it's not boring in terms that it requires creativity to say, okay, I don't have a perfect ActiveRecord model that maps to this controller, but what resourceful controller can I make instead? What is the domain object that I can put here instead? Maybe it's an ActiveModel object instead. So that way, we can apply ActiveRecord-like behavior to plain old Ruby objects, or maybe it's using a form object. That way, we can still validate all the fields that the user is providing to us, but that doesn't necessarily map directly to a full profile just yet. So I really like all the things that you've said. But I am intrigued by the approach of using a single controller. How's that feeling so far? CHRIS: That part is actually feeling fine. So a couple of things you said in there stand out to me, one, where it's a very big controller. That is something that I would definitely avoid. And so, I have extracted other pieces. There is an object that I created, which at this point is just in-app models because I didn't know where else to put it, but it's called onboarding. And so the workflow that I'm trying to introduce, the resource maybe is what we would call it, is the idea of onboarding, but it's not an ActiveRecord level thing. At the ActiveRecord level, I have a profile and a partial profile, and then there's an account, and there's also a user. There are four different database level models that I want to think about. But fundamentally, from a user perspective, we're talking about onboarding. And so I have an object that is called onboarding, and it contains the logic around given the data that we have now, what step comes next? Is this a valid step? Should the user go back? Et cetera, et cetera. So that extraction is one piece that definitely makes sense. Also, thus far, mine is relatively straightforward in terms of I get data in, and I just need to update my partial profile record each time. So the update action is very straightforward. But I've done different versions of this where there are more complex things that happen. And so what I've done is basically make a splat route. So it's like onboarding/ and then the step name and that gets posted or gets put, I guess, along with everything else for the update. And so now the update says, “Well, if I'm updating for this, then handle it this way; otherwise, just update the profile record.” And so then I can extract maybe another command object that handles like, “Oh, when we're doing the address stuff, we actually have to do a little bit of a lookup and a cross-reference and some other things, but everything else is just throwing data into a database record.” And so that's another place where I would probably make an extraction, which is this specialized case of handling the update of the address is special. So I want to extract that, be able to test around that, et cetera. But fundamentally, the controller thing actually works out pretty well. The single controller with those sorts of extractions has worked out well for me. STEPH: Okay, cool. Yeah, I can see how depending on how complex your multi-step form is, having it all in one controller and then extracting those smaller objects to then handle each step makes a lot of sense and feels very friendly to read, and is very testable. For the form that I'm working in, there are enough steps and enough complexity. I'd really love to break it out. In fact, that's something that we're working on right now is taking each of those chunks, each state of the form, and introducing a controller for it. So let's say if you are filling out an appointment and we need to get your consent for something, then we actually have a consent controller that's going to handle that part, that portion of it. And I'd be intrigued for your form if things got complicated enough that it’s the concept of onboarding or a wizard that leads us to having one controller because then we think of this one concept. But there are often four or five concepts that are then hiding within that general idea of an onboarding flow. So then maybe you get to the point that you have an onboarding address or something like that. So then you could break it out into something that still feels RESTful but then lets you have that very boring controller that does just enough and essentially behaves like a bi-directional linked list. So it knows, based on the route, it knows the step that it's on, and it knows where to go back, and then it knows the next step to go forward. And then that's all it's responsible for, so it doesn't have to also figure out what step am I currently on? CHRIS: I like the bi-directional link list, dropping knowledge bombs right there. STEPH: Pew-pew. CHRIS: It's interesting. I don't necessarily feel...right now; I don't feel that pressure. I feel fine with the shape of the singular controller. This is perhaps not necessarily even a good thing, but I think my bias is always to think a lot about the URL structure and really strongly embrace the user point of view. I'm going through the workflow. I don't care if I'm picking from a calendar and setting up a date versus filling in an address field or how you're storing those on the back end; that’s your job, developer people. And I try as hard as I can to put myself in that mindset. And so the idea that there's this sequential thing that knows how to go back and forward and shows like, show which page we're on, that feels like it belongs in one controller in my mind, or I guess I'm fine with it being in one controller. And splitting it out feels almost more complicated in that I then need to share some of that logic across them, which is very doable by extracting some object that contains a logic of what goes back, what goes forward. But I think I like to align URL structure to how many controllers as opposed to anything else. And because I'm keeping a consistent URL structure where it's /onboarding/name /onboarding/address, and I'm stepping through in that way for all of those things, then it makes sense to me that those go to my onboardings controller. But I'm interested to see if I start to feel pain somewhere down the road because I expect this onboarding to get more complicated as time goes on. And will I bump my head on the ceiling? Probably. It seems likely. But for now, I'm liking it. STEPH: Yeah, it certainly makes sense. It's one of those areas that you want to start small and then build out as it feels reasonable. But in regard to the URLs, I'm with you, where I very much want there to be a clean, nice URL for the user to see. And then we handle out any of those details on the back end since that is our work to do. But I am still envisioning that there is a clean URL. So it may be you have an onboarding/address and then onboarding/consent, borrowing from my previous example, but then that maps to where you have an onboarding namespaced controller that is then for an address or for consent. So you don't necessarily have an object that's having to be passed along that stores the state and the next step that the person is on. But that way, you do definitively know from the route okay, I am on this step. And so then that's how you get away from that question of what step am I on? Because that's already given to us based on the URL and then the controller. So then you only have to care about validating the input that's provided on that page, but then also being able to calculate dynamically okay, if this person needs to go back, what's the previous step and if they go forward, what's the next step? CHRIS: What you're saying totally makes sense. And I'm now worried that I'm going to wake up a few days from now and look at my controllers and be like, I hate this. Why did I ever do this? I think the hesitation that I had, and this feels like a terrible reason, but in terms of what the config/routes.rb setup would be for this, it's namespace onboardings. And then within that, a bunch of singular routes and inside of that, inside that namespace, would be a bunch of singular resources so like, resource address, resource blah, blah. And I don't know why, but I don't like that. I don't like that. I don't like that. Now that I'm saying it out loud, I'm like, yeah, that actually would be a pretty clean mapping. And right now, I have implicitly what those available routes are but not explicitly. It also feels like there would be a real explosion of controllers there because there's a bunch of steps and growing in this controller or in this namespace. And they're all going to do the same thing, in my case at least, of just adding data in. But that's not a reason to not make...like, controllers are cheap; I should make controllers so, hmm. STEPH: Yeah. So I think that's the part in my mind that maps to the boring part is because we are creating controllers. There's maybe an explosion of them, and it's boring. Like, the controllers don't do very much. And then that feels a little bit wrong to us because we're like, okay, I created this controller, it does very little. So maybe I should actually group this logic somewhere else. But I think that is the heart of it and how you stay boring is where you have just that code be so simple that it almost feels wrong. CHRIS: That right there, that sound bite that we just had, that was a knowledge bomb drop, and I liked it. Now I've got to go back and refactor to the form that you're talking about because I am sold. STEPH: Oh, I'm glad you like it. I am intrigued if you do refactor then what that would look like and how it feels. But I also totally understand you're busy, so if you don't, that's cool too, no pressure. CHRIS: My honest answer is that I almost certainly won't refactor until I feel the pain. It's one of those things where like, okay, maybe I've now decided that this code is not the best, but the time to refactor it isn't when that code is just humming along working fine. It's a general thing that I think we share in terms of how we think about it. But the preemptive refactoring, I guess broadly speaking, I'm not a fan of preemptive factoring. I'm a fan of refactoring just in time or as we're feeling the pain, which is the counterpoint to that is let's not extract tech debt tickets because then they turn into preemptive refactoring again. It’s like, ah, I'm not really feeling...I'm not in there right now. But the version of the code that I have now is probably fine. I don't think it's a problem although I am convinced now of the boring way. I want to go back to the boring way, but it will feel like it's worth changing down the road when I feel any pressure in that system or need to revisit it. So it's like that. That's how I think about that sort of thing. STEPH: Yeah, I wholeheartedly agree. It's one of those if you refactor...if this is a side project, if you want to refactor just for testing new software theories and then reflecting on what that new refactor looks like, that's awesome. In terms of any other refactors, then I wholeheartedly favor waiting until you feel that pain and it feels like the right thing to do; otherwise, it's unnecessary code turn. And while I strongly believe in experiments, I don't believe in putting teams through those personal experiments. CHRIS: More hot takes from Steph. I like it. STEPH: Circling back just a bit and talking about having one controller for each step of the form, that part I struggle with it frankly because it is hard to think about this is a concept, but what do I call this? Because it doesn't necessarily map to something necessarily in my database. There's a really great talk by Derek Prior that’s called In Relentless Pursuit of REST, where Derek does a great job of providing some inspiration around how to create routes that don't necessarily feel like they could be RESTful, or maybe they're following that more RPC format. And he does a great job of then turning around and saying, “Well, this is how we could think about, or this is how we could shift our thinking in turning this into a more RESTful route.” So then it does map to something that's meaningful in our domain. Because we have thoughtfully, or likely very thoughtfully, grouped this form together in a meaningful way to the user. So then that's inspiration right there to give us a way to name this thing because we are showing it to the user in a meaningful way. So then that means we can also give it a meaningful name. That’s all I got on multi-step forms. [laughs] CHRIS: That feels like it was a lot. We've covered data models. We’ve covered controller structures. We fundamentally reoriented my thinking on the matter. I feel like we covered it. STEPH: Yeah, I agree. Well, Benoit, thank you for sending in this question. I hope you found our discussion very helpful. And on that note, shall we wrap up? CHRIS: Let's wrap up STEPH: The show notes for this episode can be found at bikeshed.fm. CHRIS: This show is produced and edited by Mandy Moore. STEPH: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or review in iTunes as it helps other people find the show. CHRIS: If you have any feedback for this or any of our other episodes, you can reach us at @bikeshed on Twitter. And I'm @christoomey. STEPH: And I'm at @SViccari. CHRIS: Or you can email us at hosts@bikeshed.fm. Thanks so much for listening to The Bike Shed, and we'll see you next week. All: Byeeeee. Announcer: This podcast was brought to you by thoughtbot. thoughtbot is your expert design and development partner. Let's make your product and team a success.Support The Bike Shed
undefined
May 25, 2021 • 46min

294: Perfect Duplication

On this week's episode, Steph and Chris respond to a listener question about how to know if we're improving as developers. They discuss the heuristics they think about when it comes to improving, how they've helped the teams they've worked with plan for and measure their growth, and some specific tips for improving. Rails Autoscale Rubular regex playground The Pragmatic Programmer Go Ahead, Make a Mess by Sandi Metz Confident Code - Avdi Grimm Therapeutic Refactoring - Katrina Owen Refactoring, Good to Great - Ben Orenstein Transcript CHRIS: There's something intriguing about the fact that we're having this conversation, but the thing that's recorded just starts at this arbitrary point in time, and it's usually us rambling about golden roads. But, I don't know; there's something existential about that. STEPH: It's usually when someone says something very funny or starts singing [laughs], and then that's when we immediately: record, record! CHRIS: I've never sung on the mic. That doesn't sound like a thing I would do. STEPH: [laughs] CHRIS: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Chris Toomey. STEPH: And I'm Steph Viccari. CHRIS: And together, we're here to share a bit of what we've learned along the way. So Steph, how's your week going? STEPH: Hey Chris, it's going really well. Normally I'm always like, wow, it's been such an exciting week, and it's been a pretty calm, chill week. It's been lovely. CHRIS: That sounds nice actually in contrast to the "Well, it's been a week," that sort of intro of "I don't know, it's been fine. It can be really nice." STEPH: By the time we get to this moment of the week, I either have stuff that I'm so excited to talk about and have a little bit of a therapy session with you or share something new that I've learned. I agree; it's nice to be like, yeah, it's been smooth sailing this whole week. In fact, it was smooth sailing enough that I decided to take on something that I've been meaning to tackle for a while but have just been avoiding it because I have strong feelings about this, which you know but we haven't talked about yet. But it comes down to managing emails and how many emails one should have that are either unread that are just existing. And I fall into the category of where I am less scrupulous about how many unread or managed emails that I have. But I decided that I'd had enough. So I used a really nice filter in Gmail where I said I want all emails that are before 2021 and also don't have a user label, so it's has:nouserlabels because then I know those are all the emails that I haven't labeled or assigned to a particular...I want to say folder, but they're not truly folders; they just look like folders. So they're essentially like untriaged or just emails that I've left hanging out in the ether. And then I just started deleting, and I got rid of all of those that hadn't been organized up until that point. And I was just like yep, you know if I haven't looked at it, it's that old, and I haven't given a label by this point, I'm just going to move on. If it's important, it will bubble back up. And I feel really good about it. CHRIS: Wow, that is -- I like how you backed me into a corner. Obviously, I'm on the other side where I'm fastidiously managing my email, which I am, but you backed me into that corner here. So, yeah, that's true. Although the approach that you're taking of just deleting all the old email that's a different one than I would have taken [chuckles] so, I like it. It's the nuclear option. STEPH: Okay, so now I need to qualify. When you delete an email, initially, I'm thinking it's going to trash, and so it's still technically there if I need to retrieve it and go back and find it. But you just said nuclear option, so maybe they're actually getting deleted. CHRIS: They're going into the trash for 30 days; I think is the timeline. But after that, they will actually delete them. The archive is supposed to be the place where you put stuff I don't want to see you anymore. But did you archive or delete? STEPH: Oh, I deleted. CHRIS: Oh, wow. Yeah. All right, you went for it. [laughter] STEPH: Yeah, and that's cool. And it's in trash. So I basically have a 30-day window where I'm like, oh, I made a mistake, and I need to search for something and find something and bring it back into my world; I can find it. If I haven't searched for it by then in 30 days, then I say, you know, thanks for the email, goodbye. [chuckles] And it'll come back if it needs to. CHRIS: I like the approach. It would not be my approach, but I like the commitment to the cause. Although you still have...how many emails are still in your inbox now? STEPH: Why do we have to play the numbers game? CHRIS: [laughs] STEPH: Can't we just talk about the progress that I have made? CHRIS: What wonderful progress you've made, Steph. [laughter] Like, it doesn't matter what I think. What do you think about this? Are you happy with this? Does this make you feel more joy when you look into your email in the Marie Kondo sense? STEPH: It does. I am excited that I went ahead and cleared all this because it just felt like craft. So I have taken what may be a very contentious approach to my email, where I treat it as this searchable space. So as things come in, I triage them, and I will label them, I will star them. I will either snooze them to make sure I don't miss the high actionable emails or something that's very important to me to act on quickly. But for the most part, then a lot of stuff will sit in that inbox area. So it becomes like this junk drawer. It's a very searchable junk drawer, thanks to Google. They've done a great job with that. And it feels nice to clear out that junk drawer. But I do have such an aversion to that very strong email inbox zero. I respect the heck out of it, but I have an aversion; I think from prior jobs where I was on a team, and we could easily get like 800 emails a day. My day all day was just triaging and responding to emails and writing emails. And so I think that just left a really bitter experience where now I just don't want to have to live that life where I'm constantly catering to what's in my inbox. CHRIS: That's so many emails. STEPH: It was so many emails. We were a team. It was a team inbox. So there were three of us managing this inbox. So if someone stepped away or if someone was away on vacation, we all had access to the same emails. But still, it was a lot of emails. CHRIS: Yeah, inbox zero in a shared inbox that is a level that I have not gotten to but getting to inbox zero and actually maintaining that is very much a labor of love and something that I've had to invest in. And it's probably not worth it for most people. You could convince me that it is not worth it for me, that the effort I'm putting in is too much effort for not enough reward. Well, it's one of those things where I find the framing that it puts on it, like, okay, I need to process my email and get it to zero at least once a day. Having that lens makes me think about email in a different way. I unsubscribe from absolutely everything. The only things that are allowed to come into my email are things that I will act on that actually deserve my attention, and so it forces that, which I really like. And then it forces me to think about things. I have a tendency to really hold off on decisions. So I'm like, ah, okay. I can go see friends on Saturday or I can do something else. Friends like actual humans, not the TV show, although for the past year, it's definitely more of the TV show than the real people. But let's say there's a potential thing that I could do on the weekend and I have to decide on that. I have a real tendency to drag my feet and to wait for some magical information from the universe to help this decision be obvious to me. But it's never going to be obvious, and at some point, I just need to pick. And so for inbox zero, one of the things that comes out of it for me is that pressure and just forcing me to be like, dude, there's no perfect answer here, just pick something. You got to just pick something and not wasting multiple cycles rethinking the same decision over and over because that's my natural tendency. So in a way, it's, I don't know, almost like a meditative practice sort of thing. There's utility there for me, but it is an effort, and it's, again, arguably not worth it. Still, I do it. I like it. I'm a fan. I think it's worth it. STEPH: I like how you argued both sides. I'm with you. I think it depends on the value that you get out of it. And then, as long as you are effective with whichever strategy you take, then that's really what matters. And I do appreciate the lens that it applies where if you are getting to inbox zero every day, then you are going to be very strict about who can send you emails about notifications that you're going to receive because you are trying to reduce the work that then you have to get to inbox zero. So I do very much admire that because there are probably -- I'm wasting a couple of minutes each day deleting notifications from chats or stuff that I know I'm not necessarily directly involved in and don't need action from me. And then I do get frustrated when I can't adjust those notification settings for that particular application, and I'm just subscribed to all of it. So some of it I feel like I can't change, and then some of it, I probably am wasting a few minutes. So I think there's totally value in both approaches. And I'm also saying that to try to justify my approach of my searchable inbox. [laughs] CHRIS: There are absolutely reasons to go either way. And also, to come back to what I was saying a minute ago, it may have sounded like I'm a person who's just on top of this. I may have given that impression briefly. I think the only time this has actually worked in my life is when Gmail introduced snooze both in the mobile app and on the desktop. So this is sometime after Google's inbox product came out, and that was eventually shut down. So it's relatively recent because, man, I just snooze everything. That is the actual secret to achieving inbox zero, just to reach the end of the day and be like, nah, and just send all the emails to future me. And then future me wakes up and is like, "You know, it's first thing in the morning. I got a nice cup of coffee, and this is what you're going to do to me, past me?" So there's a little bit of internal strife there within my one human. But yeah, the snoozing is actually incredibly useful and probably the only way that I actually get things done and the same within any task management system that I have; maybe future me will do this. STEPH: I think you and I both subscribed to the that's a future me problem. We just do it in very different ways. But switching gears a bit, how's your week been? CHRIS: It's been good, pretty normal, doing some coding, normal developer things. Actually, there's one tool that I was revisiting this week that I'm not sure that we've actually talked about on the show before, but it's Rails Autoscale. Have you used that before? STEPH: I don't think I have. It sounds very familiar, but I don't think I've used it. CHRIS: It's a very nice, straightforward Heroku add-on that does exactly what you want it to do. It monitors your web and worker dynos and will scale up. But it uses a different heuristic than -- So Heroku has built-in autoscaling, but theirs is based on response time, which is, I think, a little bit laggier of a metric. Like if your response time has gotten bad, then you're already in trouble, whereas Rails Autoscale uses queue time. So how long is a request waiting before? I think it's at the Heroku router; it goes onto the dyno that's actually going to process the request? So I think that's what they're monitoring. I may be wrong on that. But from the website, they're looking at that, and you can configure it. They actually have a really nice configuration dashboard for configure between this range, so one to five dynos at most, and scale in this way up and in this way down. So like, how long should it wait? What's the threshold of queue time? Those sorts of things. So they have a default like just do the smart thing for me, and then they give you more control if your app happens to have a different shape of data, which is all really nice. And then I've been using that for a while, but I recently this week actually just turned on the worker side. And so now the workers will autoscale up and down as the Sidekiq queue -- I think for the Sidekiq side, it's also the queue time, so how long a job sits in the queue before getting picked up. And there are some extra niceties. It can actually infer the different queue names that you have. So if you have a critical, and then a mailer, and then a general as the three queues that Sidekiq is managing, you really want critical to not back up. So you can tell it to watch that one but ignore the normal one and only use -- Like, when critical is actually getting backed up, and all the other stuff is taken over then -- Again, it's got nice knobs and things, but mostly you can just say, "Turn it on and do the normal thing," and it'll do a very smart thing." STEPH: That does sound really helpful. Just to revisit, so Heroku for autoscaling, when you turn that on, I think Heroku does it based on response times. So if you get into a specific percentile, then Heroku is going to scale up for you to then bring down that response time. But it sounds like with this tool, with Rails autoscaling, then you have additional knobs like the Sidekiq timing that you'd referenced. Are there some other knobs that you found really helpful? CHRIS: Basically, there are two different sides of it. So web and background jobs are going to be handled differently within this tool, and you can actually turn them on or off individually, and you can also, within them, the configurations are specific to that type of thing. So for the web side, you have different values that you can set as the thresholds than you do on the Sidekiq side. Overall, the queue name only makes sense on the Sidekiq side, whereas on the web side, it's just like the web requests all of them 'Please make sure they're not spending too much time waiting for a dyno to actually start processing them.' But yeah, again, it's just a very straightforward tool that does the thing that it says on the tin. I enjoy it. It's one of those simple additions where it's like, yeah, I think I'm happy to pay for this because you're just going to save me a bunch of money every month, in theory. And actually, that side of it is certainly interesting, but more of my app will be responsive if there is any spike in traffic. There's still plenty of other performance things under the hood that I need to make better, but it was nice to just turn those on and be like, yeah, okay. I think everything's going to run a little better now. That seems nice. But yeah, otherwise, for me, a very straightforward week. So I think actually shifting gears again, we have a listener question that we wanted to chat about. And this is one that both of us got very interested to chat about because there's a lot to this topic, but I'm happy to read it here. So the overall topic is improving as a developer, and the question goes, "How do you know you're improving as developers? Is your improvement consistent? Are there regressions? I find myself having very different views about code than I did even a year ago. In some cases, I write code now in a way that I would have criticized not too long ago. For example, I started writing a lot more comments. I used to think a well-named variable obviated the need for comments. While it feels like I'm improving, I have no way of measuring the improvement. It's only a gut feeling. Thanks. Love the show." And this comes from Tom. Thank you, Tom. Glad you enjoy the show. So, Steph, are you improving as a developer? STEPH: I love this question. Thanks, Tom, for sending it in because it is one that I think about but haven't really verbalized, and so I'm really excited to dive into this. So am I improving as a developer? It comes down to, I mean, we first have to talk through definitions. Like, what does it mean to become a better developer? And then, we can talk through metrics and understanding how we're getting there. I also love the other questions, which I know we'll get to. I'm just excited. But are there any regressions? And also, in my mind, they already answered their own question. But I'm getting ahead of myself. So let me actually back up. So how do you know you're improving as a developer? There are a couple of areas that come to mind. And for me, these are probably more in that space of they still have a little bit of a gut feeling to them, but I'm going to try hard to walk that back into a more measurable state. So one of them could be that you're becoming more comfortable with the work that you're doing, so if you are implementing a new email flow or running task on production or writing tests that become second nature, those types of activities are starting to feel more comfortable. To me, that is already a sign of progress, that you are getting more comfortable in that area. It could be that time estimates are becoming more accurate. So perhaps, in the beginning, they're incredibly -- like, you don't have any idea. But as you are gaining experience and you're improving as a developer, you can provide more accurate estimates. I also like to use the metric of how many people are coming to you for help, not necessarily in hard numbers, but I tend to notice when someone on a team is the person that everybody else goes to for help, maybe it's just on a specific topic, maybe it's for the application in general. But I take that as a sign that someone is becoming very knowledgeable in the area, and that way, they're showing that they're improving as a developer, and other people are noticing that and then going to them for help. Those are a couple of the ones that I have. I have some more, but I'd love to hear your thoughts. CHRIS: I think if nothing else, starting with how would we even measure this? Because I do agree it's going to be a bit loose. Unfortunately, I don't believe that there are metrics that we can use for this. So the idea of how many thousand lines of codes do you write a month? Like, that's certainly not the one I want to go with. Or, how many pull requests? Anything like that is going to get gamified too quickly. And so it's really hard to actually define truly quantifiable metrics. I have three in mind that scale the feedback loop length of time. So the first is just speed. Like, how quickly are you able to do the same tasks? So I need to build out a page in Rails. I need a route; I need a controller. I need a feature spec, those sort of things. Those tasks that come up over and over: are you getting faster with those? That's a way to measure. And there's an adage that I think comes from biking, professional cycling, that it never gets any easier; you just go faster. And so the idea is you're doing the same work over time, but you just get a little bit faster, and you're always trying that edge of your capabilities. And so that idea of it never gets any easier, but you are getting faster. I like that framing. We should be doing the same work. We should never get too good for building a crud app. That's my official stance on the matter; thank you very much. But yeah, so that's speed. I think that is a meaningful thing to keep an eye on and your ability to actually deliver features in a timely fashion. The next one would be how robust are the things that you're building? What's the bug count? How regularly do you have to revisit something that you've built to change it, to tweak it either because it doesn't exactly match the intent of the feature that you're developing or because there's an actual bug in it? It turns out this thing that we do is very hard. There are so many moving pieces and getting the design right and getting the functionality just right and handling user input, man, that's tricky. Users will just send anything. And so that core idea of robustness that's going to be more on a week scale sort of thing. So there's a little bit of latency in that measure, whereas speed that's a pretty direct measure. The third one is…I don't know how to frame this, but the idea of being able to revisit your code either yourself or someone else. So if you've written some code, you tried to solve a problem; you tried to encode whatever knowledge you had at the given time in the code. And then when you come back three months later, how easy is it to revisit that code, to change it, to extend it either for yourself (because at that point you've forgotten everything) or for someone else on the team? And so the more that you're writing code that is very easy to extend, that is very easy to revisit and reload that context into your head, how closely the code maps to the actual domain context I think that's a measure as well that I'm really interested in, but there's the most lag in that one. It's like, yeah, months later, did you do a good job? And so the more time you spend, the more you'll have a measure of that, but that's definitely the laggiest of the measures that I have in mind. STEPH: I love that adage that you shared that it never gets easier, but you get faster. That feels so relevant. I really like that. And then I hadn't considered the robustness. That's a really nice one, too, in terms of how often do you have to go back and revisit issues that you've added? CHRIS: You just write code without bugs; that's why you don't think about it. STEPH: [laughs] Oh, if only that were true. CHRIS: Yeah, if only that were true of any of us. STEPH: To keep adding to the list, there are a couple more that come to mind too. I'd mentioned the idea that certain tasks become easier. There's also the capability or the level of comfort in taking on that new, big, scary, unknown task. So there is something on the Teams' board where you're like, I have no idea how to do that, but I have confidence that I can figure it out. I think that is a really big sign that you are growing as a developer because you understand the tools that'll get you to that successful point. And maybe that means persuading someone else to help you; maybe it means looking elsewhere for resources. But you at least know how to get there, which then follows up on your ability to unblock yourself. So if you are in that state of I just don't know what to do next, maybe it's Googling, or maybe it is reaching out for help, but either way, you keep something moving forward instead of just letting it sit there. Another area that I've seen myself and other people grow as developers is our ability to reason about quality and speed. It's something that I feel you, and I talk about pretty often here on the show, but it comes down to our ability to not just write code but then to also make good decisions on behalf of the company that we are working for and the team that we're working with and understanding what matters in terms of what features really need to be part of this MVP? Where can we make compromises? And then figuring out where can we make compromises to get this out to market? But what's really important then for circling back to your idea of revisiting the code, we want code that we can still come back and trust and then easily maintain and make updates to. And then I feel like I'm rambling, but I have a couple more. Shall I keep going? CHRIS: Keep going. Those are great. STEPH: All right. So for the others, there's an increase in responsibilities that I notice. So, in addition to people coming to you more often for help, then it could be that you are receiving more responsibilities. Maybe you are taking on specific ownership of the codebase or a particular part of the team processes. Then that also shows that you are improving and that people would like you to take leadership or ownership of certain areas. And then this one, I am throwing it in here, but your ability to run a meeting. Because I think that's an important part of being a good developer is to also be able to run a meeting with your colleagues and for that to be a productive meeting. CHRIS: Cool. I like that one. I think I want to build on that because I think the core idea of being able to run a meeting well is communication. And I think there's one level of doing this job where it's just about doing the job. It's just about writing the code, maybe some amount of translating a specification or a ticket or whatever it is into the actual code that you need to write. But then how well can you communicate back out? How well when someone in project management says, "Hey, we want to build an aggregated search across the system that searches across our users, and our accounts, and our products, and our orders, and our everything." And you're like, "Okay. We can do that, but it will be hard. And let's talk about the trade-offs inherent in that and the different approaches and why we might pick one versus the other," being able to have that conversation requires a depth of knowledge in the technical but then also being able to understand the business needs and communicate across that boundary. And I think that's definitely an axis on which I enjoy pushing on as I'm continuing to work as a developer. STEPH: Yeah, I'm with you. And I think being a consultant and working at thoughtbot heavily influences my concept of improving as a developer because as developers, it's not just our job to write code but to also be able to communicate and help make good decisions for the team and then collaborate with everyone else in the company versus just implement certain features as they come down the pipeline. So communication is incredibly important. And so I love that that's one of the areas that you highlighted. CHRIS: Actually speaking of the communication thing, there's obviously the very human-centric part of that, but there's, I think, another facet of technical communication that is API design. When you're writing your code, what do you choose to expose and make accessible to collaborators? And I don't just mean API in the terms of a REST API that people are heading, but I mean a class that you have in your system. What are the private methods, and what are the public methods? And how do you think about the shape of it? What data do you expose? What do you not expose? And that can be really impactful because it allows how can you change things over time? The more that you hide, the more you can change. But then, if you don't allow your collaborators to access the bits that they need to be able to work with your system, that's an interesting one that comes to mind. It also aligns with, I don't think you were saying this exactly, but the idea of taking on more amorphous projects. So like, are you working within a system and adding a new feature, or are you designing a system? Are you architecting? The word architect that role can sometimes be complicated within organizations, but that idea of I'm starting fresh, and I'm building a system that others will then work within I think this idea of API design becomes really interesting in that context. What shape do you give to the system that we're working within, and what affordances? And all of that. And that's a very hard thing to get right. So it comes from experience of being like, I used some stuff in the past, and I hated it, so when I am the architect, I will build it better. And then you try, and you fail, and you're like, well, okay, but now I've learned. And then you try it, and then you fail for different reasons. But the seventh time you try, it may be just that time you get the public API just right on the first go. STEPH: Seven times's a charm. That's how that goes, right? CHRIS: That is my understanding, yes. STEPH: I think something that is related to the idea of are you working in a structured space versus working in a new space and then how you develop that API for other people to work with. And then how do you identify when to write a test and what to test? That's another area that you were just making me think of is that I can tell when someone has experience with testing because they know what to test and what feels important to test. And essentially, it comes down to can I deploy with confidence? But there are a lot of times, especially if you're new to testing, that you're going to test everything, and you're going to have a lot of probably useless slow tests. But over time, you will start to realize what's really important. And I think that's one of the areas where then it does start to get harder to measure yourself as a developer because all of our jobs are different, and we work with different tech stacks, and we all have our unique responsibilities and goals. So it may be hard to say specifically like, "Oh, you're really good at X, Y, and Z, and that's how you know that you're improving as a developer." But I have more thoughts on that, which we'll get to in a moment where Tom mentioned that they don't have a way of measuring improvement. Shall I go ahead and jump ahead to I have no way of measuring that improvement, or shall we talk about regressions next? CHRIS: I'm interested in your thoughts on the regressions question because it's not something that I've really thought about. But now that he's asked the question, I'm thinking about it. So yeah, what are your thoughts on that? STEPH: My very quick answer is yes, [laughs] that there are regressions mainly because I respect that our brain can only make so much knowledge readily available to us, and then everything else goes into long-term storage. We can access it at some point, but it takes additional time, or maybe it takes some practice to recall that skill. So I do think there are regressions, and I think that's totally fine that we should be focused on what is serving us most at the moment and be okay with letting go of some of those other skills until we need to refine them again. CHRIS: Yeah. I think there's definitely a truth to true knowledge and experience with, say, a framework or a language that can fade. So if I spend a lot of time away from JavaScript, and then I come back, I'm going to hit my head on a few low ceilings every once in a while for the first couple of days or weeks or whatever it is. It was interesting actually that Tom highlighted the idea of he used to not write comments, and now he writes more comments, and so that transition -- I think we've talked about comments enough so our general thinking on it. But I think it's totally reasonable for there to be a pendulum swing, and maybe there's a slight overcorrection. And you read some blog posts that tell you the truth of the world, and suddenly, absolutely no comments ever that's the rule. And then, later on, you're like, you know, I could really use a comment here. And so you go that way, and then you decide you know what? Comments are good, and you start writing a bunch of them. And so it's sort of weaving back and forth. Ideally, you're honing in on your own personal truth about comments. But that's just an interesting example to me because I certainly wouldn't consider that one a regression. But then there's the bigger story of like, how do we approach building software? Ideally, that's what this podcast does at its best. We're not really a podcast about Rails or JavaScript or whatever it is we're talking about that week, but we're talking about how to build software well. And I think those core ideas feel like they're more permanent for me, or I feel like I'm changing those less. If anything, I feel like I'm ratcheting in on what I believe about good software. And there are some core ideas that I'm just refining over time, not done by any means, but it's that I don't feel like I'm fundamentally reevaluating those core ideas. Whereas I am picking up a new language and approaching a new framework and taking a different approach to what tools I'm using, that sort of thing. STEPH: Yeah, I agree. The core concepts definitely feel more important and more applicable to all the future situations that we're going to be in. So those skills that may fall into the regression category feel appropriate because we are focused on the bigger picture versus how well do I remember this rejects library or something that won't serve us as well? So I agree. I am often focused more on how can I take this lesson and then apply it to other tech stacks or other teams and keep that with me? And I don't want that to regress. But it's okay if those other smaller, easily Google-able skills fall to the side. [laughs] CHRIS: Wait, are you implying that you can't write rejects just off the top of your head or what's…? STEPH: I don't think I could write any rejects off the top of my head. [laughter] CHRIS: Fair. All right. You just go to rubular.com, hit enter, and then we iterate. STEPH: Oh yeah. I don't want to use up valuable space for maintaining that sort of information. Rubular has it for me. I'm just going to go there. CHRIS: I mean, as long as you have the index of the places you go on the internet to find the truth, then you don't need to store that truth. STEPH: A moment ago, you mentioned where Tom highlights that they have different views about code that they wrote, even code that they wrote just like a year ago. And to me, that's a sign of growth in terms that you can look back on code that you have written and be like, well, maybe this would be different, or maybe this is still a good idea, but the fact that you are changing and then reevaluating, I think that is awesome because otherwise, if we aren't able to do that, then that is just a sign of being stagnant to me. We are sticking to the knowledge that we had a year ago, and we haven't grown since then versus that already shows that they have taken in new knowledge. So then that way, they can assess should I be adding comments? When should I add comments? Maybe I should swing away from that idea of this is a hard line of don't ever do this. I think I just have to mention it because there is one that I always feel so deeply about, DRY. DRY is the concept that gives me the most grief in terms that people just overuse it to the point that they do make code very hard to change. All right, that's my bit. I'll get off my pedestal. But DRY and comments are two things [chuckles] that both have their places. CHRIS: I don't know if your experience was similar, but around DRY, I definitely have had the pendulum swing of how I feel about it. And I think again, that honing in thing. But initially, I think I read The Pragmatic Programmers, and they told me that DRY is important. And then I was like, absolutely, there will be no duplication anywhere, and then I felt some pain from that. And I've been in other systems and experienced places where people did remove duplication. I was like, oh, maybe it would have been better, and so I slowly got out of that mindset. But now I'm just in the place of like, I don't know, copy and paste not now, there was a period where I was like, just copy and paste everything. And then I was like, all right, I think there's a subtle line. There's a perfect amount of duplication, and that's the goal is to figure out that just perfect level. But for me, it really has been that evolution, and I was on one side, and then I was on the other side, and then I'm honing back in. And now I have my personal truth about duplication. STEPH: Oh, me too. And I feel like I can be a little more negative about it because I was in the same spot. Because it's a rule, it's a rule that you can apply that when you are new to software development, there aren't that many rules that are so easy to apply to your codebase, but DRY is one of them. You can say, oh, that is duplication. I know exactly what that is, and I can extract it. And then it takes time for you to realize, okay, I can identify it, but just because it's there, it doesn't mean it's a bad thing. Perfect duplication, I like it. CHRIS: Coming back to the idea of when we look back on our code six months, a year later, something like that, I think I believe the statement that we should always look back on our code and be like, oh, what was I doing there? But I think that arc should change over time. So early on in my career, six months later, I look back at my code, and I'm like, oh, goodness, what was happening there? I was very much a self-taught or blog internet-taught programmer just working on my own. I had no one else to talk to. So the stuff that I wrote early on was not good is how I will describe it. And then I got better, and then I got better, and I hope that I'm still getting better. And it's something that probably draws me to software development is I feel like there's always room to get a little bit better. Again, even back to that adage of it doesn't get any easier; you just go faster. Like, that's a version of getting better in my mind. So I hope that I can continue to feel that improvement and that ratcheting up. But I also hope that that arc is leveling off. There is an asymptotic approach to "good software developer." People in the audience, you can't see my air quotes, but I made air quotes there around good software developer. But that idea of I shouldn't look back probably this far into my career and look back at code from three months ago and be like, that's awful. That dude should be fired. I hope I'm not there. And so if you're measuring over time, what does your three months ago look back feel like? Oh, I feel like it's a little better. Still, you should look back and be like, oh, I probably would do that a little bit different given what I know now, what I've learned, but less so, I think. I don't know, what do you think about that? STEPH: Yeah, that makes sense. And I'm also realizing I haven't looked back at my code that much since I am changing projects, and then I don't always have the opportunity to go back to that project and then revisit some of the code. But I do agree with the idea that if you're looking back at code that you've written a couple of months ago that you can see areas that you would improve, but I agree that you wouldn't want it to be something drastic. Like, you wouldn't want to see something that was more of an obvious security hole or performance issue. I think there are maybe certain metrics that I would use. I think they can still happen for sure because we're always learning, but there's also -- I may be taking this in a slightly different direction than you meant, but there's also a kindness filter that I also want us to apply to ourselves where if you're looking back three months ago to six years ago and you're like, oh, that's some rough code, Stephanie. But it's also like, yeah, but that code got me to where I am today, and I'm continuing to progress. So I appreciate who I was in the past, and I have continued to progress to who I am today and then who I will be. CHRIS: What a wonderfully positive lens to put on it. Actually, that makes me think of one of -- We may be getting into rant territory here, but we talk a lot about imposter syndrome in the software development world. And I think there's a lot of utility because this is something that almost everyone experiences. But I think there's a corollary to it that we should talk about, which is a lot of people are coming into this industry, and they're like one year in, and the expectation that one year into a career that -- The thing that we do is not easy as far as I can tell. I haven't figured out how to make it easy. And the expectation that someone's going to be an expert that early on is just completely unreasonable in my mind. In my previous career, I was a mechanical engineer, and I went to school for four years. I actually went to school for five years, not because I was bad at school, but because I went to a place that had a co-op. And so I had both three different six months experiences working and four years of classroom education before I even got any job. And then I started doing things, and that's normal in that world. Whereas in the development world, it is so accessible, and I really feel like that's an absolutely wonderful thing. But the counterpoint of that is folks can jump into this career path very early on in their learning, and the expectation that they can immediately become experts or even in the short order I don't think is realistic. I think sometimes, when we talk about imposter syndrome, we may do a disservice. Like, it's not imposter syndrome. You're just new, and that's totally fine. And I hope you're working in an organization that is supportive of that and that has space for that and can help you grow in a purposeful way. In my mind, it's not realistic to expect everyone to be an expert a year in—end rant. STEPH: Well, I would love to plus-one your rant and add to it a little bit because I completely agree. I also love the phrasing that you just said where it's not that you have imposter syndrome; it's just that you are new and that team should be supportive of people that are new and helping them grow and level up. I also think that's true for senior developers in terms that you are very good at certain skills, but there's always going to be some area of the web or some area of software development that you are new to, and that is also not imposter syndrome. But it's fine to assess your own skills and say, "That's something that I don't know how to do." And sometimes, I think that gets labeled as imposter syndrome, but it's not. It's someone just being genuine and reflecting on their current skills and saying, "I am good at a lot of stuff, but I don't know this one, and I am new to this area." And I think that's an important distinction to make because I still want -- even if you are not new in the sense that you are new to being a software engineer, but you still have that space to be new to something. CHRIS: Yeah, it's an interesting, constantly evolving space. And so giving ourselves a little bit of permission to be beginners on various topics and for me, that's been an experience that's been continual. I think being a consultant, being a freelancer that impacts it a little bit. But nonetheless, even when I go into organizations, I'm like, oh, years in technology that only came out two years ago. That's pretty fresh. And so it's really hard to be an expert on something that's that new. STEPH: Yeah. I think being new to a team has its own superpower. I don't know if we've talked about that before; if we haven't, we should talk about, it but I won't do that now. But being new is its own superpower. But I do want to pivot back to where Tom mentioned that I have no way of measuring that improvement. And I think that's a really great thing to recognize that you're not sure how to measure something. And my very first honest suggestion if you are feeling that way is to go ask your manager and ask them how they are measuring your improvement because that is their job is to understand where you're at and to understand your path as a developer on the team and then helping you set goals. So since I'm a manager at thoughtbot, I'll go first, and I can share some ways that I help my team measure their own improvement. So one of the ways is that each time that we meet to discuss work, I listen to their challenges, and I take notes; I'm a heavy note-taker. And so once I have all those notes, then I can see are there any particular challenges that resurface? Are there any patterns, any areas where they continuously get stuck on? Or are they actually gaining confidence, and maybe something that would have given them trouble a couple of weeks ago is suddenly no big deal? And then I also see if they're able to unblock themselves. So a lot of what I do is far more listening, and I'm happy to then provide suggestions. But I am often just a space for someone to share what they are thinking, what they're going through, and then to walk through ideas and then provide suggestions if they would like some, and then they choose a suggestion that works best for them. And then we can revisit how did it go? So their ability to unblock themselves is also something that I'm looking for in terms of growth. And then together, we also set goals together, and then we measure that progress together. So it's all very transparent. And what areas would you like to improve, and then what areas would it be helpful for thoughtbot or as a consultant for you to improve? And then if I am fortunate enough to be on a project with them and see how they reason about quality and speed, how they communicate the type of features they're most comfortable to work on, and which tasks are more challenging for them, I also look to see do people enjoy working with them? That's a big area of growth and reflects communication, and reliability, and trust. And those are important areas for us to grow as developers. So those are some of the areas that I look to when I'm helping someone else measure their own improvement. CHRIS: I really like that, the structured framing of it, and the way that you're able to give feedback and have that as a constant, continuous way to evaluate, define, measure, and then try and drive towards it. Flipping things around, I want to offer a slightly different thing, which isn't necessarily specifically in the question, but I think it's very close to the question of how do we actually improve as developers? What are the specific things that we can try and do? I'm going to offer a handful of ideas. I'd be super interested to hear what your ideas are. But one of the things that has been really valuable for me is exploring different languages and frameworks. I, without fail, find something in every new language or framework that I then bring back to the core things that I'm working with. And I've continued to work with Rails basically throughout my career, but everything else that I'm doing has informed the way that I work with Rails and the way that I think about building code. As specific examples, functional programming is a really interesting frame of mind, and Elm as a language is such a wonderful, gentle, friendly, fun introduction to functional programming because functional programming can get very abstract very easily. I've also worked with Haskell and Scala and other languages like that, and I find them much more difficult to work with. But Elm has a set of constraints and a user-centric approach that is just absolutely wonderful. So even if you never plan to build a production Elm application, I recommend Elm to absolutely everyone. In terms of frameworks, depending on what you're using, maybe try and find the thing that's the exact opposite. If you're in the JavaScript space, I highly recommend Svelte. I think it's been very informative to me and altered a number of my opinions. A lot of those opinions were formed by React. And it's been interesting to observe my own thinking evolve in that space. But yeah, I think exploring, trying out, -- Have you ever used Lisp? Personally, I haven't, but that's one of the things that's on my list of that seems like it's got some different ideas in it. I wonder what I would learn from that. And so continually pushing on those edges and then bringing that back to the core work you're doing that's one of my favorite things. Another is… It's actually two-fold here. Teaching is one, and I don't mean that in the grand sense; you don't have to be an instructor at a bootcamp or anything like that but even just within your organization trying to host a lunch and learn and teach a concept. Without fail, you have to understand something all the better to be able to teach it. Or as you try and teach something, someone may ask you a question that just shakes the foundation of what you know, and you're like, wow, I hadn't thought about it that way. And so teaching for me has just been this absolutely incredible forcing function for understanding something and being able to communicate about it again, that being one of the core things that I'm thinking about. And then the other facet sort of a related idea is pairing, pair with another developer, pair with a developer who is more senior than you on the team, pair with someone who is more junior than you, pair with someone who's at the same level, pair with the designer, pair with the developer, pair with a product manager, pair with everyone. I cannot get enough pairing. Well, I can, actually. I read a blog post recently about 100% pairing, and I've never gotten anywhere close to that number. But I think a better way to put it is I think pairing applies in so many more contexts than people may traditionally think of it. People sometimes like to compartmentalize and like, pairing is great for big architecture design, but that's about it. And my stance would be pairing is actually great at everything. It is very high bandwidth. It is exhausting, but I have found immense value in every pairing session I've ever had. So, yeah, those are some loose thoughts off the top of my head. Do you have any how to get better protips? STEPH: Yeah, that's a wonderful list. And I'm not sure if this exactly applies because it's been a while since I have seen this talk, but there is a wonderful talk by Sandi Metz. I mean, all of her talks are wonderful, but this one is Go Ahead, Make a Mess. And I believe that Sandi refers to or highlights the idea of trying something new and then reflecting on how did it go? And that was one of the areas that I learned early on, one of the ways to help me progress quickly as a developer. Outside of the suggestions that you've already shared around lots of pairing that was one of the ways that I leveled up quickly is to iterate quickly. So I used to really focus on the code that I was writing, and I thought it needed to be perfect before my colleagues could review it. But then I realized that the sooner that I would push something out for feedback, then the faster I would get other more experienced developers' input, and then that helped me learn at an accelerated rate and then also ship more frequently. So I'd also encourage you to just go ahead and iterate quickly. We talk about with software in general, we want to iterate on the code that we are pushing up for other people to look at and then give us feedback on and then reflect on how did it go? What did we learn? What are some areas that we can improve? I feel like that self-evaluation is huge, and it's something that I know that I frankly don't do enough because one, it also prompts us to appreciate the progress that we have made but then also highlights areas where I feel strong in this area, but these are other areas that I want to work on. CHRIS: While we're on the topic of talks that have been impactful in our journeys of leveling up as developers, I want to quickly list three that just always come to mind for me: Avdi Grimm's Confident Code, Katrina Owen's Therapeutic Refactoring, and Ben Orenstein's Refactoring from Good to Great. There's a theme if you look across those three talks. They're all about refactoring, which is interesting. That tells you some stories about what I believe about how good software is made. It's not made; it's refactored. That's my official belief, but yeah. STEPH: Love it. That's also another great list. [laughs] For additional ways to level up, there are some very specific areas where it could be maybe do code katas or code exercises, or maybe you subscribe to certain newsletters, stay up to date with a language, new features that are being released. But outside of those very specific things, and if folks find this helpful, then maybe you and I can make a fun list, and then we could share that on Twitter as well. But I always go back to the idea of regardless of what level you're at in your career is to think about your specific goals, maybe if you are new to a team and you're new to software development, then maybe you just have very incremental goals of like, I want to learn how to write a test, or I want to learn how to get better at PR review or something very specific. But to have real growth, I think you have to first consider where it is that you want to go and then figure out a way to measure to get there. Circling back to some of the ways that I help my teammates measure that growth, that's one of the things that we talk about. If someone says, "Well, I want to get better at PR review," I'm like, "Great. What does that mean to you? Like, how do you get better at PR review? How can we actually measure this and make it something actionable versus just having this vague feeling of am I better?" I think I've ended up taking this a bit more broad as you were providing more specific examples on how to level up. But I like the examples that you've already provided around education and then trying something outside of your comfort zone. So what's coming to mind are more of those broad strategies of goal setting. CHRIS: I think generally, you need that combination. You need how do I set the measure? How do I think about improvement? And then also ideally a handful of tactics that you can try out. So hopefully, we provided a nice balanced summary here in this episode. And hopefully, Tom, if you're listening, you have gotten some useful things out of this conversation. STEPH: Yeah, this was fun. We managed to take this topic and make a whole episode out of this. So thanks, Tom, for sending in such a great topic. CHRIS: Frankly, when I saw the topic, I was certain this was going to happen. [chuckles] This was an obvious one that was going to fill up the time for us. But yeah, with that, I think we've probably covered plenty here. Should we wrap up? STEPH: I'm sure there's more, but sure, let's wrap up. CHRIS: The show notes for this episode can be found at bikeshed.fm. STEPH: This show is produced and edited by Mandy Moore. CHRIS: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or even a review in iTunes, as it really helps other folks find the show. STEPH: If you have any feedback for this or any of our other episodes, you can reach us @_bikeshed or reach me @SViccari on Twitter. CHRIS: And I'm @christoomey. STEPH: Or hosts@bikeshed.fm via email. CHRIS: Thanks so much for listening to The Bike Shed, and we'll see you next week. Both: Byeeeeeee. Announcer: This podcast was brought to you by thoughtbot. Thoughtbot is your expert design and development partner. Let's make your product and team a success.Support The Bike Shed
undefined
May 18, 2021 • 41min

293: Sportstaphors

On this week's episode, Chris and Steph share a speedy step to restart your rails server and chat about accessibility improvements and favorite a11y tools. They also dive into a tale of database switching and delight in a new Rails query method that returns orphaned records. Restart Rails server via tmp/restart.txt WebAIM: Constrast Checker IBM Equal Access Accessibility Checker axe™ DevTools AccessLint Assistiv Labs An introduction to macOS Head Pointer Rails date_select Rails strong_migrations Ruby RBS Sorbet - Ruby Type Checker Scout APM Rails 6.1 adds query method missing to find orphan records Transcript: STEPH: People put microphones in front of us. That is their fault, not ours. We just show up. Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Steph Viccari. CHRIS: I'm Chris Toomey. STEPH: And together, we're here to share a bit of what we've learned along the way. Hey Chris, happy Friday. CHRIS: Happy Friday. STEPH: How's your week been? CHRIS: It's been great. I did something that is wildly overdue, but I got a new chair and one day in. But it's also a very familiar chair because it's basically the same -- I think it's the same model as we had at the thoughtbot office. And it's nice to have a chair that is reasonable. And I think my old chair was maybe ten years old or something, deeply embarrassing and absurd like that for such a critical piece of infrastructure in my house. STEPH: I mean, I guess depending on if it's a good chair. I don't know what the lifespan is of a good chair. [laughs] CHRIS: I would not describe it as such. STEPH: [laughs] CHRIS: I think it was like $100 at Staples. It was a fine chair. It served me well for many years. I'm very slow and cautious with what I consider to be large-scale purchases. I hate the idea of having a thing that I've spent a bunch of money on, but I don't actually like. And these are very solvable problems. But I just tend to drag my feet and over-research and do all those sorts of things. And so finally I was just like, nope, we're going to get a chair, got a chair. Cool. Now I have a chair, and it's good. It's got all of the adjustments, which is what makes it very nice. I'd say Steelcase Leap is the model for anyone that's interested. STEPH: That's funny. I tend to do the same thing. I tend to drag my feet until I get desperate enough that then I'm forced to make a decision and buy something. I do have an oddly specific question. Do you like chairs with or without the arms? CHRIS: Oh, with the arms. STEPH: Really? CHRIS: Yeah. STEPH: I am team, no arms. CHRIS: Where do your arms go if there are no arms to put on the chair? STEPH: They're always on my lap or on my keyboard. So I just don't rest them on the armrest. CHRIS: Interesting. I feel like that would put -- I've definitely had small bouts of RSI strain fatigue in my forearms. And so I'm very purposeful with how I'm bracing my wrists. I have a little wrist rest that I put my hands on when I'm using my keyboard because the keyboard is slightly raised up because I have a nonsense mechanical keyboard, of course. STEPH: Delightful, not nonsense. CHRIS: Yeah, I love it. I would never trade that in, but I have to make it work and not actually sacrifice my body for a clackety keyboard. [chuckles] But yeah, I think I need some more support for my arms; otherwise, there's too much pressure on my wrists, and things are breaking at weird angles, and that's been my experience. I'm intrigued by the free-flying no arms on the chair approach that you're talking about. This particular model has nine degrees of freedom on the armrest. So I'm able to bring them in and forward and at the exact right height so that they perfectly meet my arm where it would naturally be, and that seems good. That seems like the thing that I want. STEPH: That makes a lot of sense. But yeah, I'm team no arms. Every time I have them, I can't get them at the right comfortable spot. And I like the freedom of where I can quickly get up and out of my chair and not have arms in the way, which sounds like a very small improvement in my life, but yet it's what I want. CHRIS: I just like the idea of you sitting there and being like, I need to be able to make a quick escape at any moment; who knows what's going to happen? And I need to be able to run the other way. STEPH: If there's a gnarly bug, I got to be able to run. I can run away quickly as possible. [laughs] CHRIS: But in other news, so yeah, new chair that's great. I also recently embraced something in the Rails world that I have known about I think for forever for the entire time that I've worked in Rails, but I've never really used it, which is the tmp/restart.txt file, which my understanding of it is if you touch that file, or if that file exists, Rails will recognize that and will restart the server in development mode. And I think I've always known about this, but I've never used it. And I recognized recently that either I was trying to use a gem that I'd added to the Gemfile, but my server didn't know about it. So I was going to do the thing that I normally do, which is kill the server and then restart the server so CTRL+C and then CTRL+P in my terminal and hit enter, and then wait a bunch of minutes and get distracted, all of the bad things there. And I was like, wait; I remember that there's a thing here. And I don't know why I haven't been doing this for years. It's so much better. I actually went the one step further, and I configured a tmux binding so that tmux prefix and then R will touch tmp/restart in the local directory of the tmux session. That's been very nice, I will say. So I keep moving between branches. And I have environment variables that I need to reload or config initializers that I've made a change to, and I want to load that in. Or a gem that I've added to the Gemfile and I've now installed, but the server doesn't know about. All of these are just so quick now. And why wasn't I doing this the whole time? STEPH: I saw that you mentioned this on Twitter a couple of days ago, and I was so excited. But at the moment, I bookmarked it for later, but I didn't have time to actually really check it out. And I'm so glad you're bringing it up because I actually just tried it while we're chatting. So I started up my Rails server, and then I did the touch tmp/restart, and this is amazing. This is awesome. I'm very excited. CHRIS: It just does the thing. STEPH: It just does the thing. CHRIS: Yeah, it's so nice. [laughter] STEPH: Yeah, this is fabulous, almost as good as the pending migrations button. Not quite because that's a very special button, but this is also up there. CHRIS: It's a very, very good button. [laughs] I really got very enthusiastic about that button, didn't I? But I stand by it. It's a very good button, and this is a very good file. But this file has existed for so much longer, this workflow. And so many times, I have restarted the server and have been annoyed that I had to do it. And my brain just had this answer available. I didn't read a blog post and relearn this thing. I've always known it. And it was this one particular time that my brain was like, "Hey, you know how we're always annoyed by having to restart the server? You know there's another answer, right? I know that you know it because I'm your brain, and I'm telling you this." [chuckles] This is my weird internal monologue. So I'm very happy to be on the other side of that and to share that with as many people as possible who may be, like me, know about this but haven't actually leaned into it, small things that make the Rails world very nice. STEPH: Well, I'm glad you internalized it and then surfaced it because this is not something that I had heard of before. So I'm very appreciative of it. This is going to be great. CHRIS: Happy to share the wealth. But yeah, that's some of the stuff that's been up in my world. What's been going on in your world? STEPH: It's been a rather busy week. Most of that week has been focused on improving the accessibility of existing pages and forms, which is an area that I don't get to spend a lot of time, but each time I do, I really would like to be a pro when it comes to accessibility. Well, that's probably a long journey to become a pro. I would like to become more knowledgeable in terms of accessibility because it is so important. And while working specifically on these accessibility tickets and improvements, I've discovered a few helpful tools that I figured I'd share here. So one of the tools that I've started using is a color contrast tool. It's created by WebAIM or web accessibility in mind. And a number of our headers in our application have a white font that's on a background color, and we were getting warnings that this isn't very accessible and that there's not enough contrast. So with the Contrast Checker, you can provide the foreground color and the background color, and then it's going to tell you that contrast ratio. So if you're wondering, well, what's a good ratio? That's a great question. And the W3C Accessibility Guidelines recommend a contrast ratio of 4:5:1 for normal texts and 3:1 for larger texts. Larger text is anything that's typically around 18 px, 18.5 px, or larger. So the color contrast tool has been really helpful because then that's been very easy that we give the blue that we're using, and then we can just darken it a bit to improve that contrast. And then we apply that everywhere throughout the app. The other tool that I've been using that I'm really excited about it's a browser extension called the IBM Equal Access Accessibility Checker. Is that something you've heard of or used before? CHRIS: I have not heard of that. STEPH: I would love to know what you currently use for accessibility, and I'll circle back to that in just a moment. But for this particular browser extension, I'm pretty sure they have it for multiple browsers. I'm using Chrome. So I've installed the Chrome extension. Once you have it installed, you can open up the browser console and then tell it to scan the page that you're on. And then it generates a really helpful report that has all the high-level offenses, which are called violations. It also has warnings and recommendations. And then if you click on a specific issue, then the right-hand area shows a detailed description of the offending HTML, what's wrong, why it's important, which I really appreciate that part, and then a couple of examples of how to fix it. So it's been a really nice way as we are working to improve the accessibility of form. We actually have feedback to know that we are making progress and that we are improving the accessibility of that particular page. And then circling back, I'm curious, do you have any particular tools that you use when it comes to improving accessibility or any standards that you tend to follow? CHRIS: Yeah, this is a very apropos question. I'm working on a new project now, and accessibility is definitely something that I want to consider on every project, but it's all the more so important for this particular project, or it's something that we're, as a team, collectively really embracing early on and wanting it to be a core focus of how we're building out the application. That said, I will say that I'm accessibility aware but far from an expert and still very much learning. But some of the things that I have used are the axe DevTools. I forget what the acronym actually stands for there, but we can certainly include a link in the show notes. But those are DevTools that allow you to, I think, do some color contrast checking actually in the browser just right there, which is really nice. There's also AccessLint, which is a project that scans pull requests and, where possible, does static analysis of the HTML. And that's actually by some former thoughtboters. So it's always nice to have that in the reference. There's actually a new tool that I've been looking at. I haven't actually tried it out yet, but it's from a company called Assistiv Labs, Assistiv without an E interestingly at the end. But their tool is, as far as I can tell, it allows you to use screen readers and other tools but across various platforms so that you sort of turn on -- It's very similar to if you've ever used an emulated Internet Explorer session because you're working on not an Internet Explorer machine, but you want to make sure your site works in Internet Explorer, same sort of idea, I believe. But it allows you to do the same approach for accessibility. So using a screen reader or using what the native accessibility technologies are on various platforms and being able to test across a wide range of things. So that's definitely one that I'm going to be exploring more in the near future. And beyond that, there are a handful of static analysis-based tools that I've used. So Svelte actually has some built-in stuff around accessibility. Because they are a compiler, they can do some really nice things there, and I really appreciate that that is a fundamental concern that they've built into the language, and the framework, and the compiler, and all of that. And I've also used ESLint A11y, which is the acronymnified version of the word accessibility. But that again, static analysis, so it can only go so far. And unfortunately, accessibility is one of those things that's hard to get at from a static analysis point of view, but it's still better than nothing. And it allows you to have a first line of defense at the code as you're authoring it. So that's a smattering of things. I've used some of them. I'm interested in others of them. But this is definitely an area that I'm going to be exploring a bunch more in the near future. STEPH: I like that you brought in the static analysis tools because that's the other thing that's been on my mind as we're making these accessibility improvements; that's been great. And we can run this particular browser extension to then check for warnings or issues on the page but then looking out for regressions is on my mind. Or as we're introducing new pages and new forms, how do we make sure that those are up to standard if someone forgets to run that extension? So I really like the idea of -- There's AccessLint that you mentioned, which will then scan PRs for accessibility improvements. That sounds really great. I'm also intrigued if there's a way to also -- I don't know if maybe tests are a good way to also look for any sort of regressions in terms of changes that we've made to a page. I don't know what those tests would look like. So I'll have to think on that some more, but I think some people at thoughtbot have thought about it. CHRIS: My understanding is the testing library suite of testing frameworks, so it's like testing library React, testing library, et cetera. It's primarily used in the JavaScript world, although there is Cypress, which is more of a browser-level automation. But it fundamentally works from not exactly an accessibility but a -- It doesn't allow you to do DOM selectors. It really tries to hide that. And it says, "No, no, no. You're not going to be digging in and finding the class name of this thing because guess what? A user of your application can't do that." What we want are – Typically, it's like find by label or find by things that are accessibility available or just generally available to users of your application. So whether it's users that are just clicking around or if they're using any sort of assistive technology, the testing library framework forces you in that direction. You can't write a test if your code is inaccessible tends to be the way it plays out, and it really nudges you in that direction. So it's one of the things that I really love about that. And I actually miss it when I'm working in a Capybara test suite because, as far as I know, there is not a Capybara testing library variant of it. And really, at the end of the day, it's just a bunch of functions to allow you to select within the context of the page. But again, it does it from that standpoint, and I'm all about that. STEPH: Yeah, that's really nice. That's a good point. Yeah, I don't think Capybara has that explicitly. I know that you have to use specific parameters. Like, if you want to access something on the page that is hidden, that's not something you can just do easily. You have to specify: I'm looking for an element that is hidden on the page. But otherwise, I don't think it goes out of its way to prevent you from doing that. There is an article that this conversation about accessibility made me think of. There's a really fun blog post written by Eric Bailey, who has been or who is a champion of accessibility at thoughtbot and has written a lot of great content around making the web more accessible. And in addition to publishing with the thoughtbot blog post, he has written for a number of publications. And the article that comes to mind that he published on the thoughtbot blog posts is An Introduction to macOS Head Pointer, and we'll link to it in the show notes. But he does a great job walking through what the head pointer is on macOS and then how to use it. And he uses his eyebrows to essentially move the mouse and then click on certain buttons or click on certain links on the screen. And it's incredible. So if you need a little bit of accessibility and joy in your life, I highly recommend checking out that article. CHRIS: Yeah. Eric has absolutely just been such a fantastic champion of accessibility. And he's definitely someone that I think of constantly as being -- I think he's involved with the Accessibility Project. He writes on CSS Tricks. He's around the internet just being the hero we need because accessibility is such a critical thing. And I'm a deep believer in the idea that accessible applications are better for everyone. And I so appreciate the efforts that he's putting in out there. Thanks, Eric. STEPH: Thanks, Eric. And then, on a slightly separate note, I have a slight complaint that I'd like to file. And this one is with Rails specifically. And I'm filing this complaint with the understanding that I'm also very spoiled in terms of Rails does so much, and I'm very appreciative of how much Rails does for me and for us. But specifically, while working on accessibility for a date of birth form field, so it's a form field with three different selects, so you have your month, day, and year. And while creating this, there's a very helpful Rails method that's called date_select, where then you can generate all three of those select fields. And you can even specify the order in which you want them generated, but this particular function doesn't have a way to make it accessible. So you can generate a label for each option that's in the select dropdown. And there's no parameter. There's nothing you can pass through. It doesn't automatically generate it for you. So I was in a spot where I was updating a form that's using the Rails date_select. I can't use date_select and make an accessible dropdown selection for date of birth. So instead, what I had to do is I had to split it out. I had to move away from using date_select, and instead, I'm using select_month and then select_day and select_year because from there, I then can pass in; in my case, I'm using aria-label to provide a label because I don't actually want the label to show up on a screen, which could be another accessibility concern because we do have the birth date label for those three sections. But then we still want at least each text field to have a label, even if it's only visible to screen readers. So then that way, if someone is selecting from year, they understand they're selecting from year or for month they're selecting from month. So by using select_year and select_day and select_month, I could specify the aria-label as month, day, or year, but I couldn't do that with the date_select. And I just realized that there's probably a number of date of birth forms out there that aren't accessible because us Rails developers are leveraging this existing method. So it just seems like a really good opportunity to improve date_select to be able to pass in a label or generate one automatically. CHRIS: Wow. I'm surprised that's the state of the art that we're currently at. I really wonder if there have been conversations or if there are fundamental limitations because I'd be surprised if such a core piece of the Rails world someone hadn't brought this up in the issues. What's the story there? Because I'm guessing there's a story there. Although flipping it around, I wonder -- I've never loved that input sequence; as an aside, like three different selects, that's not how I think of my birthday. My birthday is one thing. It's not three things that we smash together. But I wonder are we at a point now where IE 11 usage is so small that we can use a native date_select input and then have a polyfill -- And then I start to trail off because I don't know what the story is for. Like, I think Safari doesn't do a great job, and I forget where it's at right now. And what about mobile Safari? And wouldn't it be nice if everything was just easy and everybody kept up? [laughter] But that's an aside. But yeah, that's part of my question here, is like, can we just not use that thing at all? Like, the three select dropdown version of picking a date of birth because, man, that's my least favorite way to do it. STEPH: Yeah. I'm with you. I'm also curious if there is a story behind this and also if anyone has a different opinion, and I'd love to hear it. Because this has been my experience in digging through the docs is I would date_select, and I could not find a way to pass in a label or have one generated to make it accessible. So then that prompted me to use the three different methods, which, by the way, is fine. It made me stop and pause to think this is the method that most people recommend the usage of in terms of creating those three different select fields for a date of birth or for any particular date that you're supplying; it does not have to be a date of birth. So it also surprised me that then we couldn't make it accessible. So yeah, I was a bit miffed in the moment. [laughter] I had to walk myself back and be like, well, if I want to make the world a better place, I should help make the world a better place. And that started with changing the code in this codebase. But then also it means looking into Rails to see if there's an improvement that I could help with there. CHRIS: This is what we do: we take our moments of miffed, and we turn them into positive action in the world. This is what we want to see. [chuckles] STEPH: I figure the least I can do is share a blog post or something on Twitter that shows what it was before and then using the new date_select functions because that is reasonable, although working with a form is a bit different. It got a little tricky there in terms of making sure that each value for each select field is still being passed within the expected nested parameter. And some of that was available in the public API for select_year and select_day, but it's not as well documented. So I'm like, well, this seems to be intentionally public, but it's not documented, so I feel a little nervous about using this. Yeah, that's it. I just wanted to share my annoyance with Rails [laughs] or the fact that it made me work so hard to have a date of birth field. CHRIS: You joke, but that's a lot of why we use Rails is because we want these common regular things that we're doing to be as easy as possible, to require as little code on our part as possible but also this sort of thing like there's a lot of subtlety and stuff. Accessibility is one of those things that I want a framework that has security, and accessibility, and ease of use, and all of these things just baked in, so I don't have to think about it every time. It turns out having a date of birth, or generically any date field, is going to come up in web applications a lot, it turns out. And so having all of that stuff covered is frankly what I expect of a framework like Rails. So I'm totally on board with your being miffed here. STEPH: Yeah. Those are all really valid points. So I'm with you. What else has been up in your week? CHRIS: Well, we've been leading up to this, I think, for many weeks. I did a Rails 6.0 upgrade a while back, and a big reason for that was partly just to get on the current version of Rails but also because I wanted to open the door to database switching, and finally, this week, I tackled it. And let's tell a tale because there was a bit of an adventure, if we're being honest. Fundamentally, all the stuff there makes sense. I'm happy with the end configuration, but there was a surprising amount of back and forth. I broke the app more times than I want to actually announce on a podcast, but I broke it only for a brief period of time. It's fine. It's fine. Everybody's fine. [laughs] I feel a little bad about it, but these things happen. But yeah, it was interesting, is how I'll describe it. So fundamentally, Rails just has nice configuration for it. So at a high level, you're introducing your config/database.yml. Instead of it just being production is this URL, you now say primary is this replica or follower, whatever you want to name it is this. So you have now two configurations nested within your production config. And then in your ApplicationRecord, you inform Rails that it connects_to, and then you define a Hash for writing goes to the primary, reading goes to the follower. And you have to sync those up with the thing you just wrote in the config/database.yml but fundamentally, that kind of works. That makes it possible in your application to now switch your database connection. The real magic comes in the config environment production file. And in that, you specify that you want Rails to use a database resolver that says GET requests go to the replica, and anything that is not a GET request goes to the primary. So anytime you're writing data, anytime you're changing data within the system, that's going to go to the primary. And there's also a configuration that, as far as I can tell, gives a session affinity. So for the next two seconds after that, even if you make a GET request subsequently right following it, so you make a right, you POST, and then immediately after that, you do a GET. Like, you create an object, and then you get redirected to the show page for that object, Rails will continue to go to the primary. I think it's probably using a cookie or something to that effect, but you can configure that time span. So you can say like, "Actually, we see that our follower lags behind a little bit more, so let's give it a five-second timeout where all requests for that user will then go to the primary." But otherwise, once that timeout clears, then you're going to switch back, and you're going to go to the follower, and all GET requests will happen to the follower. And that's the story. You have to configure that, and then it works. STEPH: I always love when you start these out with "I have a tale to tell." I very much enjoy these adventures. And you also answered my question in regards to if you immediately just created something, but then you do a fetch that's very close to after you just created it and how that gets rendered. So that was perfect. CHRIS: Frankly, the core configuration is very straightforward, and it's very much in line with what we were just talking about of; this is what I want from Rails: make this thing very easy, hide the details behind the scenes. But as I said, there's a bit of a tale here. So that was the base configuration. It sort of worked but then immediately upon deploying it to production -- So we deployed it to staging first just to test it out. Staging was fine, as is often the case. Increasingly, I'm leaning into Charity Majors' idea of you got to test in production. You're testing in production even if you say you aren't. So once it got to production, we started seeing a bunch of errors raised or a handful of errors. And they were related to a handful of controller actions, which are GET requests, so they're either show or index, but in them, they were creating, or they were trying to create data. And so we were getting an error that was read-only connection error or something to that effect, ActiveRecord read-only, I think, was the error class. And that makes sense because I told it, "Hey, whenever you get a GET request, you're going to use that follower." But the follower is a read-only database connection because it's a follower, and so it was erroring. It was interesting because when this happened, I was like, wait, what? And then I looked into it. And it's frankly fine at all the levels. It is okay to create a record in a GET request as long as that creation is idempotent. You create if it doesn't exist, and then from there on, you use that same one. That still fits within the HTTP rules of idempotents, and everybody's fine with that, except for the database connection. Thankfully, this is relatively easy to work around. You just need to explicitly within that controller action say, "Use the right database, use the primary." And the way I implemented that, I wrote a method within ApplicationRecord that was with right DB connection, and then it takes a block, and you yield to that block. It's basically just proxying to another similar thing. And it's very similar to wrapping something in a transaction; it sort of feels like that. It's saying just for this point in time, switch over and use the primary because I know that I'm going to be having some side effect here. STEPH: Wow. That's so fun. I'm sure it was not fun for you. But as me hearing the story later, that's fun in regards to I hadn't thought about that idea of you're telling all the GETs you can only go to the read, and now you're also trying to create. I am feeling nervous in terms of local development. So if you're working on a new controller and if you have a fetch or GET action, but you're also creating something, you haven't seen another controller that is demonstrating that strategy that needs to be used. Is it just going to work locally? I imagine it does because it was working for the other code that you were running that didn't yet have that strategy in place. So I'm feeling nervous in terms of someone could easily miss that. CHRIS: I think there are a couple of different questions in what you just said. So let me try and answer all of the ones that I think I heard. So for local development, your database/config.yml is still going to be the same as it was. So you're just connecting to database name_development. There's only one of them; there's no primary follower. So this is a case where you have a discrepancy between production and development, which is always interesting. And maybe that's something to poke at because ideally, I want as little gap there as possible. But this is one of those cases where I'm like, eh, I don't think I'm going to run two databases locally and have one be a follower. That feels like too much to manage. Under the hood with that right DB connection method that I talked about where you want to explicitly opt-in, in the case that we're in development, I just yield directly to the block. So instead of doing the actual database switching at that point, the method is basically saying, "If we're in production, then switch to the primary and yield and if we're not in production, then just yield." And so it'll just run that code, and it'll connect to the only database. More generally, I have the connects_to configuration; I wrapped that. So that's in ApplicationRecord where you're saying, "Hey, connect to these databases based on this logic," that is wrapped if we're in production check as well. And the same thing in the top-level configuration that says -- We're getting ahead of ourselves in the story because this is the end state that I got to. It's not where I started, and I screwed some stuff up in here, but basically, all of the different configuration points, my end result was to wrap them in a check that we are in production. STEPH: Okay. Sorry if I rushed your story. I was already thinking ahead to how could we accidentally goof this up? That makes a lot of sense for the method that's with right DB connection, that method that then it's going to check if we're in production, then we can use a primary follower strategy; otherwise, just use the database that we know of. So that helps a lot in answering those questions. And then we can pause and then get to my question later. But my other question that I'm curious about is what helps us prevent the team from making this mistake in terms of where we're adding a new controller, we add a new GET action, and we are also creating data, but then someone doesn't know to add that strategy that says, "Hey, you are allowed to go to the primary to also get data but also to write data too." And I'll let you take it away. CHRIS: I don't know that I have a great answer to that one if we're being honest. As I saw this, it was very easy to find -- I think there were three controller actions that had this behavior in the system that I was working on. They all threw errors. It was very easy to just wrap them in this extra method and fix that, and then we're good, and I haven't seen that error again. As for preventing it from new instances of this behavior, I don't have a good answer other than potentially you share this information within the team and then PR review. Ideally, someone's like, "Oh, this is one of those things you've got to wrap it in the fancy database switching logic." Potentially, and I don't actually think this would be possible, but there's a chance that RuboCop or other static analysis type thing could look inside any index or show action and say, "I see a create or an update or any of the methods." But again, Rails is so hard to do static analysis on that I would be surprised if were actually feasible to do that in a trustworthy way, probably worth a poke because this is the sort of thing that can easily sneak out. But potentially, my answer is, well, it'll blow up pretty loudly the first time you do it. And then you'll just fix it after that, which is not a great answer. I'm open to that being a mediocre answer at best. STEPH: [chuckles] Yeah. That's a fair answer. Just because I pose a question, I don't know if there necessarily is a great answer to it right away. And disseminating that information to the team to then having the team be able to point that out also sounds very reasonable but then still hashes that danger of someone overlooking it. The static analysis is an interesting idea, sort of like strong migrations. As you're introducing a new migration, strong migrations will do a wonderful job of showing you concerns that it has with the migration that you've added. And this is all just theoretical dreams and hopes because, yeah, that would help prevent some of those scenarios. CHRIS: It's interesting now that this is the second time we've discussed static analysis in this very episode. Clearly, it's a thing that I want more of in my world, and yet I work in languages like Ruby that are notoriously difficult to perform static analysis on. STEPH: I had a moment today writing a method that was currently just returning a string each time but then I was about to update that method. I was looking for a way like, well, maybe I don't always want a string. Maybe I actually want a Boolean here. But in the other case, I want a string. And the person I was pairing with they're like, "You could return -- [inaudible 29:31] Boolean in one case and then a string in the other case. Like, this is Ruby." [laughs] I was like, true, but I feel bad about it, and I don't love it. And we just had a phone conversation around that. If you're in the Ruby world following the more functional programming or type strictness and where you're returning specific types or trying to return a consistent type, it's ideal. But then also in Ruby, it's like it's Ruby, so sometimes you can finagle the rules a little bit. CHRIS: YOLO, as they say. STEPH: [laughs] CHRIS: Yeah, I'm definitely interested to see where projects like Sorbet and...I forgot what the core Ruby typed thing in Ruby 3.0 is called, but either of those. I'm really intrigued to see where they go and how the Ruby community either adopts or doesn't. I wouldn't be surprised if that were part of the outcome there. I've been impressed with the adoption of TypeScript and JavaScript, which is also a very, very free language, not quite to the degree that Ruby is. But yeah, it remains to be seen what will happen on those fronts. But continuing back to our saga, so we've now had the read-only error, we've fixed those, just wrapped them in blocks, and said, "Explicitly connect to the primary." So the next thing that I did after that, I realized that my configuration was a little bit flimsy is probably the best word to describe it. I was explicitly creating a new environment variable with the URL, the Postgres URL of the follower. And so I was using that environment variable to define where the URL like the Postgres URL of the follower database -- But I realized if Heroku comes in and does any maintenance on that Postgres instance, it's possible that the AWS IP address or other details of it will actually change and so that Postgres URL will no longer be valid. So that's one of the things that I rely on Heroku for, is to maintain my databases for me. But they will update, say, the DATABASE_URL environment variable if they change out your database. But now, I had broken that consistency. And so I'd set us up for somewhere down the road this will break, and I realized that because Heroku reached out and said, "Hey, your follower database needs maintenance." And I was like, oh, no. So, I tried to get it from -- It turned out, in this case, it didn't actually change. They were able to swap it out in place, but I wanted to add a little bit of robustness around that. And so I actually reached out, and Dan Croak, former CMO of thoughtbot, actually had written a wonderful blog post about how to configure this and particularly how to configure it in the context of Heroku. And he described how to use the Heroku naming scheme for the environment variables. They happen to have colors in them. So it's like Heroku Postgres cyan URL or orange URL or purple URL. And so he defined a scheme where you set an environment variable that describes the color, and then it can infer the database URL environment variable from that. And then went the one step further to say, "If that color environment variable is set, then treat as if we are configured for database switching. But if it is not set, even if we're in production, pretend like we don't have database switching," which that was another nice feature that I hadn't built in the first place. When I first configured this, I just said, "Production gets database switching. And if we're in production, then database switching is true," but that's actually not something that I want. I want to be able to say, "Upgrade our follower," at some point or do other things like that. And so I don't want to be locked into database switching on production. So that was a handful of nice configurations that I wanted to get to. Unfortunately, when I tried to deploy that switch, man, did it break. It broke, and then I was like, oh, I see I did something wrong there. So then I tested again on staging. Staging was fine. And then I went to production, and it broke again. And this happened like three times in one day. I felt like a terrible programmer. I had no idea what I was doing. Turns out that staging and production had different environment config files, and so their configurations were fundamentally different. They also had a different configuration for the database level. So one of the things I did as part of this was to clean those up and unify them so that staging was production with some environment variables to config it, but identically production, which is definitely a thing that I believe in, and I want basically all the time. I don't think we should have a distinct staging environment config that is wildly different. It should only vary in very small ways, basically just variables that say, "This is where the database is for staging," but otherwise be exactly configured as production. So I eventually got on the other side of that, fixed everything, have a nicely Heroku-fied color-based environment variable scheme, which is a bit of a Rube Goldberg machine, but it works. And I was able to hide that config in one place. And then everything else just says, "If there is a database follower URL defined, then use it." But yeah, so that was the last hard, weird bit of it. And then the only other thing that I did was I realized that this configuration was telling the Rails server how to behave, but there are also background jobs. And this application actually happens to have a ton of background job traffic. And so I did a quick check of those, and there were a handful of background jobs that were read-only. A lot of them were actually sending data to external systems, so to analytics or other email marketing or things like that. And so constantly, as users are doing anything in the application, there are jobs that are queued that aggregate some information, maybe calculate some statistics, and then push it to another system. But those are purely read-only when those jobs execute. And so I was able to add another configuration which said, "Use the read-only connection and configure that to wrap those particular sidekick jobs." And with that, I think I have a working database switching configuration that will hopefully give us a lot of headroom in the future. That's the idea, that's the dream, but we will see. STEPH: That is quite the saga between having GET requests that create data and then also the environment inconsistencies, which is a nice win that then you're able to improve that to make those environments more consistent. And then the background jobs, yeah, that's something that I had not considered until you just brought it up, and then being able to opt-out of the database switching sounds really nice. In regards to moving in this direction, you're saying gave you a lot of headroom for this; when it comes to monitoring performance, is there anything in place to let you know how it's doing? CHRIS: I love that I knew that this was going to be your question. I love that this is your question because it's a very good question. And unfortunately, in this case, it's actually somewhat unsatisfying. So as is my typical answer for this, we're using Scout as the application performance monitoring tool on this. And I was able to go in and monitor what it looked like a week ago, what it looked like after I made the change, and it was a little better. And that's all I can say about it. But that's fine. The idea with this, and at least in the way I was thinking about it, is this should get better at the margins. On the days where we have a high spike in traffic, those are the days where the database is actually working hard. They shouldn't make the normal throughput of the application that much higher in the regular case; it's for those outlier instances. To that end, though, I did analyze it. And so the average response time got 2% to 3% better in that week-by-week comparison, which was fine. The 95th percentile response time, so starting to get out to those margins, starting to get to the long tale of where stuff gets -- a couple of requests came in at the same time, and the application had to try a little harder, those got 8% to 9% better. That shape of improvement where for most requests, nothing really changed for some of the requests that used to be a little bit slower; those got a little bit better. That's the shape of what I would hope to see here. And it remains to be seen. This application has particular traffic patterns where they'll encourage a lot of users to be using the app at the same time. And historically, those have been somewhat problematic, and we've had to really work to shore up the performance in those cases. That's where I'm really interested to see how this goes. It would be hard to replicate those traffic patterns at this point. So I don't have a good way to really stress test this, but my hope is that for those cases, things will just hum along and be happy. STEPH: That makes a lot of sense and something that would be hard to measure, but the fact that you already see a little bit of improvements that's encouraging. CHRIS: But yeah, certainly, if I get a chance to see what that looks like in the near term, I will respond back and let you know how this has played out. But overall, now the configuration seems pretty stable. I think we're in a good spot. Hopefully, we won't have to do too much proactive management around this. And ideally, it just buys us a little bit of headroom. So that is certainly nice. But with that, with your wonderful question getting to the heart of the issue, I think that wraps up the saga of the database switching. STEPH: Well, I appreciate you sharing that saga. That's really helpful. I've been very excited to hear about how this goes because I haven't gotten to work on a project that's going to use database switching just yet. And now I know all the inside baseball. I'm trying to use sports metaphors here as to how to do this for when I get to work with database switching. CHRIS: Sports de force. CHRIS: Along the lines of new stuff, there is something I'm excited about. So in juxtaposition to my earlier statement or my earlier grievance where friends don't let friends use date_select in regards to trying to keep the web accessible, I do have some praise for something that's being added in Rails 6.1 that I'm excited about. And it's a really nice method. It's a query method that can be used to find orphan records. So if I'm writing a query that is then looking for some of these missing records, so if I have my table -- I didn't come with a great example today, so let's just say we have like table A and then we're going to left_joins on table B. And then we're going to look for where the ID for table B is nil, so then that way we find where we don't have that association that it's missing. And so left_joins does this for us nicely. And then I always have to think about it a little bit where I'm like, okay, I want everything from table A, and I don't want to exclude anything in table B if there's not a match on the two. And so then I can find missing records that way or orphaned records that way. The method that's being introduced or has been introduced in Rails 6.1, so anyone that's on that new-new, there is the missing method. So you could do tableA.where.missing and then provide the table name. So there's a really nice blog post that highlights exactly how this method works, so I'll use the example that they have. So for where job listings are missing a manager, so you could do JobListing.where.missing(: manager), and then it's going to perform that left_join for you. And it's going to look for where the ID is nil. And I love it. It's really nice. CHRIS: That sounds excellent. That's definitely one of those things that I would have to sit down and squint my eyes and think very hard, really anything involving left_joins otherwise center. Any joins always make me have to think and so having Rails embrace that a little bit more nicely sounds delightful. STEPH: Yeah, it sounds like a nicety that's been added on top of Rails so that way we don't have to think quite as hard for any time; we want to find these orphaned records, and we know that we can use this new missing method. CHRIS: On the one hand, I feel bad saying, "I don't want to think that hard." On the other hand, that's literally our job is to make it so that we encode the thinking into the code, and then the machines do it for us. So it's kind of the game, but I still feel kind of bad. [laughs] STEPH: Well, it's more thinking about the new stuff, right? Like, if it's something that I've done repetitively, finding orphan records is something I've done several times, but I do it so infrequently that then each time I come back to it, I'm like, oh, I know how to do this, but I have to dig up the knowledge. How to do it is that part that I want to optimize. So I feel less bad in terms of saying, "I don't want to think about it," because I've thought about it before. I just don't want to think about it again. CHRIS: I like it. That's a good framing. I've thought about this before. Don't make me think about it again. [chuckles] STEPH: Exactly. On that note, shall we wrap up? CHRIS: Let's wrap up. STEPH: The show notes for this episode can be found at bikeshed.fm. CHRIS: The show is produced and edited by Mandy Moore. STEPH: Thanks, Mandy. If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or even a review on iTunes as it really helps other people find the show. CHRIS: If you have feedback for this or any of our other episodes, you can reach us at @bikeshed. And you can reach me @christoomey. STEPH: And I'm @SViccari. CHRIS: Or you can email us at hosts@bikeshed.fm. STEPH: Thanks so much for listening to The Bike Shed, and we'll see you next week. All: Bye. Support The Bike Shed
undefined
May 11, 2021 • 43min

292: Debugging with Joël Quenneville

On this week's episode, Steph and Chris are joined by fellow thoughtbotter, Joël Quenneville, to discuss all things debugging. Joël is helping publish a weekly debugging blog series and in this conversation they discuss how the series got started, technology agnostic debugging strategies, writing less bug-prone software, and speculate if Joël moonlights as a hockey coach. Debugging Blog Series 2021 Classical Reasoning and Debugging Monodraw An Elm debugging story Chelsea Troy - PoSD 2: What causes insidious bugs? Follow Joël Quenneville on Twitter Joël Quenneville on the thoughtbot blog post Transcript: STEPH: All right. And then who will be editing this episode will be Mandy. So as we run into blunders, which we never do, but if we do, then we can talk to Mandy and ask her to edit things for us. So I will try very hard to do that because I will likely still talk to Thom. [chuckles] CHRIS: Hello, Mandy. It is a pleasure to meet you. In the last recording that will be going through you, I was referring to you indirectly as our next producer. But now that we know your name, I'm so excited to have you on the team and to know who is on the other side of these, hopefully not too nonsensical recordings. So pleasure to meet you. STEPH: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Steph Viccari. CHRIS: And I'm Chris Toomey. STEPH: And together, we're here to share a bit of what we've learned along the way. So, hey Chris, today is an extra special day as we have a guest today. Joining us is Joël Quenneville, a thoughtbot developer extraordinaire and a previous Bike Shed guest. Welcome, Joël! JOËL: Hi. CHRIS: It's a pleasure to have you. JOËL: It's good to be on the show. STEPH: So, Joël, you and I are on the same client project. And during the past few weeks, we have encountered some very challenging bugs. In fact, if I look back at my developer journal, the last five or so of my entries begin with a very cheesy mystery title that's like the case of the missing data or Harry Potter and the chamber of errors. But in addition to spending your time bug hunting with me on this project, I understand that you and other thoughtboters, Jesse Bailey and Louis Antonopoulos, have been publishing weekly blog posts specifically about debugging. JOËL: Yes, that's correct. This has been a project that we've been doing for a little while. We spent about three months doing research, conducting a bunch of interviews, and gathering a lot of material on debugging. And then, starting in April and through the middle of the summer, we are publishing an article every week on the topic of debugging and exploring different aspects of it. STEPH: I'm really curious; what prompted y'all to start talking about debugging? I'm really excited to talk about the specific topics that are included in the series. And then also you mentioned all the research. I'd love to know how you went about that research. But before we go there, what prompted this conversation and then led to the creation of the series? JOËL: Within thoughtbot, we spend a lot of time trying to improve ourselves, improve the broader community as well. And there was a conversation that got started about how we could get better at debugging. It's a thing that, as developers, we do all the time, and it's not something that's often taught explicitly. Many of us our experience with debugging has been very much just learning on the job and picking it up by osmosis and trial and error. And when you're more junior, a lot of that is just random stuff and changing random lines of code and maybe copying something from the internet and hoping things go well. And as you build experience, you tend to start becoming a little bit more methodical because you've seen what works and what doesn't. And so we wondered within thoughtbot, we have all these different people who've come up with their own experience. Can we meld that together and share the summary of all thoughtbot debugging knowledge combined and help everybody level up? Initially, we were wondering could this be just an internal workshop or something where we get together and exchange on the different ways that we've learned or different techniques that each of us uses? But then this evolved into a project that we wanted to share not just within thoughtbot but with the wider world. And so its final form, or at least its current form, has been a blog post series that's going to run over the course of three to four months. STEPH: I appreciate how you've taken the opportunity to take all of this knowledge and then turn it outward-facing, so it's available to the public as well. And it really wasn't until you were talking about the series and then I started reading the weekly blog posts that are being published how little I read about concrete strategies around debugging. I think you said it very well earlier in terms of it as something that you pick up on the job, and you learn different strategies as you go. It's really hard to know exactly how to go about debugging unless you've had experience with that type of bug before. CHRIS: I've been also reading the blog posts as they come out. And I'm similarly very grateful for both the general theme of thoughtbot of hey, let's take this thing and actually make it a shared resource for the world. But specific to debugging, it really is this interesting intersection of practical steps that you can take but also almost an art form. There's like, oh, I use Pry, and I put a debugger statement here, and that's a mechanical approach that we have. But then there's also the more general how do I think about code, and how do I build a model in my head and compare that to the thing that's running on my screen? And that is really so much more something that you learn in passing, or at least it typically is. And this idea of trying to be a little more purposeful and share more of that with the world is something that I absolutely love because it is both the harder aspects of the job but also probably one of the more common. I'm spending a lot of time being like, why isn't that working the way that I thought it would? I don't know, maybe 90% of my time as a developer, maybe I'm a bad developer, but it's so much of the time that I spend. And so any amount that I can get better at this or learn from others, I'm super open to that. So thank you for producing this and for sharing all the secrets. JOËL: I liked your example of saying you drop in and you drop Pry to put a debugger at a particular location. And I think that maybe that's something that most people are familiar with and might use. But even something like that sounds like a fairly simple, basic concept. I think someone with a lot of experience, if you're pairing with them, you might ask, "Why did you put the Pry here and not there?" And that might make the difference between spending all day versus spending half an hour to find the bug. And that's, I think, a lot of where the experience comes in and where being a little bit more structured, having a strategy can really make the difference in being efficient. Because oftentimes, you're using the same tools and doing roughly the same techniques, but one can be totally flailing and doing random stuff, hoping you'll get lucky with a solution. And the other one is very methodically working towards finding the problem. CHRIS: I love that framing of the question, but I also love the idea of you ask that to someone, and they're like, "Huh? I don't actually know. But now that I think about it…" and then they sort of discover the answer. But it's very much they're operating from intuition and just like, well, obviously I put the debugger right before the line that I think has this going on. But that's not necessarily something that's top of mind. So when you ask the question, you almost help them to formalize it. So yeah, there's gold in those hills. JOËL: Absolutely. Definitely. I think gaining self-awareness about why you do what you do is always such a rich exercise, at least in my experience. STEPH: There's an interesting comparison here where people will ask me how to do something in Vim. And I'm like, oh, let me do it first because I need the muscle memory for it, and then I can tell you how to do it. And I feel like debugging is often the same way where I'm like, well, let me hear about the problem, and then I can work through it with you, but I can't necessarily tell you off the top of my head all the different strategies that I would apply. So I really liked the idea of becoming more self-aware as to how you would approach that so then you can provide those tips to someone else. You mentioned earlier about the research for creating the series, and I'd love to hear more about that. Because I imagine there's so much that you could talk about in terms of debugging but then still wanting it to be helpful to people that are working in different technologies. So how did that research go? What did that look like? JOËL: Initially, this was focused on experience within thoughtbot. So we focused a lot on more internal research. We had a survey where people just shared their debugging thoughts, tips, and tricks. And then, we also set up a bunch of interviews with multiple thoughtboters across varying levels of experience to get their thoughts on debugging, and that was incredibly rich. So we did, I think, 10 or 12 interviews within the company. We captured all of that and then synthesized the output of those interviews, the survey with all of the random tips and tricks, existing thoughtbot blog content. And then, we also pulled some external resources that we had found as well as some podcasts, some other blog posts elsewhere. And we have tried to mix all of that together to come up with 10 or 12 high-level topics that we thought were particularly valuable to talk about and then turn those into blog posts. STEPH: That's really awesome. I love how you took that approach of interviewing different people to take that instinctual knowledge that we have around how we're debugging but then helping people put that into words and then capturing that and then sharing it. What type of questions did you ask people to help people walk through what are their debugging strategies? JOËL: That was actually pretty fun because we came up with a list of questions ahead of time, not that it was a strict list to be adhered to, but we wanted to have something to have a little bit of commonality between the interviews and then let them go where they will. Most of them we opened up with just asking people, "What does the word debugging make you feel?" What is your personal connection to debugging?" And I expected most people to be like, "Oh, dread or frustration," And that was not the case. Most people said, "Actually, I like debugging. It's a challenge. It's a puzzle. It appeals to the analytical side of me." One person mentioned that it's like the code equivalent of an escape room and that escape rooms are one of their favorite things, and so they actually really enjoyed it. Where a lot of the frustration comes in is when you have deadlines, and this bug is unexpected and therefore is taking time away from things you really need to be doing now or yesterday. And so the timeline can cause pressure, but the bug itself most people seem to enjoy finding the bug. STEPH: I love so much that you just used the comparison of an escape room because I was just chatting with someone recently about how my week has been going, and I'm like, I am in an escape room. That is my job this week is to figure out how to get out of this or to understand what is happening in the system. So that's really funny. That's also very encouraging to hear that so many people have a positive association with debugging. But I certainly understand that it's more the deadline, the timeline that is then putting pressure on us to solve something quickly, but we actually enjoy the hunt is what it sounds like. CHRIS: And in my experience, when it goes well, it can be really fun. But then there are those days where not just under deadline pressure but just sort of I lost a day to blah. I couldn't figure out what was going on. And especially when it turns out to be something relatively simple, that feeling is somewhat crushing. But when there's this like, oh no, things are not working the way I expect it, and then I'm able to dig in, read a bunch of Stack Overflow, put a bunch of debug or put statements and crack that, that is a fantastic feeling. And there is the optimum flow level of where it's just at the edge of your knowledge and skill set, and it doesn't take too long, but it's not too easy. Because, like you said, the thrill of the hunt is there. So there is a sweet spot, I think, and there are ways that it can go wrong on either side. But I definitely resonate with the idea that a medium-scale bug that I'm able to tackle that's a good day, actually. I feel good at the end of that day. STEPH: There's a delightful blog post by Chelsea Troy where they share a graph that talks about the time spent debugging versus the probability of a one-line fix. And so the more hours that you've invested into fixing a bug -- Joël, I think I found this particular blog post thanks to the Debugging Series. It's like two in one of those posts. And so the more hours that you sink in finding a bug, the more likely that it's going to be just a one-line fix. And those are the ones that feel the most painful to me. It's something that is small and comes down to an incorrect assumption about the system. And those are the ones that feel good that I solved it, but I didn't really enjoy the hunt for that one. There's a specific time window in which I'm enjoying myself, which then just becomes stressful and frustrating. JOËL: Sometimes, it almost feels like the number of lines of code for the solution needs to match the amount of effort it took to find the bug. And so if I spend a day searching for the bug, it's frustrating that it's only one line and that the solution took 30 seconds, but the search took eight hours. CHRIS: I think my measure would be the length of the commit message associated. I'm fine with a one-line change as long as there is a couple of paragraphs of explanation of the journey that I went on and not just self-serving like, hey everyone, listen, because this was rough for me. But the look at all the things I learned, let me capture that knowledge here because there's actually a bunch that this one code line change encapsulates. But it's actually looking at the history of HTTP and the way that the different headers are handled in different browsers and, for many reasons, this one-line change. But if it's a one-line change and the commit message is like, "Oh, typo, sorry," then I feel bad. That's a bad day for me. [chuckles] JOËL: This reminds me of the project, Steph, that you and I are on where I've definitely had several bugs that I've gone through to fix where the commit message is definitely quite a bit longer than the actual diff. And the commit message includes ASCII diagrams showing the structure of certain database tables and why I had to change a particular query. And it's weird. And you would never understand why without realizing the schema. So yeah, I definitely feel you there, Chris, where sometimes you go on a journey, and it's very important to record that for the next person. STEPH: Yeah, your commit messages have been phenomenal. And I love all the diagrams that you've included because that has helped me have context for what exactly you understand about the system and what appears to be wrong with the system. That has been wonderful. Speaking along those lines, as we were just talking about how it can feel very ephemeral in terms of the strategies that we use for debugging, I'm really curious what strategies do y'all use for debugging? Do you have particular tools that you use? I know Joël, you are a fan of diagrams. Is there a particular tool that you use for that? JOËL: There's a variety of tools that I'll use. Recently I've been using Monodraw, which is a tool for macOS that allows you to make diagrams that can be exported as text using various ASCII characters, which means that you can then draw an entity-relationship diagram of your database and include that in a commit message where it needs to be text. You can also export it as an image. But the fact that I can use it as text in a commit message has made it particularly valuable recently. So that's my latest go-to diagramming tool. STEPH: That's really nice. I haven't used that, but I've been seeing you use it so extensively that I've added it to my list of things to check out very soon. Are there any other particular strategies that you use, since we're on the topic of concrete strategies, for debugging and how you approach debugging? JOËL: I am a big fan of binary search, which sounds like a fancy computer science term. But I think from a very practical standpoint; it's just a process of elimination. Sometimes finding where the bug is, is harder than where it's not. And so can I eliminate roughly half of this file or this project or whatever and know that it's not a concern and then keep repeating that until I have a pretty small surface area to actually find the bug itself. And this can be as simple as just commenting out lines of code. And so, like, I'm going to comment out roughly half the lines in this method and see can I still reproduce the bug? If so, then I know it's in the ones that haven't been commented repeat, repeat until I find the bug. And because you remove so much code every time that you have, it takes relatively few steps until you find a very narrow area in which the bug likely is. CHRIS: I love that. Binary search is definitely one of my favorite approaches. And I think that was a very welcoming and friendly introduction to the topic for anyone that might not be familiar. But it really is such a simple and yet effective tool for narrowing down the scope. Because when you look at an entire application, you're like, ah, something's wrong, and you start from the outside, that's overwhelming. But if you can start to narrow it down, especially, like you said, in this more methodical, purposeful approach, that is really wonderful. I think one of the tactics that I have been reaching for more and more is using minimal reproduction cases. So rather than actually working in the context of the full application -- This is especially true if I'm working on, say, a JavaScript app or Svelte is the most recent example. Svelte has a REPL on the svelte.dev website. And I find myself more and more reaching for that and just trying to very minimally reproduce code that just isolates that bug. And then I try and tease away the pieces, but now I'm left with this minimal reproduction there in an executable format, and that ends up being really useful. The same sort of thing if I'm on the Ruby side, I might actually do in a Spec just because that's a really nice way to harness execution and be like, I want to do these things. Here's the setup. And it's one of the reasons we love Specs, but I find it's actually a really great tool for setting up some data, executing the app in a certain way, and then testing it. And I find particularly with RSpec and Rails; I feel like I have good control over getting the system into a certain shape. Other applications I find that's a little more difficult, so other techniques may be necessary. But yeah, that's definitely one of the things that I've been leaning on more and more is minimal reproduction so that I can really narrow down the scope of what I'm looking at. JOËL: I like that example, Chris. And that actually is a variant of probably one of my favorite approaches, which is reasoning by analogy. So you have a hard problem, and you can't figure out a way to solve it. So you find a similar easy problem, find the solution to that, and then try to backport the solution to your hard problem. Oftentimes, the easy problem is just a simplified version of your hard problem, such as stripping out all the unnecessary detail, then you can backport that. But sometimes, it's just something in a different domain that you understand more easily, and then you can take that solution and backport it. And I had a magical experience a while back. For those of you who know me, I'm a big fan of the Elm language. And I spend a lot of time in their Slack just helping out people. And someone ran into a situation with random generators, which are a concept in that language that can generate random values. And they're trying to combine a bunch of them and having really weird bugs. And I tried a bunch of different techniques to figure out what was going on, and I couldn't figure it out. But the thing that I did figure out is that random generators in this particular dimension we were trying to understand work very similarly to functions, which are a much more simple concept. And the moment I realized, oh, in this very particular way, generators and functions are the same, all of a sudden, that unlocked in my mind; wait, I can reason by analogy here because I know how to solve this problem with functions. I don't know how to solve it with random generators. So I went and solved it by saying, "I don't know how to compose a bunch of generators. I do know how to compose a bunch of functions, figure that out and then take that solution and bring it back to generators." And I followed that process through, and it worked, and it was amazing. I came in to work the next day, and I was really excited about this. And I was talking with some colleagues, and everyone's like, "You should write about that." So there's a blog post from a year or two ago where I walked through my whole process and all the different debugging strategies I used, including reasoning by analogy to solve what was a pretty tricky bug. STEPH: I love how the typical response from talking to a thoughtboter about going through something like that is often, "Oh, you should write about it." CHRIS: And I'll help you edit it, not just "Please go write that." And Joël, you live that to the extreme. You have been an absolutely prolific author on the thoughtbot blog. And you bring some of the images and things like that that you're talking about. You really, I think, provide such a great example of paying knowledge forward and sharing better. So if anyone wants to learn about blogging on the internet, just go follow Joël's, work for a while, and you'll learn some great things. STEPH: Yeah, that is so true. I love how much you publish, and I'm a big fan of everything that you write. One of the debugging strategies that was mentioned in the blog post that really rang true for me was talking about identifying assumptions because that is one that I typically fall into where I will read about a problem, and then I will say, "Okay, I understand exactly what problem they're running into." And once I start troubleshooting, if I'm unable to reproduce -- Because I follow a similar strategy, Chris, that you just mentioned where I will try to replicate the issue either if I'm doing it locally or ideally if I'm writing a test, so then I can write a test that fails and then I can then make that test pass. But if I'm unable to reproduce, then I'm forced to go back and say, "Okay, am I making an incorrect assumption about what's being reported?" And that has been so helpful. Like, there are just little things where I realize I'm on autopilot for where it's like, the user downloads a report, and I'm like, oh well, they mean this report. And then I find out that they actually meant something else. Also, assumptions in the codebase, and that's one that you and I, Joël, have run into so much with this past week in regards to assumptions in the codebase as to how many associations a record can have. Is it one? Is it many? It has many, but it really only wants one record. So assumptions from the perspective of when someone is reporting an issue and then also assumptions in the codebase. For the first one, I have found that, especially when I'm new to a team when someone reports an issue, I often like to hop on a quick call with them and say, "Hey, are you able to reproduce this for me? And so I can watch you, and I can understand your workflow." And I have found that typically speeds me up drastically. JOËL: One of the things that was mentioned in the article on listing assumptions which is maybe a bold claim, but it opens by saying that all bugs are a form of miscommunication. And this might be human-to-human communication where you didn't understand the requirements or what was trying to be done. But code is also communication between us and computers. We want computers to do a thing. And if the computer doesn't do what we're telling it to, it's not doing that just to show us who's boss; it's because we didn't communicate correctly what we wanted. And so yeah, trying to better understand ourselves what we mean and our assumptions is a key part of debugging. And that's the thing that came up over and over and over in all the interviews was one, build self-awareness about the assumptions you have, and there's a bunch of different techniques for doing that. And then once you have self-awareness of what your assumptions are, never trust anything, validate, validate, validate, because yeah, you're often wrong. CHRIS: There's an interesting parallel to that in my mind of we often end up with these systems, and it's behaving in an odd way. And so we have to build this mental map of okay; what are all of the different states and workflows that can get us into those various states? And having now debugged a handful of times in my life, I'm trying as much as possible to flip the script and go with an ounce of prevention is worth a pound of cure. And this is why one of the most common things that I say in a pull request is, "Hey, can we make that null false on that new database column? Hey, can we change this type constraint so that instead of it being a Boolean and then another attribute, it's actually a three-state enum?" and et cetera, et cetera. How can I collapse the states down so that when I'm in debugging mode, I actually can take some things as givens? Still, maybe validate from time to time, but the more I can learn to trust a type system or the database or things like that, things that are a little more trustworthy than I am or other humans, I'm increasingly loving that. And there's obviously a gentle balance there, but that's something that I've been leaning into more and more. And I think it's directly informed by the years of my life that have gone into debugging at this point. Is that accurate? That seems like a high estimate, but it's a lot. It's a bunch of weeks at a minimum. JOËL: I felt that really strongly. I'm kind of disappointed as an industry that we default things to be nullable so often. I wish database columns were non-nullable by default, and you had to opt in to make them nullable. I wish GraphQL didn't make columns nullable by default. And I think oftentimes, when you're working in a dynamic language, you don't care about that distinction. And so you just let it go by. And let's say with GraphQL, again, I hang out a lot in the Elm Slack channel, and I spend a lot of time helping people integrate Elm in GraphQL. And when you get a schema and try to load that into Elm because it has a type system, it will read your schema and wrap everything in maybes because it's as though this field is optional, this thing is optional, this thing is optional. And then people come to the Slack, and they're like, "Why is there maybe everywhere with deeply nested -- This is a terrible mess in Elm. What went wrong?" And then I have to tell them, "It's not the Elm tool that's wrong. If your schema has this implicitly, the default thing was to make it null." And so it just looks normal, and you want to put all those exclamation marks everywhere. And a lot of time people don't believe me. And I have to say, "No, no, you're right. It really is the schema that's the problem. Please go put all these exclamation points." I'll give an example of a non-null schema, and then they try it, and they're like, "Wow, this makes such a difference." STEPH: Joël, the defender of Elm. JOËL: [laughs] STEPH: And I am with you, and it is interesting. And I've been there myself, too, where there's a fear of over-restriction in terms of if I make this not null and something blows up, then that feels like a bad outcome. And so I've seen a number of projects where we let nil get through so easily because then we just always handle the nil versus having that restriction earlier and making that decision. It's like we're pushing off that decision of like, well, that'd be nil for now, and then we'll figure it out later. Versus starting with that decision upfront and saying, "No, let's go ahead and make that decision now. What do we do if this is nil?" And I agree that it would be wonderful if we had more restriction upfront and then we loosened the requirements as we find out that we need to versus starting with the loose requirements because walking that backwards is incredibly difficult. JOËL: Yes, having to backfill nullable columns that we don't have values for in the database because now we want to restrict it, but for years we didn't collect that value. And so what do we do now? That becomes really tricky. STEPH: Chris, a minute ago, you were mentioning prevention, which I love so much because then we can avoid a number of these debugging discussions, although this is also delightful. But there's an opposite end of that spectrum that has taken me a while to gain comfort with, and it is when you don't know enough about the bug, and you can't reproduce, and then you essentially have to let it go. And there are other ways that you can debug, but you can't fix it in the moment. So let's say that you have an error or something that happens every once in a while, but you can't actually find the reasons that it's happening or if you have data that's getting created in a certain state, but you don't know what in your application is creating that state. So instead of spending what could be hours and days triaging how your system got in that state is instead perhaps adding some logging around it to say, "Hey, this is the moment where we are causing this to happen," or maybe it's adding a constraint, so something fails very loudly whenever the system tries to put data in a particular state. And in those cases, it took me a while to become comfortable with the idea that I can't solve this today. I can't solve it now, but I can take steps to then know how this is happening. So then, in the future, we can actually prevent this or apply the fix. But initially, that always felt like a really bad outcome for a ticket that's reporting a bug where it's like, hey, I can't fix this today. I don't know exactly what's happening, but I've added some logging, or I've added something that's going to raise when this happens. So when we do get notified of it again, then we can more quickly triage and put the right fix in. CHRIS: Yeah. If anything, I feel like there can be a -- Like we were talking about earlier, there's almost an enjoyment to solving a bug where it's a puzzle. It's a thing that may capture our attention, but sometimes, actually, perhaps often, the correct answer is that's actually not the most important thing right now, or the cost is too high to try and solve it given the information that we have. It's affecting a very small number of users. Maybe we can make that experience better. It's not just a generic 500-page, but we turn it into a, "Hey, sorry, you got into a…" like a more explicit error state but defer the actual debugging because there are other things that are slightly more pressing, affecting more users, or again, we just don't have enough information. And I feel like that actually can be a difficult thing to be like, no, but I want to solve the puzzle now. This is a fun puzzle. Please let me solve the puzzle. But sometimes, we don't get to solve the puzzle today. JOËL: One of the articles in our series that I'm really excited about takes a look at classical philosophy and various categories of reasoning identified in classical philosophy and how we can apply them to debugging to debug in a more strategic and methodical manner. And one of those categories is inductive reasoning, which is very similar to, say, the scientific method where you gather a lot of information. And then, based off of those cases, you try to come up with a pattern, have a hypothesis for it, test it. And then if you can show that it's actually the case, then you're likely correct. But of course, that depends on having enough test cases for there to actually be a pattern and for it to be statistically significant. And so for some bugs, if there's only one instance of it happening and you just say, oh, somehow a bad value made it from our front end UI into the database, and we don't know how. But it's not happening to every user. Like, there's only one use case, and we can't figure out how that happened. We don't have enough information to build up those test cases to try to find a pattern. And so that's where getting more test cases becomes really important. As Steph mentioned, logging can be really helpful here, raising whatever you need to do. Maybe it's adding a constraint or a validation to say, "Blow up the next time this happens." But once you have enough test cases, then you can start seeing patterns. And that's your inductive reasoning to solve the bug. STEPH: Something that we touched on earlier but I don't think I've given enough credit to but is something that I really appreciate is the fact that all of these strategies that are being talked about in each blog post are applicable across technology stacks, across languages. Because you really highlighted a point just a moment ago around how most of the bugs that we're working with are all bespoke. They're all special little bugs in their own special way. And so it's really hard to have a blanket strategy that then applies to each one because they are unique. And the fact that y'all are creating so much content that has general strategies that people can apply when debugging is really impressive to me. So I'm really curious how are y'all doing that? JOËL: When we came up with a series idea, we laid down a few principles for how we wanted the outcome to be. And one thing that came up pretty early was it's important for this to be language agnostic. We don't want to just teach like, here are some very specific Ruby tools you can use, which that's a very helpful article, but that's not really the kind of information that we were looking for. So we were trying to find what are some higher-level techniques and strategies that are usable throughout your career? And then secondly, we wanted to focus on finding bugs rather than how do you solve them or how do you prevent them? Chris mentioned a little bit earlier about techniques for preventing bugs from happening in the first place, and we might have a bonus episode or article on how to write bug-resistant code. But the focus of the series is what are some language agnostic ways that we can improve your search to find the root cause of a bug? And a lot of that has just been synthesis, so saying okay, here's a bunch of different things that different people told us. And we were fine with having language-specific examples in the interviews. But how can we then find what's common and what's not? One thing that I think was really interesting was talking how different people gain information about a particular point in code, and debuggers are a pretty common way of doing this. But print line debugging is also a really common way to do that. And every language does this slightly differently. And you can even do this, not just via text, but visually. So if you're writing CSS and you are trying to figure out when a particular rule triggers, you might put a 1-pixel red border around something. And that's CSS' equivalent to print here or console.log in JavaScript or Ruby or some other language. So the idea is to see all what different people told us and then see can we extract a general principle out of this? And walking a fine line between we want the theories to have practical advice for people that they can use but also zoom out just a little bit so that we have some of the big picture so that you can make some of these big connections and see some of the patterns that can help you apply in different situations that you run into for debugging without necessarily getting head in the clouds, you know, what is debugging? And I'm sure there's a really fascinating philosophy article, not the classical philosophy article I mentioned; that one's actually good. But you can philosophize about debugging in a way that's too abstract to be useful. So we want just a touch of the philosophy to keep it big picture while also giving very concrete, useful tips and techniques that are language agnostic that folks can use. STEPH: That's fabulous how y'all are able to separate that thought process away from the direct, specific action that someone takes. So when someone is dropping in that console.log statement or that print statement, it's like, okay, but what thought process took you there to the point that then you were trying that action? I really like that. There's one other trick that was mentioned in one of the articles that I also really enjoy. It's the analogy of taking a ball of yarn with you, so you're always tracking where you've been. And that is the other thing that I do heavily when I'm debugging is that I always have a note-taking application that's open because I always document what I've looked at, what were my findings. And then I think through what do I want to look at next? And sometimes I'll write down a list of three or four different questions of it could be this, it could be that. And then, I will prioritize those based on what's the quickest to look at? What can I replicate the fastest? What can I remove from this list? Back to earlier, when you were talking about the process of elimination and then walk through each one. So that way, when I do find the bug, I also have those steps that I can look back. So in case, someone has a question about it, in case they're like, "Well, what about this?" I can say, "Well, yes, I also checked that," or there just may be extra helpful tidbits that fall out of that process. At least they prevent me from checking something twice. JOËL: I've been pairing with you, Steph, on several bugs recently, and I've really appreciated the notes that you keep. They're very thorough, and that's something that I've tried to bring into my own practice by seeing you do that. CHRIS: That's something I've been iterating on in my own workflow of late is having a directory of notes associated with each project. And as I'm working on each, it's almost not even just bugs at this point but any new feature anything that I'm exploring. Because often, initial exploration of integrating with a library feels a little bit like debugging, sort of poking at the edges and what's true and what's not and writing up little reproducible steps of okay, run this code, then this code, then this code. And I've now just taken to keeping those forever because it turns out like, oh, I know I integrated that, but what was the step? I feel like there was something I did. And being able to go back and have that artifact now is so useful. And it's actually something that I've only really gotten in the habit of over, I'd say, the last two years, but that archive of notes is now very useful even to this day. STEPH: So I think we've covered a number or hinted at a number of the wonderful topics that are included in the series, including some of the different ways that we can identify our assumptions or ways that we can get unstuck. That was one of my favorite ones on all the different ways to get yourself unstuck when you are in the throes of debugging. There's also the idea of when you encounter a bug that if you can't fix it right away, but then some of the steps that you can take to then be able to fix it in the future. I would love to know, if you don't mind sharing some spoilers, as to some upcoming topics that will be in the unpublished but soon-to-be-published blog posts. JOËL: Yeah. So for all of The Bike Shed listeners who want the inside scoop, there are a few that I'm really interested in. So we opened the series by talking about mindset issues, how to approach a bug, how to think about assumptions, and now we're moving into some more concrete techniques and tooling. One that I'm really excited for is ways you can use Git more effectively. There are a couple of people that we interviewed who mentioned just how important it was to their workflow. And we've got some really interesting notes on that. There are also some really interesting ideas around areas in our codebase where bugs tend to accrue that are more likely to be, particularly around the nebulous concept of boundaries. This was a conversation that we had with one of our interviewees where we had our initial list of questions, and then we ended up completely throwing them away and going down this long, random tangent about boundaries; it was so good. We decided to dedicate a whole article to it because there are really interesting things around that. STEPH: All of that sounds really exciting. I love that you mentioned Git because even in the conversation that we've been having right now, that didn't cross my mind, but yeah, that is such an incredible debugging tool. So I'm really looking forward to that and also the one that's going to dive into boundaries. All of that sounds really exciting. JOËL: One thing that I think is really fun with digging into Git is that generally, when we think of debugging, we're trying to find where the bug is. But oftentimes, the real question we need to answer is not just where is the bug it's when is the bug? So not just debugging through space but debugging through time, and Git is the tool to do that. STEPH: Oh, that made me laugh but also made me depressed: when is the bug? [laughs] JOËL: And I feel like this is going to turn into a cheesy sci-fi TV series. CHRIS: It doesn't need to be cheesy. JOËL: True. CHRIS: Yeah, it does. [laughter] STEPH: For it to be good, I think it has to be a little cheesy. Sci-fi bugs coming to your application next summer. CHRIS: Everybody hop in the Tardis. We're going to find the bug. JOËL: I feel like there's some variation of that line that shows up in a lot of time travel series. Like not where is X, but when is X? STEPH: On that delightful note, thank you, Joël, so much for coming onto The Bike Shed and chatting with Chris and I about debugging. For everyone that would like to follow along for the Debugging Series, where can they find those articles? JOËL: So you can go to the thoughtbot blog. There is a tag we created specifically for that, Debugging Series 2021. And I'm sure that you'll link to that. All the articles are also going to be linked from the first article of the series, and I'm sure that will be included in the notes as well. STEPH: Perfect. And where can people follow your work? CHRIS: People can follow me on Twitter @joelquen, J-O-E-L-Q-U-E-N. It's not the hockey coach, although I can neither confirm nor deny that the two are the same person. We've never seen them in the same room together. STEPH: I didn't know you were a hockey coach in your spare time. Oh wait, this is the part that you can't confirm, right? JOËL: [laughs] Well, that would be letting the secret out. STEPH: All right. We will try to maintain your secret identity or whichever one that is. CHRIS: It's a really terrible secret identity if it's actually the same name. Joël, you really should have put more effort into this, coach Q. JOËL: [laughs] STEPH: Coach Q. I'm going to start calling you Coach Q. That's wonderful. Well, with your permission. JOËL: That's the real nickname. For those who don't know, Joël Quenneville is or formerly was the coach of the Chicago Blackhawks NHL hockey team, one of the best coaches ever in the National Hockey League. And a couple of years ago, I got to give a talk in Chicago, a conference talk. And everybody asked me, "Ooh, any connection to the coach?" STEPH: To which you replied? JOËL: Oh, I had a whole slide about the conspiracy that we may or may not be the same person. STEPH: That's really fun. Well, thank you again so much for coming on our show, Coach, and walking us through the wonderful Debugging Series. The show notes for this episode can be found at bikeshed.fm. CHRIS: This episode was produced and edited by Mandy Moore. STEPH: If you enjoy listening, one really easy way to support the show is to leave us a quick rating or a review on iTunes as it helps other people find the show. CHRIS: If you have feedback for this or any of our other episodes, you can reach us at @ _bikeshed on Twitter. And I'm @christoomey. STEPH: I'm @SViccari. JOËL: And I'm @joelquen CHRIS: Or you can email us at hosts@bikeshed.fm. STEPH: Thanks so much for listening to The Bike Shed, and we'll see you next week. All: Bye. Support The Bike Shed
undefined
May 4, 2021 • 50min

291: All Things Inertia.js with Jonathan Reinink

This week Steph's taking a quick break, but while she's off, Chris is joined by a special guest - Jonathan Reinink. Jonathan is the creator of Inertia.js. Inertia.js lets you quickly build modern single-page React, Vue and Svelte apps using classic server-side routing and controllers, and listeners of the show will certainly have heard Chris rave about it on previous episodes. Chris and Jonathan dig into what makes Inertia unique as compared to frameworks like Phoenix LiveView, Laravel Livewire, and Rails' Hotwire & Turbo. They also discuss how Inertia embraces the URL, the unique "protocol" nature of Inertia, and how to consider Inertia alongside native mobile applications. Throughout the conversation, Jonathan's consistent philosophy of wanting to build robust, performant, and delightful applications shines through. Jonathan Reinink on twitter Inertia.js Eloquent Performance Patterns TailwindCSS Church Social Jonathan on Full Stack Radio Foundational blog post: Server-side apps with client-side rendering Laravel Livewire Alpine.js Phoenix LiveView Hotwire Turbo The Inertia Protocol Transcript: CHRIS TOOMEY: I am seeing what I believe to be the relevant things. JONATHAN REINIK: Let's dance. CHRIS: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Chris Toomey. And this week, Steph is taking a quick break, but while she's away, I'm joined by a special guest, Jonathan Reinik. Jonathan Reinik is the creator of Inertia.js. And listeners of the show will know that that is increasingly one of my favorite frameworks and, frankly, just ways to build applications on the internet. Jonathan is also the creator of the Eloquent Performance Patterns course, which teaches the Eloquent ORM, which is the ORM in Laravel but really digs into deep performance and database things, so really covering that back end as well. Jonathan also collaborated on the development of Tailwind CSS, a utility-first CSS framework which again is something that I have spoken of in very high terms on this podcast. And lastly, Jonathan currently runs his own SaaS business called Church Social. So really, Jonathan is a bit of a quadruple threat covering back end and front end design and entrepreneurship. So pretty much everything you want to see. And frankly, I've been so impressed by the breadth and the depth of Jonathan's work and just the deep way that he is thinking about building applications. So I am absolutely thrilled to have him on the show today. So without further ado, Jonathan, thank you so much for joining me. JONATHAN: Thanks so much, Chris. That was a very kind introduction, and yeah, it's awesome to be on The Bike Shed. I've been a long-time listener, and as I've said to you, I really appreciate the support that you've given to my work over the years. So yeah, it's awesome to be here. CHRIS: That's interesting. We're measuring it in years now, but it's a very sincere thing for me. I think with Inertia, you've built something that is both very unique and a special approach to how we build things, but it's also built from very familiar pieces and allows us to reuse the deep amounts of knowledge that we have in the Rails community or the Laravel community. But actually backing up just a little bit, because we're going to dive deep on Inertia.js today, for folks that are not as familiar or have only heard me mention it in passing, there's a wonderful episode of Full Stack Radio where Jonathan and Adam Wathan talked about Inertia.js, and I think gave a very good foundational summary. So we'll link to that in the show notes in case anyone wants to dig in a little bit more. Likewise, Jonathan has a really fantastic blog post called Server-side Apps With Client-side Rendering, which, as far as I can tell, is the manifesto that began this whole journey for you. And I really love that you have done so much of the work in public, and people can see the history of how this idea has evolved and really crystallized into what now is a very production-ready framework in sort of the way to build things. But I would love to hear right now just for anyone who is not as familiar and also just to hear how you summarize it at this point in time. What does your introductory elevator pitch for Inertia.js sound like in April 2021? JONATHAN: That's a great question. So it's hard to answer this without unpacking a lot of different things. And you mentioned the podcast with Adam; I think that's good because it goes in a lot of the technical detail of how Inertia works and why I created it. But the elevator pitch these days for when I talk to someone about it, it's generally I explain it as a way of building modern web apps. And in particular, when I say modern, I mean web apps that have a lot of JavaScript, so frameworks like Vue or React or Svelte, so applications that are built using those tools. And the key thing that Inertia offers is for you to develop these modern applications without having to first build an API sort of the way historically if you ever wanted to use one of these modern web stacks like Vue or React or Svelte, you could use them within Laravel or Rails by inserting them into your Vue or into your Blade templates or your ERB templates. But it was difficult if you ever wanted to turn it into a legitimate single-page application. And anytime you would ask that question, if you go out on the internet and say, “Hey, how do I build SPA with, say, Vue and Laravel or React and Rails?” The answer was always, “Well, you need to build an API. You need to build an API.” That was always the missing piece because that's the way that everyone in the Jamstack era that we're in that's the way that everyone's building their applications that are like these heavy client-side applications. And I totally get the need for those style apps and the place for those style apps. But I really missed this way that you could build an application with Rails or with Laravel where you could just literally spin up a new app, create some routes within your server-side framework, create some controllers, create some views, and have a working application within minutes really. You could have something being displayed on the screen within minutes with these classic monolith applications. And if you wanted to do the same thing, if you wanted to get an app up and running in minutes with Vue or React as your completely client-side SPA scenario, it just wasn't working because as soon as you say, “Well, in order to do that, you're going to need to have a back end Rails or a Lavarel application, and then a client-side Vue or React application. And then you're going to have to create this API that connects the two together.” There's just a lot more work that goes into that. It's not only the work of actually creating the API; I find a lot of the decisions that come along with building an API; it’s like, okay, what does the abstraction look like? Am I going to build it with REST, or am I going to build it as a GraphQL API? And all the decisions that come along with designing and architecting that which again has its place. But there's just something awesome about saying, “Here's a new route. Here's the view that I want to render. And here's the data that I want that view to have,” and just go off and do it, and it's done. And some people ask me, “Well, with Inertia, if you're not building an API, what happens someday if you need an API?” And they frame it like, well, this is a terrible decision. You should be starting with an API. But for me, the reality is that so many of the web applications that I was building and that I've seen other people building is they had already made the decision not to use an API because they had already made the decision that they wanted to use Laravel as a monolith app that had their controllers and the routes and their views all within that and the same thing with Rails. So if you've made that decision to build a monolith app with Laravel or Rails, you've already made the decision to not build it with an API. I was coming in from the other way. It's like, I just want to build an app the way I've always built in Laravel, and I don't want to have to build this API. Of course, there are times where you do need an API, which I think we're going to talk about maybe a little bit later if I don't ramble on too long, where it does make sense to have an API. But yeah, that's kind of the elevator pitch. I think maybe to close off that thought is that I really, really enjoy having a tight coupling between my routes and my data layer and my views, which, again, I appreciate that. That probably sounds like blasphemy in modern web development. But for me, I think it's so empowering when you say, “Hey, I have a controller that's given me some data, and I have a view that's rendering the data, and those two know about each other, and those two depend on each other.” You can work so fast because I'm not thinking, okay, well, I have this API endpoint that returns a user, and that has their first name and their last name and their email. But I also need to think about it in the situation in the future where I might need this attribute or that attribute or some other attribute and make sure I have all that figured out ahead of time or at least have a way to add it in later. And all of that thinking that goes into designing an API, I find that that adds a lot of overhead. And then maybe related to that also is the amount of times that you're rendering a view within your application that needs data from multiple different places. And to me, this is like one of the huge performance benefits that you get with a tool like Inertia is with, say a REST API, GraphQl solves this, but with a REST API, you're often getting too much data for what you actually need for the page, or you're often making more than one HTTP request because you say, “Well, on this particular dashboard, I need some user information. So I have to hit the user endpoint. I need maybe the latest product sales data, so I need to hit that endpoint.” And you’re dealing with these performance issues that you get with a REST API that with Inertia, you don't have that problem because it's just going back to classic Laravel Blade views or Rails and ERB templates. Am I saying that right, ERB template? CHRIS: Mm-hmm. JONATHAN: In those situations, you say, “Well, if I need data from three different places, well, I'll just grab data from three different places and send it to the view, and that's fine. And I can do that in the most efficient way and get the data that I need specifically for that view.” So anyway, that's some of the thinking that drove me to build Inertia and some of the things that I was going for. And yeah, it was an evolution. It really came out of me using Turbolinks and really appreciating what Turbolinks gave me but taking it to that next step where it's like Turbolinks, except it's built with the same principles as Turbolinks but built for modern client-side frameworks like Vue and React. CHRIS: Yeah, that all feels very familiar to me. And in my experience, I've now worked with Inertia on a handful of projects, but in particular, I have just a small personal app that I use to manage different aspects of my life. And it's been my playground for different technologies. And I've migrated it through a bunch of different versions where it used to just be a Rails app. And I was like, oh no, the thing that I need to do to be on the cutting edge is to turn this into -- it's a Rails app on the back end with an API, and then it's a React app. It's separately deployed, but those two talk to each other. And what you were talking about of the deep coupling, I think that coupling exists whether we want it to or not. And so embracing that and revisiting when I eventually migrated that application to use Inertia and the client-side stuff folded back into the core codebase. Now deployments all go out in sync. And that turns out to actually be a really nice thing and a non-trivial thing to solve otherwise. As a developer of one on this particular project, the amount of complexity that was removed from the app when I switched it over to Inertia was amazing. I got to remove client-side routing. I got to remove client-side state management, which I think I was using Redux at the time. I got to remove some form helpers that I had. I think I might've been using Formik or React Use Former, any of those. But there are so many different little pieces that you ended up cobbling together to make an application. And it was amazing to me as I moved to Inertia, where I was like; actually, I don't need any of those, and routes suddenly are defined in one place in Rails in a familiar way. But things like redirects all work -- It feels just like a Rails app but with the extra abilities that a front-end client-side framework gives you when you want those when you need those. But otherwise, it really does feel like I'm rendering to an ERB template. It just happens to be that that template is rendering on the client-side and is written in React or Svelte or whatever the front-end framework is. But it almost feels like progressive enhancement. I'm borrowing a term, and it's not actually applicable here, but it really feels like that. It's like, oh, it's a Rails app, but I just want to make it a little bit fancier, and Inertia does that in such a fantastic way. And actually pivoting just a bit, as far as I can tell, there seems to be an explosion of thinking in this space. There are a handful of frameworks, namely Laravel Livewire, which is often paired with Alpine.js. Elixir has Phoenix LiveView, and then Rails has the Hotwire suite, which Turbo and Stimulus are the most pointed considerations. But interestingly, I think all of those frameworks, which I think are trying to provide a very similar experience, tend to keep things on the server-side, so using the Laravel Blade templates or the ERB in Rails. But you've taken the different approach to say, “No, let's embrace this front-end technology where it makes sense.” And again, there are a lot of pieces that can fall in, and I don't need the Redux and the React Router and all of those things but still use that client-side framework to be the rendering engine. And so I'm intrigued if you can talk a little bit more about that and that trade-off because I think it really differentiates Inertia and its approach. I personally found it to be fantastic, but I'd love to hear a little bit more about your thinking on that. JONATHAN: The thing about modern websites and web apps, in particular, is it doesn't matter how you slice it; we need JavaScript. So if you disagree with me there, then everything I say from this point on will not make sense to you. But I think we can all agree that modern web apps need JavaScript. JavaScript is the programming language of the web of the browser that allows us to do whatever magical things that we want to do. And if you look at tools like Phoenix LiveView, Laravel Livewire, and even the new Turbo stuff from the folks over at Basecamp, they all are embracing JavaScript in the same way. It's just they're framing it in a different way. I would say, especially with Livewire and LiveView, they're almost creating like an abstraction between the server and the way you write things on the server and the client-side. And they're almost hiding the JavaScript, which is really, really cool. I think it's such an interesting thing to try to do where somebody who's not familiar with JavaScript and not familiar with Vue, React, and Svelte can go and do things, write server-side code that gets rendered server-side. And then there's some JavaScript that these libraries insert that allow you to do more interesting things, whatever those things might be, show a drop-down, or drag and drop or validate a form or submit a form without actually submitting it fully to the server but submit it over XHR instead, all these kinds of things. But the point is they're embracing JavaScript just in a different way. And same with Turbo, Turbo gives you a way to write JavaScript for an application that mostly has server-side rendered HTML. So I think it's important to just recognize that JavaScript is key in all these frameworks. With Inertia, it's the same idea in that Inertia wants to embrace that classic way of building applications using the classic server-side monolith application framework like Laravel or Rails. But the difference is it acknowledges or embraces these existing client-side frameworks that have really grown in popularity. And the three, again, I keep mentioning them, Vue, React, or Svelte. Svelte being an up-and-coming one that's not nearly as popular yet, but it seems to be gaining a lot of steam. CHRIS: It's on the rise. That’s my long [inaudible 14:46] JONATHAN: Yes, and people keep saying that. So anyway, Inertia basically said, “Hey, we want to keep building server-side apps. We want to keep building monolith apps similar to these other tools, except what we're going to do is we're going to embrace the fact that there's this really, really amazing tooling that's been developed for the client-side.” And it just doubles down on that. So for me, the reason that I ended up here was because, in my own SaaS application, it was a Laravel application that started with mostly Blade views initially. And then, over the years of building it, which has been many, many years, I've slowly added more and more Vue components within my app. And initially, the way I did it is those Vue components would just be inserted in as regular HTML tags in my server-side rendered templates. And then, when the page renders, those Vue components would boot up and do whatever they need to do. So for me, when I was building Inertia, I had already fallen in love with Vue, in particular, and having all the power of these client-side frameworks. And there is so much there. It's not just Vue, React, and Svelte; it's all the amazing tooling that's available out there that you can add on top of it. And this is the thing I often tell people that Inertia isn’t -- we say right on the homepage, “Inertia isn't a framework.” And the reason why I say that is because I don't want people to think of Inertia as an alternative to Vue, React, and Svelte. Do you know what is a better way to frame it? It's actually more of an alternative to React Router or Vue Router; that's really more what it is, where you can say, “All my routings are handled server-side,” and that has all kinds of interesting implications. But it's more of a router, and it just so happens to pass along that routing control over to the server. Anyway, so that's really for me what differentiates Inertia from those other tools is because it's really doubled down on these client-side frameworks. And I think the reason why Inertia has been relatively popular is because people know Vue and people know React. And when it comes to then working with Inertia, it's not some new thing that they have to learn. It's an existing set of tools that they're already super comfortable with. And in so many ways, when you're building an Inertia app, you're kind of building a classic Vue app or a classic React app or a classic Svelte app. It's just there's a bunch of pieces missing. Like you said, it's like a bunch of the client-side state management stuff, which nobody likes anyway, is gone. The other thing that's gone is client-side routing. You don't have this back-end routing is over here now, and client-side routing is over here, and I have two different routing definitions. It's like, no, that's all just server-side now in one place. The other amazing thing you get is you mentioned redirects and that whole HTTP layer you get just along with Inertia for free because it's just part of your server-side stack. And one key aspect of that is auth. You can just use good old-fashioned nothing is better than session auth. Like, it just works. And, so whatever your typical solution for doing session auth in Laravel or Rails or whatever server-side framework you're using, all this stuff just works. So anyway, coming full circle on your question, the reason why Inertia has gone this way is because I really think that there's a huge amount of value with using these modern frameworks. And we just doubled down on using them. CHRIS: Yeah, that resonates with my experiences using Inertia and in contrast to the other frameworks. Everyone seems to be trying to get to the same place of providing a mechanism to have more almost app-like functionality but still using the traditional server-side technologies. But I think Inertia has chosen an approach to that that is unique in that category and really has provided a fantastic outcome. I've been very frankly surprised by the fidelity of experience and how app-like I can get something to feel when building with Inertia while still using all the same technologies. And the fact that I can use just traditional server-side auth and redirects and things like that is just so nice, and everything feels right. There's an experience that I've had on many applications that are, say, a React client-side bundle that gets sent down and then boots up, and then the layout starts to render. And as its data fetching, it gets like a 402 response or something like that in that data fetching. And then it's like, oh no, I need to hard redirect you over here on the client-side to this other page. And there's this junk of semi-filled-out layout, and then suddenly you're on the login page. And again, with Inertia, it looks like a normal server-side rendered app, but it isn't in the ways that really matter to us. And it is one of those things where the more I played with it, the more there's an experience of interacting with Inertia that it surprises me consistently how nice it is to work with it and yet it's so much easier to maintain an application using it. I know I'm raving here, but I am really a big fan of this for everyone listening in the audience. JONATHAN: [laughs] CHRIS: And actually to continue on one of the things you were saying there, one of the things that stands out to me in Inertia is the way that it embraces URLs and to a certain degree, that seems like a purposeful thing, but it also seems like it just naturally falls out of how Inertia works because we're no longer using a client-side state management technology, the way to manipulate state is through the URL. If you want to see a different version of the to-do list you're looking at when you click on that link, you change the URL and the state changes in response to that. And so everything is fundamentally kept in sync, but URLs are very much at the center of the architecture, and I really love that so much. I think URLs are often forgotten in client-side frameworks or underserved or underused. And it turns out in my experience as a user and both having served many users, people love to command-click on links. They love to right-click open a new tab. They love to be able to reload and see the same thing on the screen when they reload the page. They love to be able to bookmark. These are all really wonderful things that come out of working on the web. And the fact that Inertia has a pit of success around having URLs and have that be the way that we drive state is just so fantastic. So I'm wondering how much of that was very purposeful on your part versus how much of that fell out of the architecture. JONATHAN: That is very much something that fell out of the architecture. I say that not to say that I don't value URLs; I absolutely did. That's the way every single one of my Laravel built apps worked. It always starts in the route file. You hit the route file, you define a new route, and it goes from there. So I absolutely think that the URLs are critical. But the fact that it just ended up working out so nicely was, yeah, I'm going to say it was a bit of luck, a bit of coincidence. I find this is what's so interesting when you start pushing on a new way of things; you initially don't really know where it's going to end. It’s like you have some ideas of how the tool can work and where it might go, but I think there's a lot of unknowns that you just figure out after a while. So the thing I said earlier actually about the fact that Inertia in a lot of ways is like a client-side router; it’s, it's a routing library, to put it that way. I had been working on Inertia for a year and a half, and then a buddy of mine, Taylor Otwell, the creator of Laravel, he and I were chatting, and he said to me at one point he's like, “Oh, you know what? Inertia is actually super simple. It's really just a routing library.” And it was like, bam. It was kind of that moment; it’s like, oh yeah, I hadn't thought of it like that at all. But when he said that, it made a ton of sense to me. So it's just this interesting progression the more you work on something, and the more you push on the edges, you learn what's possible and what it even is. I had this interesting experience, so remembering that Inertia came from Turbolinks. So I had my whole app built with Laravel ton of server-side rendered templates with Blade with view mixed in. And I had the SPA mode by clicking around using Turbolinks. So when I decided to try building Inertia, I removed Turbolinks, and all these requests now happened over XHR but using this preset JSON structure that powers Inertia. I really, in my mind, had this idea that it was only for GET requests, for GET visits; it was just for that. So the initial version of Inertia, there was no Inertia.post or Inertia.put or anything like that. It just wasn't something I even thought was possible. But then I remember, and this is often how it goes; I was out for a hike that day to get away from the computer for a little while and just let your brain drift; I'm sure you can relate to that. I was like, wait a minute; I could totally just support POST, PUT, PATCH, DELETE. And that was such an aha moment for me where I just realized that it was so much more than what I originally thought it was. And then the whole thing from that I remember it was a bit of like a waterfall effect after that where I remember running home out on that hike and hacked it together, and then it was like, okay, well, if I submit a form using POST, well, okay, I'm on the create user page. And I submit this form using Inertia.post to the user's endpoint. I'm like, well, how do I now end up back at the user index page or whatever page, maybe the user edit page. I’m like, wait a minute, I can just return a redirect back to the user index page, and it's literally going to return an Inertia response from the user index page. And then the way Inertia works is it dynamically swaps the page component client-side. And it was just like, oh, this is way too cool. And this really drives my thinking now that it's become a little bit more clear to me is that it really it's all based on HTTP using headers and normal HTTP stuff like redirects are such a critical piece of the story. But to me, that's super neat that, in a way, it's like a throwback to the fundamentals of the web and the browser and the fact that Inertia can just use those things,,, and it doesn't have to be fancy in a lot of the ways. It can just rely on those existing core pieces of the browser. So, yeah. CHRIS: It really is interesting to me how it feels like progressive enhancement in that way where you're building on top of these core fundamentals of HTTP and requests and redirects and status codes and things of that nature. Particularly interesting to me was it took me a while, I'm going to be honest, to figure out forms and particularly validation errors in Inertia. And that is entirely my fault. You have absolutely fantastic documentation. I am so impressed by the quality and the density of the documentation that you have that really covers everything. If we're being honest, I hadn't read the page, but I was doing form posting and then the subsequent errors and how you deal with that. I was doing it in a very traditional Rails way which if we're being honest, that is not a fundamental of how HTTP works. Rails just chose an option of oh, if you POST but we don't create the object because there's a validation error, then we're going to stay on the URL of the POST, so the collection route, but we're going to re-render the form in line. And that's a choice that Rails made that is interesting because at that point, if a user reloads the page, then things are weird, and it's not going to reload. They're not going to see the same thing after that reload, or it's going to try to repost or et cetera, et cetera. There's a bunch of edge cases there that sort of fall out. Whereas with Inertia, you end up redirecting back, and there's this interesting handshake of the errors, but from an end-user experience, it is absolutely fantastic where you stay on the form; the URL does not change. Technically, there's a POST and a redirect back under the hood, but Inertia just handles all of that for you. And you end up with sort of in-line validation errors. But you don't clear out any fields, and there are just wonderful things that fall out of it that again took me a while to get there, but it was another one of those oh, wow, this just naturally falls out of the architecture, but it's so nice and such a nice incremental advance on top of frankly, the stuff that I was doing in Rails historically. JONATHAN: So the way that Laravel works and it's always worked this way is when you make a request using POST or PATCH or DELETE or whatever to an endpoint, and that endpoint does its validation in the event that that validation fails, this is just like built-in standard like Laravel Stock behavior. It automatically redirects you back to the endpoint that you were on. So if you're on the create page or the edit page, it automatically redirects. That's just Laravel behavior. And what it does is it takes those errors that come out of the validator, it flashes them to the session, and then when the forum page reloads, you have those errors available to you in the session. Now, of course, if you're building like a classic server-side rendered application and you redirect now back to your form, you have to repopulate old form inputs, which is not a lot of fun, which you don't have any of that stuff with Inertia because Inertia allows you to preserve your state. But anyway, that's a separate thing. But for me, it’s like you build a tool a little bit like in your own silo and the world that you know, and for me, that's Laravel. But there are also ideas that you get that just come from the tooling that you use, and the fact that Taylor Otwell made that decision in Laravel at one point is absolutely what now dictates how is the go-to way to do it in Inertia, just because it works so nicely. CHRIS: I wonder if there's been any consideration in the Rails world to adopt that because I think from an experience perspective, it feels like it's a better thing. It feels like it has the same robustness and guarantees that I would expect. But yeah, that's interesting. It makes sense that that was just naturally there because, again, it didn't feel like the obvious correct thing that Rails was doing. It was always a little bit odd and so interesting that Laravel was already there. But then Inertia can take it that one step further. But taking a slightly higher level view of all of this, one of the things that's really interesting about Inertia to me, especially in contrast to some of the other frameworks that we've been talking about like the Livewires in the LiveViews is Inertia is almost at its core a protocol more than it is…it's a sum of pieces, and with Inertia, you have a server-side adapter, so there's the Rails adapter and the Laravel adapter. And then, on the client-side, you have a separate either Vue or React or Svelte. So those are the officially supported ones on both sides, but there's also been a swell of community support. And so there's a Django one, which I'm not sure if it's currently maintained, but I just saw a Clojure one the other day. There's a Java Spring Boot. So those are all server-side adapters. I haven't seen as much on the client-side, but I imagine there are at least a handful of them out there. And it's so interesting to me that there's this core idea that you define this protocol of communicating back and forth from the server to the client and now this collection of things that are growing around that. And I wonder again, how much was that purposeful versus how much did that just happen? And then further to add a second question to complicate things, how are you thinking about managing that community? Because my sense is that this could allow for Inertia to be so much of a bigger tent and really bring in the best ideas from all of these different communities and end up with something at the core of this Inertia thing that is the best of every community and all of that. So yeah, a lot of questions there, but I'll hand it over to you because I'm super interested. JONATHAN: So I think when I first got going, it was Laravel and Vue; those were the tools that I worked with. And often, the best software and the best open-source software in my mind comes out of trying to solve something for your own needs. So that's really where Inertia came from and specifically for Laravel and Vue. But I quickly realized early on that it didn't have to be just a Vue and Laravel thing. So intentionally early on, I had this idea of trying to build it with multiple adapters, and I had this idea that you could build as many server-side adapters as you want and as many client-side adapters as you want, and maybe we'll officially maintain a certain amount of those, which is what we do right now. We officially maintain the Vue, the React, the Svelte adapters. And then, we also maintain officially the Laravel and the Rails server-side adapters. So that was, I would say, pretty intentional. And it's crazy how many server-side adapters people have been able to put together. Somebody wrote a ColdFusion server-side adapter for Inertia. I had no idea ColdFusion was even a thing anymore; yeah, legit. There are node ones; there are Phoenix ones; if you can believe it, there's a WordPress one, which I'm not even totally sure even how that works. There is ASP.NET. CHRIS: [chuckles] JONATHAN: Like, there's a whole bunch of them. And it's actually despite of me, not because of me, that this has happened because I am yet to write a good here's how to build an Inertia server-side adapter in the language and framework of your choice guide. It's been on my to-do list. I have a bunch of things I want to do. So it's still something I want to write, but people what they're doing is they're just reverse engineering what we're doing in Laravel and Rails and these other adapters, and they're figuring out how to do it in their own server-side language and framework. So that's been really, really cool. On the flip side, on the client side, I'm starting to realize more and more that that's actually where the most important work for us as the maintainers of Inertia that we need to focus our efforts on because it's non-trivial to create these client-side adapters. And for us, we actually have four of them now because we have React and Svelte, but then we have Vue 2 and Vue 3. And they're different enough those frameworks that we actually had to create a separate adapter. So that's really where all our work is. The core of Inertia is actually ridiculously short, like the whole file, like the whole core Inertia adapter is 150 to 200 lines of code. And maybe it's a bit more than that, but it was that for a long time. It might be 300 or 400 now. It's very short. Even honestly, the client-side adapters are pretty short too. It's just that it's more difficult to make these client-side adapters because you get to learn all the intricacies of how each one of these frameworks handles their rendering. The core behavior that Inertia uses is the fact that you can dynamically swap components. So we dynamically swap page components when you visit from one page to the next and the details that come along with that. Anyway, so I’ve realized that moving forward, my job is going to be to make sure that the client-side adapters are awesome and then letting the community drive the server-side adapters a little bit more and providing some better guides on how to do that. But yeah, for now, it’s like if we can get it working in Laravel and Rails, we should be able to get that functionality working in any server-side adapter. And because it's all again just based on HTTP, that's the language, that's the protocol like you say. That's the thing that matters between all these web frameworks, which they all, of course, support since they’re web frameworks. CHRIS: I think you're not giving yourself nearly enough credit for the support that you've given to the server-side frameworks because you do actually have a page in the documentation called the protocol that does a great job of at least summarizing it at that HTTP level. But at the end of the day, again, like the job of someone implementing it is to then map that into their given language and framework of choice. But yeah, the documentation is impressive in just how much you put in there and how much care you obviously put into it and lots of nice, subtle details that are covered very well in that. So that again, if you read it, unlike me, then you get to know everything; eventually, I got there. I think I've read the whole thing now. But there's a lot there, and you cover all of the details. But actually looping back to a topic that you hinted at earlier, but this is something that I've been pressing up against lately is I absolutely love building web apps in Inertia, but there's often the need to bring in a mobile app, and we want native mobile for various reasons. I love the idea of progressive web apps, and I want to push that envelope as much as I can. But as an example, right now, iOS does not support push notifications to PWA. So if that's a key feature that we want, then we're dead in the water or if there are certain GPS things. There are a bunch of true platform native things that we just can't get. And so I'm now contemplating building out an app alongside my Inertia web stuff, but I want to build a React Native app, and I'm wondering, to a certain degree, does this invalidate some of my ideas? I know you hinted at this earlier, but I think I'm still convinced of the utility of Inertia on the web. But I think I need a different paradigm to build for a mobile app, and I'm trying to decide where that line falls. I'm also wondering if I can just get away with embedding a bunch of web views and reusing my web logic because, again, if I'm building all of this, I'm going to build it in a mobile responsive way. I don't want to rebuild the core page functionality of my app just to put it on mobile. Maybe mobile folks would tell me I'm wrong there, but I'm interested in maybe wrapping it and getting access to those platform features. But yeah, I'm interested in what your thoughts are there. JONATHAN: Well, embedding a web view within a native app has been proven to work just as DHH, obviously. But yeah, there are definitely people who disagree with that approach and feel like you should build a legitimately native app. Let's say that we're going to legitimately build a real native app. We want to have an Android and iOS app. So I actually ran into this myself for my own SaaS application, and I solved it by building a native app using React Native so React Native obviously being an abstraction on top of iOS and Android and all the tooling there, which is such an amazing platform. It was just a real joy to work with. And I don't even hardly work with React, and I was able to get a nice, high-quality native feeling app built relatively easily. But I had to come to grips with this very question because, like I've been saying all along like, “Inertia is great because you don't need to build an API. Yay, this is amazing. This is what you should do. Oh, crap. I need an API.” And I had those questions like, okay, well, does this invalidate everything that I've been doing? So I was thinking about it, and in the end, what I did is I just built a light API alongside my Inertia application. So what it is is I think I have seven endpoints, and they're just REST endpoints that are designed specifically for my native app. And this works honestly so well. And I think I've explained to you a little bit in a previous conversation, so I'll repeat myself a little bit here for the benefit of the listeners. The reason why I think it's completely legitimate to have Inertia and build your entire web app that way and then have a companion API alongside it in the same monolith app (let's be clear: it's in the same application. It's in my Laravel app, or it would be in your Rails application) is because it just extends a core principle for me of what Inertia is. And that core principle is a tight coupling between my data layer so my controllers, and my views. So if we take that thinking where we say, well, Inertia in an Inertia web app when we have an endpoint, we hit the controller, we load data from the database, we pass that very specific data to the view, which is Vue or React or Svelte and it renders it. And there's a very tight coupling between the two. And I treated my native app in the exact same way. I said, “Okay, I need an API because obviously, the native app on iOS and Android has to make an HTTP request to get this data somehow. But instead of trying to create this super generalized API that could theoretically be used for anything, I use the same principle that said I'm going to allow myself to create an API that has a really tight coupling between the screens in my native apps and the actual data that's coming from those API endpoints. And this worked out really, really, really well. I don't have to deal with a lot of the issues that you run into when trying to create a more generalized API because I could just say, “Hey, I have this calendar page, and I want that calendar page in my particular app. I want it to show people's birthdays, and I want it to show wedding anniversaries, and I want it to show custom events and these things that we have called schedule reminders.” So it’s data from four different endpoints. I didn't try to say, “Well, I'm going to go and create now my event’s endpoint, and my birthday's endpoint, and my anniversary's endpoint, and my schedule reminder’s endpoint,” and now have all that work to do in my native app to okay, we'll hit all these different endpoints and merge it all together; it wasn't like that at all. I created a calendar endpoint that returned all the data that's needed for that screen. And I basically applied that thinking through my whole native app, and it was really a joy to work this way. So I think that approach works really well if you have an app that doesn't have complete feature parity with your web app. And I think if you had a native app that needed absolute feature parity between the native app and the web app, then my thinking might be a little bit different on this. But in my experience, so often, native apps have a vastly reduced subset of the features that the web app has in particular, even if not for the core functionality of the application but just for the administrative side of it. There's a whole bunch of stuff that you tend to have in a web app around administrative stuff that you literally never need. And I mean administrative both in terms of it's a multi-tenant style app, which most apps are so in terms of the user's administrative functionality and in terms of the system level, the software owner administration. If you build your whole web app to be built on top of an API, all that administrative stuff that really doesn't need to exist in both places, you now have to make it exist in your API because you've made that decision to build it that way. Whereas if you just stick with Inertia on the web and just build it using that classic monolith way where you get data from the controls and send it to your Blade views or in this situation, client-side page views, and then you just expose the stuff that you actually need natively, for me personally, it's worked out so well. And if I look at my own web app, the amount of controllers that I have for the whole web app, I have like 100; it's a very big app. And for my native app, I have about 10. So that was like, I'm so glad that I didn't have to create 100 of these in both places. And then some people will be like you might be thinking, well, now I have duplication. I have duplication in some of my API endpoints and my web endpoints, and that's true. I would say first that duplication isn't always a bad thing. I think more duplication in our web apps would actually probably lead -- I feel like we run away from duplication too quickly. I don't think duplication is as bad as software developers often think it is. But even then, if with the duplication you can't live with yourself, there are still ways to solve duplication. So Laravel, for instance, has this concept of they're called API resources, which is basically they're essentially transformers. You give it some models, and it transforms that model into some other states, some other design. So there's nothing stopping you. And I even did this myself within my server-side application within Laravel to have an API resource to have a transformer that's used by both my Inertia controller and my API controller in a couple of situations and for me, only when it makes sense. I'm not going to do it all the time because I found that most often, I wanted the data in a slightly different format in my native apps than I'd want it into my web app. So quite often, that didn't happen. But I'm just saying if you're scared of duplication, there are totally ways to solve it. And we can solve this in our existing frameworks. Laravel or Rails has ways to allow us to abstract some of that stuff and reuse it in multiple places. So, yeah, that's my long-winded answer to how I've approached doing the native apps sort of thing. I think that tight coupling between the data and the screen I think that's a really nice thing, and you just can build faster. And just like you can build faster with Inertia on the website, you can build fast [inaudible 43:19] CHRIS: Frankly, that answer makes a ton of sense one and two, makes me feel better about the path that I'm on because, again, I'm really desperate to cling to Inertia for the web side of things. So I love what you're saying. And again, it really resonates with me and how you're thinking about building. There's also I really appreciate a subtle common theme that I've seen in a bunch of things that you've said where you're like; let me poke at best practices a bit and see what falls out. What if we were to actually embrace the coupling between our data and our view layer? And it's like, actually some really nice things happen there. And actually, going back to an earlier project that you worked on, Tailwind CSS is one of those projects that when you first see it, you're like, well, that's obviously wrong. That's definitely an incorrect way to do things. But then you explore it, and you're like, well, I mean, I know there are trade-offs here, but actually, in my experience and I'm sure in your experience, Tailwind is absolutely fantastic. And the trade-offs you totally win in the long game, and it's maintainable, and it's understandable. And you can continue to develop on top of it in a way that I've never found with any other CSS framework. But again, at first glance, you're like, ooh, that's not right. That can't be right. JONATHAN: 100%, exactly. I think it's fun to push back and just experiment with different things. And for me, I think a lot of my decisions, too, come back to the fact that I'm running a SaaS application as one person, and I need to be able to move fast. I don't want to have two different servers and two different repos. I want to be able to build my applications as fast as I can, as a single developer, a single founder. And so I think the things that I push against and try and experiment with come out of me trying to find the simplest ways to maintain things. So Tailwind, that's really Adam's brainchild. I came along in the first six months or so; me and him built it. I was really just helping him flesh out his idea there, and that was super fun. But yeah, I had the exact same experience as you. Adam was telling me about this, and I'm like, that sounds pretty terrible. Like, I have CSS figured out already. And then it was like, oh man, this is amazing. Fun little fact, my SaaS app, me and him, were both working on web apps at that time. So my SaaS app was one of the first Tailwind applications ever because I and Adam were literally both building our own apps while building Tailwind CSS. But anyway, so yeah, it comes out of not me trying to be like, I know better than other people; it's not that at all. It's more just I'm trying to find a way to survive as a business and trying to build at the same time, not only survive but also I want to build awesome products. I don't want to build software that is just kind of okay. I love striving to make software that's just exceptional that delights people that works the way someone expects it to work. And I just think that there's so much broken software out there. There's a lot of bad software. And don't get me wrong, I've created a lot of bad software, too. But I really try to hold myself to a high standard. And really, for me, that comes down to not necessarily what some purist says that “This is how you need to do it.” It comes more down to like, okay, let me see the results. How fast does the webpage open? What's the performance? You mentioned my course earlier. I’m really, really interested in database performance and how to use databases more intelligently to deliver really fast web applications. And that matters to me because customers hate waiting. They hate it. And that was even part of what drove me to create Inertia because I hated this. I was working for a company, and we had built the right way where we have an API and the client separate And we went down that road. And that was a big team with 20 to 30 developers in the end. And I was just like -- I shouldn't say, “I was,” but we, in general, were not happy with what happened because just the way that the app was built and the way that single views were hitting the API. You could probably argue that this was like we were doing something wrong, but the paradigm didn't lend us to doing it right, in my opinion. So we'd have pages that were hitting the REST API with sometimes 10, 20 HTTP requests just to get the data. And you're dealing with all the loading states of all this stuff. And of course, there were probably better ways to design, but we were trying to ship a product there too. We were trying to get it out the door and make happy customers. And I didn't feel like that way was helping us. I think GraphQL, just as an aside, is a huge step forward where you can say, “Hey, here's all my data in an API, but I'm not going to hit the user's endpoint just to get back whatever you decide to give me.” I can be much more intentional about saying, “Hey, I want this data and then pull in this relationship for that data and this other piece of data.” And I think that's really, really cool. But I think the problem there again is you need to build that GraphQL API, and that's non-trivial, not to mention you probably have to figure out OAuth, which is pretty much always a game-stopper for me because if I never have to work with OAuth in my life [laughs] I'll be totally okay with that. I know it has its place, but yeah. CHRIS: There's a clear passion and a desire that you're describing there to just build good things and the belief that it can be done. And then, as someone who has really benefited from your work, I thank you for carrying that torch and for pushing the envelope. And like you said, having that high standard and holding yourself to it but then hopefully bringing the rest of us along, and I really appreciate that. But I think with that, that's probably a perfect time to wrap up. If folks want to follow more of what you are working on, where can they find what you're up to on the internet? JONATHAN: I'm on Twitter, the classic place to go for following someone in tech, so twitter.com/reinink, my last name. That's R-E-I-N-I-N-K. So that's where even if I have stuff shared elsewhere on the web, that's where it starts. CHRIS: Perfect. We'll include links to your Twitter as well as everything else that we've mentioned in this episode in the show notes. So folks that do want to keep up or investigate further listening to that other podcast episode that I mentioned will have all of that available. But with that, thank you so much for your time, and yeah, again, really appreciate you joining. JONATHAN: Thanks so much, Chris. Pleasure to be here. CHRIS: The show notes for this episode can be found at bikeshed.fm. If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or even a review on iTunes, as it really helps other folks find the show. If you have any feedback for this or any of our other episodes, you can reach us at _bikeshed on Twitter. Or you can reach me @christoomey, or you can e-mail hosts@bikeshed.fm. Thanks so much for listening to The Bike Shed, and we'll see you next week. This podcast was brought to you by thoughtbot. thoughtbot is your expert design and development partner. Let's make your product and team a success. Support The Bike Shed
undefined
Apr 27, 2021 • 33min

290: Can You See My Secrets?

On this week's episode, Chris and Steph discuss testing webhooks, the challenges in replicating third-party data, and troubleshooting unexpected side effects. They also respond to a listener question about secrets management, touring popular solutions and discussing the trade-offs. This episode is brought to you by ScoutAPM. Give Scout a try for free today and Scout will donate $5 to the open source project of your choice when you deploy webhook.site git-secret Datomic 1Password Secrets Hashicorp Vault Heroku Secure Key Support The Bike Shed
undefined
Apr 20, 2021 • 38min

289: Have You Ever Ridden a Horse?

On this week's episode, Steph and Chris tackle a pair of questions -- the first dealing with how closely we might want to map an API to the underlying database schema, and the second dealing with back of the envelope math and horses (it makes more sense in context.... mostly). They also discuss the subtleties of the javascript date API across browsers, and a quick adventure in tuning database indexes for fun and profit. This episode is brought to you by ScoutAPM. Give Scout a try for free today and Scout will donate $5 to the open source project of your choice when you deploy Testing LiveView Inspecting Postgres Indexes Postgres Partial Indexes Postgres Create Index Concurrently Sponsored By:Scout: Give Scout a try for free today and Scout will donate $5 to the open source project of your choice when you deploy.Support The Bike Shed

The AI-powered Podcast Player

Save insights by tapping your headphones, chat with episodes, discover the best highlights - and more!
App store bannerPlay store banner
Get the app