The Bike Shed

thoughtbot
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
undefined
Apr 13, 2021 • 37min

288: 10x Puppy

On this week's episode, Chris and Steph discuss migrating a polymorphic relationship over to UUIDs and balancing trade-offs between data integrity vs complexity. They also touch on a new Rails feature that adds support to safely remove and add columns, GitHub Discussions, measuring team experiments, and purposeful communication. 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 Modeling Polymorphic Associations in a Relational Database Rails 6.1 - Add support for if_exists/if_not_exists GitHub Discussions Loom: Video Messaging 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
undefined
Mar 30, 2021 • 38min

287: Turn it up to Eleven

On this week's episode, Steph shares a recent performance improvement, a Postgres delight, and testing concurrency in RSpec. Chris revisits an earlier theme of "Good Idea, Bad Idea?" as he explores ways to speed up tests builds and avoid duplicate test builds. They round things out with a listener question about managing ERB partials and Vue components. 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 ActiveRecord - with_lock Domain Name Sanity by Edward Loveall strong_migrations gem react-rails gem Become a Sponsor of The Bike ShedSponsored 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
undefined
Mar 23, 2021 • 27min

286: Time After Time

On this week's episode, Chris shares a rare airing of grievances regarding the importance of secure, encrypted websites and Steph shares a tale of time zone troubles and testing. 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 Time.use_zone Sidekiq Become a Sponsor of The Bike ShedSponsored 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