316: Constrain and Refactor
The Bike Shed - Ein Podcast von thoughtbot - Dienstags
Kategorien:
Chris finally got his new computer! 🎉 🎉 🎉 He gives his initial review. He's also super excited that GitHub announced a beta for pull requests merge queue, and even more excited that multiple people who listen to this show very kindly pointed that out to him on Twitter! Steph discovered something that is quite niche, but she's excited to tinker with it more, called CookLang. It's a markup language that's designed for cooking and recipe management so you can store recipes and text files and there's no database required; making it easy to have control over recipes versus storing them in a separate application. Then they answer a listener question about refactoring murky legacy code. 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. CookLang – Recipe Markup Language Pull Request Merge Queue Limited Beta | GitHub Changelog After_party - Automated post-deploy tasks for Ruby/Rails Therapeutic Refactoring by Katrina Owen Unused - Identify unused code in Rails, Phoenix, and other types of applications. Become a Sponsor of The Bike Shed! Transcript: STEPH: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Steph Viccari. CHRIS: And I'm Chris Toomey. STEPH: And together, we're here to share a bit of what we've learned along the way. Hey, Chris, what's new in your world? CHRIS: What's new in my world? Well, we've talked about it before, but it has finally happened. I finally got a new computer. STEPH: Yay, yay. CHRIS: Five years in the making. I held out, I waited. The new computer is fantastic. I'm in that transition phase of trying to set everything up and get it all...the particular thing holding me back is actually this recording and some dongles. I need to live that USB-C life now. Everything needs new connections and whatnot, particularly my external monitor. STEPH: I'm now realizing how old your current laptop is. CHRIS: [laughs] Did I just date myself? Yes. STEPH: You did. You just dated it with a USB-C. I thought you were still on the USB-C life. CHRIS: I'm pretty sure it's a 2016. I'm currently recording on a 2016 MacBook Pro. But yeah, I'm very excited with the new one. The shape of them is weird. I did not expect this because I've seen the 13-inch MacBook Pros that have the touch bar and other things that I didn't really want. But the shape of that laptop was more familiar to me. And this one, I don't know, it's weirder and rounder and bulkier in ways that I didn't expect. And it's heavier than I expected. I got the 14-inch, as an aside. I went with a slightly smaller version assuming that my 16-inch with a giant bezel, because it's from the past, would have a similar amount of screen real estate to a 14-inch with no bezels or with the screen going almost out to the edges. As an aside, the notch in the top of the computer screen is ridiculous. I've dealt with it on the phone for a while now. I accepted that I live in the land of notches. But somehow, it's way, way worse on the computer like when I take my terminal full screen most of the time, and so stuff just gets lost. I don’t know, I got to deal with this or not, maybe I can just not care. But it is covering things that I want to read. And I'm like, well, this is annoying. But yeah, beyond the notch, everything else is great. It's a nice form factor. It seems to have great battery life. It's very fast. It goes very fast. It also has...there's more RAM in it. There's more hard drive space and whatnot, so a bunch of the things that I use. Often as we start this recording I'm like, oh no, I wonder if I have enough hard drive space for the recording we're about to do, which I should probably be past that at this point in my life. Well, now I think I am, except I haven't yet ported my recording setup. Nonetheless, I'm very excited. And particularly, a lot of the development workflow tasks like starting up the dev server of the project that I'm on just moves a lot more quickly. It's a lot snappier, and everything is just speedier. The fans don't really turn on. It doesn't get too hot even when I'm doing challenging things, downloading everything from the internet and compiling from source code. It's like, yeah, cool; I can do that. That seems like a thing that's well within my wheelhouse. And I'm like, cool, computer, good job. Glad to have you on the team. STEPH: So I learned something about you recently because hey, we were hanging out in person recently since I was in Boston for a week. That was amazing. And I learned that we have a similar thing where we both like to start our machines from scratch, and slowly (or at least correct me as I'm going through this), we bring things over. But it's not an immediate just port everything from my current laptop over to my new laptop. And I'm fascinated because I thought I was the only one with this sickness. But it turns out that you, my friend, also have this in your life. CHRIS: Well, yes, you are correct. That is the thing that is true. And also, to reiterate, it was really lovely seeing you in the human world as opposed to just in a Skype window as we so often do. But yes, I start fresh every time. But to be clear, it's been more than five years since the last time I did this. So I feel like I can make a bit of an event of it each time I do it. So I'm fine with that. But I do like starting from fresh reinstalling everything rather than trying to copy over an image of the system. I felt a little bit shamed by the operating system because there's the like, welcome to your new Mac. What's your language? What's your WiFi? What do you want to migrate over? And I'm like, nothing. But there wasn't a button for no, thank you. [chuckles] There were buttons that were like...there were two different options, but neither of them were no. And I'm just staring at it, and I'm like, but I would like to not though. I would like to just start fresh. STEPH: [laughs] CHRIS: And I got it from here. I appreciate the effort. Turns out they were hiding it in the bottom left corner, but it wasn't a button. It was like link text, but it was barely emphasized. And on the right were where all the action buttons for every previous step and subsequent step were. So this was like no one would ever want to do this. So we're just going to hide the option over in the corner. Yes, I very much like starting fresh. I like to get the chance to shed some of the mistakes of the past and only bring forward the things that are bringing me joy in the modern-day, so here we are. A lot of stuff doesn't work, by the way. [laughter] I brought over my dotfiles, and things did not work super great. Or I opened an application, and it's like, oh, that hasn't worked for years, and I'm like, I was living in the past. All right, this is fine. I'll update my workflow. It'll be okay. STEPH: I like how you said make an event of it because I haven't really found the right words for it, but that's perfect. I have a fresh laptop, and it's an event, and I want to spend that time setting it up and shutting the mistakes of the past. [laughs] That too, that also resonates. CHRIS: Yep. It's really about the mistakes of the past. I don't want to live there anymore. I want to move on from them. But overall, yeah, it's going well. I think I'll be able to work with it. I'm actually unfortunately limited in that I can't connect it to my monitor right now or to the keyboard that I have and whatnot. So I can't quite integrate it into my home office setup. I can use the laptop itself, and it's working fine. I've got my current project running on it. So I was able to install Ruby and figure out how do I trick it into using the Homebrew things versus the M1 things versus which architecture and yadda yadda yadda? That actually went better than I expected. I thought it was going to have more issues there, but stuff just kind of worked. And I had to find one weird Stack Overflow and copied and pasted as one does when you're setting up new computers or all the time as a programmer. And then yeah, we're off to the races. But yeah, unfortunately, right now, the limitation is a physical cable. I need a couple of new cables. So I'm excited to get those in the near term. STEPH: Yeah, that'll be nice to have it set up and feel like you can fully transition to the new setup. CHRIS: But in slightly other news, so computer is great, very happy about that. Perhaps I’m even more excited that GitHub has announced a beta for pull requests merge queue, and even more so excited that multiple people who listen to this show very kindly pointed that out to me on Twitter. And I was like, this is the dream. I just rant about stuff on a podcast, and then people tell me when the world has solved the problem that I complained about. This is so great. But it's a public beta, but you go on an invite list and whatnot. So I'm on that list right now. And I'm trying to just mention it as much as possible, especially near any friends I know that happen to work at GitHub; just be like, "Hey, if you happen to know how to find that list...and I will give wonderful feedback. I will be an active member of this beta." But I would really love to get to try out that feature. So I'm excited. We'll include show note links to the blog post announcing this new feature. So I'm really excited that it is built into the platform and will sort of be there. And I'm hopeful that GitHub has done a great job. This is actually a really interesting feature in my mind. It's one of those things like, oh, it's pretty straightforward. You just make a queue, and then you merge things. It's like, oh yeah, but what about all the ways it can go wrong? It's a great I think iceberg feature where the simple, happy path is wonderful. And then there are so many different failure modes like, oh, what do you do when the PR fails to rebase? Or should you proactively rebuild the sequence of them? Do you stack them up and start preemptively rebuilding the other PRs that are next in line in the queue after that? But what if then it fails? And et cetera, et cetera. There's lots of fun stuff that can go wrong. So I trust that GitHub will do a wonderful implementation. I would love to get to try that, but currently, I've yet to get in. STEPH: Have they shared how to get access to the limited beta? Is it just random selection, or can you specifically request? It sounds like you can't, based on what you're saying, but I'm curious. CHRIS: There is a waitlist. Particularly, they had me put the repo that I was interested in, so like, I would like to be in the beta. And this repo, in particular, is the one that I would like to put into the beta. So I've submitted that, but now I'm just in a list. I don't know where in the list I am. I have no visibility into that. So again, I'm just saying things out into the void and seeing if the universe then reflects anything back at me, and by that, I mean people that work at GitHub. Hi, friends. STEPH: I totally believe that if you speak things into the universe, the universe will give that back to you, or that's probably not true. So here's hoping that you get into the beta list. CHRIS: Here's hoping. One other tiny thing I have been using Afterparty a bunch lately, which is a gem that you have recommended to me, I believe, in a previous episode not that long ago. But we started doing feature flags. Feature flags are great. We're using Flipper. It's wonderful. But Flipper stores...the way we've configured it, we're storing them in the database. And so, when we get to the point where we want to fully release a feature, we dial-up so that everyone has access to it. So at this point, the feature flag system is still running, but everyone is getting access to that feature. And so then there's a code change that removes any references to the feature flag, any checks for it. And subsequently, we want to then clean up after ourselves. Afterparty has been fantastic. I'm really happy with the way that that works and that we can just include the Afterparty data migration associated with that, delete the record from the database, and then everything consistently works together. This does poke at the edge, though, of how Heroku does deployments and the fact that there is that latency where it will basically restart with the new code, needs to run all migration steps, et cetera, et cetera. And then it's running on the new code, which is great. That's what I want in this case. But it does have a cost, and I would love to figure out true blue-green deployments sometime in my usage of Heroku such that we can have zero downtime deployments is actually the way to phrase it. Using Afterparty for this, I think, is leveraging the idea that it's not a big deal. It'll be fine. Because if we had zero downtime deployments, I think temporarily, we would be in a space where feature flag has been deleted; therefore, no one is in the feature. Therefore, no one would see it for that weird second. I'm safe from that. But it's this weird trade-off that I have in my mind of I want blue-green deployments, but I'm appreciating that I don't have them right now and that there is a consistent...and I think that's the reason that Heroku makes the choice that they do. But, man, everything's complicated. Why can't stuff just be simple? STEPH: Everything is complicated. So I'm thinking back through to now my previous client's setup since I'm about to transition to a new project. And we have AWS. We have rolling deploys, so we don't have that specific downtime. We are using Afterparty. So I think based on everything that you're saying that yes, there's a slight period of where we are rolling out a feature flag or potentially dropping it, and that could put some users into a weird state if they're active and then the code suddenly changes. I'm thinking through out loud and singing it because apparently, that is what I do. We haven't run into any issues with that. But I am now trying to think through of when someone might end up in a weird state because of that. CHRIS: You almost certainly won't run into any issues because we're talking about a matter of seconds where the code is in an inconsistent state. But the same thing applies to migrations like database migrations where if you're doing rolling deployments and the database migration hasn't happened yet, but there's new code that's running that expects that database state, then you're in this subtly inconsistent mode. And I think if you really want to adopt this idea of rolling deployments or zero downtime deployments, you have to separate data changes from the code that uses that data change such that both versions of the code when the migration goes out are fine. And then later, you deploy the code that uses the migration, but then that's like a whole bunch of more steps. And you got to think about it, and you have to probably put in some pull request review step to check on it. And so again, it's just complicated. STEPH: So that's one of the reasons we did change our feature flag implementation was because we realized it was a pain. Because anytime that we wanted to drop feature flags, we first issued a PR that removed any references in the code to that feature flag, let that deploy. And then we'd issue a separate pull request that went out in a different deploy that went and dropped that column. So that way, we didn't have any situations where maybe ActiveRecord has cached to that column, and then there's code that's looking for it, but then it's not actually there. And then you run into that funkiness and complicated behavior. So we were at a point where removing a feature flag required two PRs and two deploys, and that was awful. So we switched up how we were handling feature flags specifically so we could avoid that and started storing them in JSON files because we took the more bespoke approach to feature flags versus using Flipper. CHRIS: Bespoke artisanal feature flags. STEPH: I don't know that I'd recommend it. It's working. It's been a journey, and there are things working well about it. But it's definitely still in that space where it's like, I'm a little uncomfortable of like, how far should we go in this bespoke world and at what point we should reconsider and use something like Flipper. I would say stay tuned, but I'm not on that project anymore. So I'll just never know. [laughs] Sounds like a devious laugh. I will know probably from some friends that are on the project, but I won't be there for it. [laughs] CHRIS: Cool. Well, yeah, I think that sums up things in my world. What's new in your world? STEPH: I have discovered something that's quite niche, but I'm excited to tinker with it more. It's called CookLang. And it's a markup language that's designed for cooking and recipe management. So you can store recipes and text files, and there's no database that's required. So it's easy to have control over your recipes versus storing them in a separate application, which is currently how I store my recipes. Which for the record, I'm happy to pay for software. I am very appreciative of when people make my life easier. And so I very much enjoy that. But I also still love the it's mine, and I can just have it stored somewhere in the cloud, and I'll never lose it. And I don't have to worry about renewing memberships to keep access to something. So there's a part of me that is very drawn to the idea that I can just have everything in the text file. I can store it anywhere that I want. And then, for this particular CookLang markup language, it lets you define certain qualities of your recipe. So you can define the ingredients, the quantity, cookware, timers. It'll help you create shopping lists, and you can set metadata. So maybe you want to include the total prep time or the type of meal, or the number of people that the recipe serves. And they also have an iOS app that is in beta. So speaking of beta, this one is closed at the moment because they've had a pretty overwhelming positive response or interest. So they have shut it down for now in terms of accepting new people to the beta; at least, that's my current understanding. But the iOS app does look like it'll be really nice. It's going to read from your files; I think stored in iTunes. But I'm just excited for all of this. It looks very interesting. And it looks like something that's just fun to play with. So I haven't moved anything over to start really investing and creating these .cook files that you use for the cook language. But it all seems very cute, very niche, and something I'm totally into. CHRIS: I have not seen this, but it looks absolutely fantastic. I'm just reading the brief syntax snippet that they have at the top and the way that they're inferring semantic meaning from plain text recipes. And then you can generate a shopping list, super cool. You can just be like; I’m going to cook these things from my recipes. And then they'll tell you the shopping list, and it's grouped. And this feels like what software should be in my mind. This doesn't feel like we're going too far with it, which is very easy to do. But it's like, no, no, we've annotated just a little bit of semantic meaning on top of something. And then with that, we can do wonderful things. Look at all the good stuff that falls out of it. We can have an iOS app. We can generate shopping lists. It's great. But it's also minimal and contained, and not locked in a proprietary format. And this is fantastic. STEPH: There's also the nice part where they've already started highlighting that people can just store their recipes on GitHub. And then you can just fork recipes, or you can import everybody else's recipes. And I love that. I want more open-source recipes, although frankly, there are tons of just free amount of work and recipes that people have on the internet. But I love this because then I could skip all the ads that go with it. And I can just grab the stuff that I care about. CHRIS: You mean you don't want to hear the person's life story before every single recipe. You don't want to hear about the walk that they took along the river when they were thinking about seasonal gourds, and then they decided to make a particular -- [laughs] STEPH: [chuckles] I would normally...previous Stephanie would have been snarky about that in regards to all of that backstory that's given for a recipe. But then I saw somewhere maybe a friend was telling me about someone who had asked, "Why do y'all do this? Why are you putting so much information? Why can't I just have the recipe?" And they're like, "Well, frankly, Google's going to rank us a lot higher if we give you this whole backstory and if we look like there's more content to this page than just a recipe." And I'm like, dang. Okay, so it's Google that I have to be snarky about because they're doing their best in terms of trying to get you the recipe that you care about and make it very searchable. CHRIS: Oh, yes. Absolutely. It's both a self-imposed problem. But also, I don't think Google's doing anything wrong here either. Google's part of their algorithm is they penalize duplicate content. And so there's what they view as the canonical source, the thing that's telling the truth. And then recipes look very much the same because they are kind of the same most of the time. This is how you make bread. This is also how you make bread. Two people now have almost identically the same content on the internet. And Google will penalize one that it determines to be copying essentially, which I think is a good thing broadly. I've seen so many Stack Overflow...I want to say clones, but they're not even that. They're just literally taking the content and then republishing it entirely new. And that is a very bad thing that happens constantly and takes away from creators and whatnot. And so, in a way, Google's trying to do the right thing here, I think. But it leads to really just so much summary about recipes. It's one of those weird...is this a tragedy of the commons? I don't know. I've never quite understood that one. But I think it might be. STEPH: Yeah, I don't know. That's my off-mic answer. [laughs] CHRIS: That's fine. Someone on the internet will tell me whether or not this is a tragedy of the commons. But either way, I do not blame the recipe authors. I do not blame Google? You'll note the question in my voice. But I think I stand by that. I'm not sure who to blame. Maybe it's us. But here we are. [laughs] STEPH: Yes, I like that final takeaway of let's not blame the people that are trying to give us wonderful, helpful content. Circling back a bit to some of the code that is powering CookLang or some of the interesting bits about CookLang, for anyone that's interested, you can also visit the GitHub repos they have. I believe everything is open source. There's a particular repo called spec. And you can view the issues, and you can see all the conversations around how should we annotate an ingredient? Or should we have comments in the language? And how should we handle this type of metadata? And I find that interesting because I haven't really participated in any of the language crafting forums for Ruby or some of the other languages that I use. So it's neat to watch how some of this is being done in public to say, hey, we're just making this new markup language, and we're not really sure how we want to annotate everything. So what do people think? What's working for you? What's not? And I'm enjoying that conversation and reading along. CHRIS: It really is such an interesting microcosm of an example of a language, an ecosystem, a community, a specification, and all of the stuff that is true of all of the other languages that we use more generally. But I'm looking down here, and there's the parser implementation. There's a Swift canonical parser. But then Rust, somebody is rewriting this in Rust because, of course, they are. But you know what? It's going to parse your recipe so fast, and it's going to be great. [laughter] But it does have the shape of everything that I've seen from other programming language conversations. And how do we evolve the language, and what syntax do we accept versus what do we not? And this is so interesting. STEPH: Yeah, it's really neat. I'm really into it, and I'm really enjoying it thus far. Mid-roll Ad And now a quick break to hear from today's sponsor, Scout APM. Scout APM is leading-edge application performance monitoring that's designed to help Rails developers quickly find and fix performance issues without having to deal with the headache or overhead of enterprise platform feature bloat. With a developer-centric UI and tracing logic that ties bottlenecks to source code, you can quickly pinpoint and resolve those performance abnormalities like N+1 queries, slow database queries, memory bloat, and much more. Scout's real-time alerting and weekly digest emails let you rest easy knowing Scout's on watch and resolving performance issues before your customers ever see them. Scout has also launched its new error monitoring feature add-on for Python applications. Now you can connect your error reporting and application monitoring data on one platform. See for yourself why developers call Scout their best friend and try our error monitoring and APM free for 14 days; no credit card needed. And as an added-on bonus for Bike Shed listeners, Scout will donate $5 to the open-source project of your choice when you deploy. Learn more at scoutapm.com/bikeshed. That's scoutapm.com/bikeshed. STEPH: So, changing gears just a bit, we have a listener question that frankly is a doozy of a listener question. It's a bit long, but I feel like all of the content in here is important. And I feel like most people are going to be able to relate to this scenario that the question is describing. So this question comes from Michael Kopinsky and Ben Rosenbach from Philadelphia. And they write in, "There's one area of our codebase which has a lot of skeletons." I'm already empathizing. "The UI is error-prone jQuery soup, and the UX doesn't entirely make sense. Test coverage is close to non-existent. And the expected behavior isn't clear and has lots of edge cases. We've thought over the years of how we can redo it, but it's never a priority. Redoing it would be a huge lift, and...it works usually. We get bug reports occasionally, which inevitably get closed as won't fix workaround provided. But a few months ago, we received a bug report, and I decided to start cleaning things up. I opened a merge request with a set of factors extracting some behavior into a service object, making explicit some things that were implicit and adding test." Side note, that's some hero work. I appreciate that right there. "It's gone through a few cycles of code review and pairing each time suggesting additional cleanups to do, requesting additional test coverage and that sort of thing. But the more we do, the more we discover that needs to be done. So at this point, the merge request has been open for about eight weeks. I've paired with two other developers for probably 12 hours. The user is impatient, and we're tired of this darn thing and want it to go away. But it feels so bad. It really is severely lacking in test coverage. And the expected behaviors still aren't clearly defined. And it just doesn't meet the normal quality standards that we try to hold ourselves to. We could sit for another 8 to 40 hours and write more tests or documentation, but we're so drained from this whole thing. So overall, we're really struggling to balance two things. We want to get things out the door, have short-lived PRs, and focus on incremental improvement. However, we still don't have defined requirements of how the feature is supposed to work across all permutations. And we can't write proper tests until expected behavior is defined more clearly. It feels like that's a baseline requirement before being able to merge or refactor. We'd love to hear any thoughts. Sincerely, Michael and Ben." Ooh, friend, there is so much we can dive into here. Would you like me to get started, or would you like to start? CHRIS: Yeah, I've got a handful of thoughts because, as you highlighted, this is all of the stuff. This is the murky pile of complexity that writing code and delivering value in an organization is made up of. I'm going to start at a random place and then just start saying things because I think there is not a singular answer to this is an important starting line. We're not going to say, oh, if you just did X, then it would be fine. Obviously, this is a number of different complexities and situations, both person or both interpersonal human, and then code, and et cetera, et cetera. So one of the things that actually came in the latter part of what you were reading there is we do not have defined requirements as to how this feature is supposed to work across all permutations of how it can be used. And that one is really interesting to me because right now, I wonder if there's something that can be done there. So often, you have code that does some stuff but also could do other stuff. And it's not clear if it's actually intended to do that other stuff or if that's just something that falls out of overly permissive like, we'll take any data you throw at us, and then we'll do some stuff. Typically, we don't actually want that. We want a more constrained system that's doing something. So that particular part was really interesting. Some of the conversations about testing were really interesting. I think one of the things I would do is I would really ask, is this worth the effort? This code is kind of doing its thing. It's been around for a while. It's business-critical, but nobody really understands. It's like, is there a version of just leaving it at rest? Probably not. I typically would not do that. But it is a question that I think is worth asking because it can be really hard to make these changes on an under-defined system written in legacy technologies and whatnot, especially ones that we don't necessarily have as much...like, I haven't worked in jQuery for a while. So I wouldn't be quite as good at that. I wouldn't be able to understand that code as intuitively. That might be similar for this team. So is there a version of just letting it lie? That is the first question I would ask. Presuming that's not the case, then I think I start to look at what is the attack that I would take to approach this. And I would definitely start with, hopefully, some small but meaningful introductions of test coverage but as a standalone thing. It's not part of the big change of a pull request. It's merely trying to lock in the code as it exists. Again, I think I've referenced this talk perhaps more than any other, but Katrina Owens'Therapeutic refactoring is a really wonderful story and talk and really talks through this idea in a great way, but also it's just fun watch. But in it, Katrina talks about her approach to a similar thing where she was like, there's some code, and nobody really knows what it does. So I'm going to try and lock it down. And the first thing that she does is wraps test coverage around this code. So at least whatever it's doing now, we've constrained it a little bit. And actually, via the testing that she does, she ends up using that as a mechanism to sort of what does this system do? And finds a way to exercise the system and determine what its behavior actually is because nobody really knows that at this point. So I would start with the testing. And then I would ask the question of we don't really know what it does across all permutations. Is there a way to actually constrain that? And say, let's actually lock some stuff down. Let's add a tiny bit of code that looks at the inputs being requested or coming into this code path or whatever it is but actually explicitly delineates what we think is the expected inputs and rejects or at least logs things that are surprising and that are like, we don't think it should do any of this. And then suddenly, if you see that showing up in the logs, you're like, I guess it does need to do that. Now we have a better idea of what is the actual target that we're trying to hit. But really, both of those in my mind are ways to try and constrain the problem to try and add a little bit of support before tackling the hard work of the refactor because, just to be clear, you're probably going to break it in the refactor. I've broken every single system like this that I've tried to refactor without question. And there's a certain amount of organizational trust that you need to have. And it just gets to be a very complex time. And so anything that I can do to provide some guide rails, some support, something to help me in that time is something that I'm going to start with before I even go near the refactor itself. So those are some thoughts scattered around throughout the question. What do you think, Steph? What comes top of mind for you as you hear this question? STEPH: I think those are great thoughts. And to expand on what you're talking about determining the expected behavior, that's also one of the bits that jumped out to me. And I love that you asked the question, can we leave it at rest? It sounds like yes, but there are some user concerns that come through or some bug reports. And then the team has to collectively say, "Yes, we are going to put someone through a difficult time to either do maybe a gross fix or implementation." And then we're making this area of the codebase feel worse and more accepting of different flows and adding to the number of flows that we have. Or we document it as won't fix and then provide a workaround. So I really like that you said, "Can we leave it at rest?" Because I think that helps a team make a decision together as to like, what is the understanding of this part of the codebase functionality? How important is it to the business? If we decide that it's not that important to the business, then it's frankly not worth anybody's time working on it even if it feels really bad and you know there's this gross code over here, and you really want to go work on it. But if it's not important to the business, then it just shouldn't be done. And it needs to be left alone until the business decides this is important to our functionality. We do want to prioritize this work, and we're going to work on it together. And I still think there are a couple of nuanced strategies even from there. So if you're looking for the more incremental improvements of we can't totally let this lie...we do have to improve it. But we're also not willing to invest in an overhaul situation. So some of the incremental improvements would be, as you'd mentioned, adding some test coverage to it to at least start to lock in the behavior that's already there and establish a baseline of understanding and then document that behavior. I've also found that really helps for context switching. So if you go in and you just document one part of that system as something that it does, then it frees you up. You can add something. You can issue a smaller merge request. You can get that merged because you're just adding test coverage. So it's not going to break anything. You're not making code changes. But then you can nearly let that go. And you can jump to something else that the team or the business has decided that's more important. Some other areas that I've used in the past for working in these types of legacy or just bug-prone areas is to look for flows that are no longer in use. So if there's something that stands out to me as like, I'm pretty sure users aren't using this, or this code just looks unused. Maybe it's running a tool like Unused, which we can link to in the show notes, and see if there's truly something that's unused that you can just immediately remove. Or maybe it's logging a message to Rollbar or something similar. So that way, you know this is actually being used, but you can have it log whenever that's being run. Let that live for a month and then go back and check to see if that flow is ever executed. As for the larger overhaul approach, that really has to be a team effort. So this has to be one of those moments of like the company decides this is important. And there may be smaller wins you can look for, but I do think it's going to require at least a developer or the product manager to be invested and ideally a designer as well. And then some small steps there may be looking for reducing responsibility of that feature. So maybe it's this really verbose long form that's complicated. And maybe you can collect some of that information somewhere else in the app. So you can start to chip away at some of the responsibilities in that portion of the application. Or maybe you have a mini design sprint. You may realize this is a very important part of your application. We should really have this be a better experience for users. And let's really take a hard look at what this does and start to document. And that's the bigger like we're going to rebuild this, or the whole team is going to focus on this and improve this portion of the application. So that was in response to some of the lovely things that you said that you made me think of. The other thing that really jumped out to me is this merge request that's sitting because this person, Michael or Ben, they've already done some really hard work where they have decided to add test coverage. They've cleaned up some things. And I can certainly relate to you issue a pull request and someone...because you've started cleaning up a space, people are like, "Oh, what about that cobweb? And then you missed a spot. What about over here?" And so, how do you make progress when you're in that state? And I would say move the goal line and merge the pull request. So your goal line sounds like right now is trying to address everything which doesn't feel humanly possible. So instead, move the goal line. Figure out okay, I just want to add these couple of tests, or I just want to add this refactor to a function or something that documents this part of the system. And if people are nervous about merging the current merge request because it has gotten so big and people have made so many small requests, is to then break it up into smaller, more focused PR. So then it makes it easier for people to say, "Yes, that does improve the state of this codebase. Please merge it." And I will often prefix my PRs with that. I'll say, "Dear reviewer, this PR is intended to improve the state of the world. It does not improve everything because I can't, but this is the goal of this." So that way, people go in with the mindset of they'll realize they're still going to see some cruft. But this is still leading us towards a better place. And unless there are strong concerns about the changes made in that specific PR, any other requests need to be noted and then can be captured somewhere else, but we're not going to address it right now in this PR. CHRIS: I agree so very much with all of the things you just said. For anyone at home who cannot see us on the Skype call that we're on, I was just aggressively nodding my head the entire time Steph was saying all of that. [chuckles] You touched on I think all of the additional points that I would want to make specific to this merge request is out there in the world and whatnot. The one other tiny bit that I would add to it is I have definitely been the person, and then I've also been on the other side of it where people get attached to the code that they've written. "But I've already written the merge request." And you're like, "Yeah, but can you break it up?" And they're like, "Yeah, but it already is a thing. And I did a bunch of work. And now I'm emotionally invested in this pile of work getting across the finish line and breaking it apart..." We can get caught up in our code. And this is a really great example of this thing just got away from you. This is too big of a refactor. It's too much of a risk. And so again, I have struggled with this personally where I'm like, I'm so proud of this pile of work that I just made. And people are like, "Yeah, but that's a big pile of work there." I'm like, "You're right. You're right. I should break this up. I will break this up." But I can feel that resistance in myself to doing that, what feels like extra work, what feels like almost undoing of work. It's like, "No, no, no. I think it's ready." I'll be like, "I don't know, I'm not convinced. Can we break it apart?" And that's almost always the right decision. But it can be painful. And so, just knowing that and having that in the back of your head as a thing that my brain will tell me the opposite. My brain will be like, no, no, go with it. It's great. We're proud of this. But being open to the idea that breaking things up is good, smaller pieces are good, et cetera, et cetera, can be a useful psychological aspect of this conversation because,, of course, there's one more facet. There are so many facets to this question. STEPH: I like that anecdote about that you haven't regretted breaking things up because I also relate to that where there's that initial like, I've already done this much work. And now you're asking me to do more work. And it's going to have the same outcome where it's still going to be the same code. It's still going to be the same set of changes, but you're asking me to break it up into smaller changes. And I just feel exhausted from this work already. And I don't want to have to break this up. So I understand and relate to that initial resistance. But I also really like the idea that all of these changes and everything going into the codebase is managed by a team. So we really want there to be a level of comfort from the team that this is what's going in. And as the author of the changes, I am far more comfortable with everything that's about to get merged in because I've spent so much time with it. So for me, whenever I do feel that initial resistance, I have to ask myself, well, what's the value? Will this make it easier for folks to review? Maybe it will show some areas that I didn't add test coverage because it felt like it was already getting so large. And so I started to negotiate with myself as to like, well, maybe I don't need to test that because this is already getting so big. And I don't want to add more to it. So I really think through what's the value of if I break it up? And will it make the team more confident in this change? And ultimately if it does, then to me, that's always a resounding yes. So it's just going through what's the value versus what's my initial oh, this is a waste of my time to oh no, this is a good use of my time because it really benefits everyone else. Circling back to something else that was said in the question was the message that we're just so drained from the whole thing. And I really don't want to take that for granted because that's really important. And it sounds like someone has taken their own time and hopefully company time to work on this. But it's not a team initiative. So this does feel like one of those areas where it feels like the team needs to prioritize whether this gets worked on or not. And it's okay if the team says, "Yes, this is important, but six months from now because we need a break from it." But as long as it's something that the team worked on together to say this is important, or it's not, I feel like that's an important part. And also to recognize that we don't have to fix all of this right now. That's really important for the company to buy into it. I have two small things that I want to add, sort of the what not to do because we've been talking about some things that we would do. And so one thing that I definitely wouldn't do is avoid having one person going off and trying to fix this part of the codebase. Don't have one person, either a designer, developer, just a single soul who's like, I can fix this, and tackle it, and define all the things, and improve it. That's going to go poorly. Don't do that. And then also, I would avoid refactoring just to refactor, which I think goes a little bit against Therapeutic Refactoring, which is a talk that I absolutely love. But if we're really invested in proving this part of the codebase, then I think it is very helpful and important to have a fixed user deliverable goal driving your refactor because that's also going to help shape and influence your PRs and encourage the team to take the time to review those PRs and get them merged. So there's a nice coupling there where you want something driving that goal. Granted, it could be different for different teams. Therapeutic Refactoring may work wonderfully. But I've just seen so many people sink a lot of time into something, get very frustrated, then nothing gets shipped. So I would favor leaning more until we have this very focused thing that we're going to deliver, whether it's adding a test or two tests. Have a goal in mind when you start that refactor. CHRIS: Again, I find myself nodding aggressively with everything that you're saying. And in addition, I really like the subtlety that you're bringing to one of my favorite talks. I've mentioned many times that this is one of my favorite talks. But I think you've added a really interesting like, yeah, but also, in the way that everything depends, it also depends. And I think you framed that really well that refactoring for refactoring sake can be fun and therapeutic, and cathartic for an individual. But we have to make sure it's part of the overall work that we're doing. And you've now, I think, really well captured the human side of this work that is captured in what is such a complicated, nuanced question that I think also does such a good job of just highlighting the work. This is what it looks like. This is a particular almost perfect storm of the work, but that happens. But each little piece of this that we've talked about is this is the day-to-day of software development interestingly. I always thought it was just going to be writing for loops. I haven't written a for loop in years. I don't even know how to write a for loop. But this stuff is what I do every day. [laughter] STEPH: The most challenging part for me in software I realized is when a problem has escalated to the point where it's like, I can't just write code to fix this. I need to go to the team. I need to get buy-in. And that's where I start to realize this is where software to me really gets hard because then I need other people to contribute and to get their opinions. And it turns out way better for it. But yes, that is definitely the intersection for me where I can write code up to a certain point to then I really need people to help make really great software and fix some of the underlying issues that we're facing. On that note, Michael and Ben, thank you so much for writing this in to us. I'm very interested to hear how this turns out for your team. And thank you so much. I wish you the best of luck. On that note, Shall we wrap up? CHRIS: Let's wrap up. 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 at @_bikeshed or reach me on Twitter @SViccari. CHRIS: And I'm @christoomey STEPH: Or you can reach us at [email protected] via email. CHRIS: Thanks so much for listening to The Bike Shed, and we'll see you next week. All: Byeeeeeeeeeeee! 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.Sponsored By:Scout: Scout APM is leading-edge application performance monitoring designed to help Rails developers quickly find and fix performance issues without having to deal with the headache or overhead of enterprise-platform feature bloat. With a developer-centric UI and tracing logic that ties bottlenecks to source code, you can quickly pinpoint and resolve performance abnormalities -- like N+1 queries, slow database queries, memory bloat, and more. Scout's real-time alerting and weekly digest emails let you rest easy knowing Scout's on watch and resolving performance issues before your customers ever see them. Scout has also launched its new error monitoring feature add-on for Python applications. Now you can connect your error reporting and application monitoring data on one platform. See for yourself why developers worldwide call Scout their best friend and try our error monitoring and APM free for 14-days, no credit card needed! And as an added bonus for Bikeshed listeners: Scout will donate $5 to the open-source project of your choice when you deploy. Learn more at scoutapm.com/bikeshed.Support The Bike Shed