352: Case Expressions

The Bike Shed - A podcast by thoughtbot - Tuesdays

Categories:

As developers, we care a lot about code quality. How do we know how good is good enough? When do we stop improving code? Alternatively, when working on code that's really bad, how much do you improve it before calling it a day? thoughtbot's Stephanie Minn joins Joël to chat about this and case expressions: We recently discussed these as part of thoughtbot's RubyScience reading group. Are case expressions bad? Are they equivalent to multi-way conditionals? When do you use polymorphism? This episode is brought to you by Airbrake. Visit Frictionless error monitoring and performance insight for your app stack. RubyConf 2022 RubyConf Mini Stephanie's talk at RubyConf 2021 WNB.rb Joël's RailsConf 2022 talk Ruby Science older episode on wizards TCR Transcript: JOËL: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Joël Quenneville. And we're here to share a little bit about what we've learned along the way. Today, I'm joined by a fellow thoughtboter, Stephanie Minn. STEPHANIE: Hi, Joël. JOËL: Welcome to the show. STEPHANIE: Thanks. Happy to be here. JOËL: Stephanie, what's new in your world? STEPHANIE: Thanks for asking. I've been working on writing a CFP for RubyConf, which you have been plugging internally at thoughtbot. I wasn't really sure if I wanted to do it, and then I found out about RubyConf Mini, which is happening as an alternative to the main conference in Houston. And that got me really excited to have some more options and just got me thinking about what I might have sitting on the back-burner that I might want to give a talk about. JOËL: That's really exciting. I'm curious, what is your process for coming up with an idea for a talk? STEPHANIE: I think they come in seasons, ideas, for me, so not even necessarily when it's conference time. But if something has been sticking in my brain for a really long time, especially as it relates to processes on teams that I'm on or my day-to-day workflow, and it's something that I keep coming back to and trying to figure out what it is about it that my brain wants to work through a process, that usually tips me off that this might be something that other people are thinking about or working through. In this case, I am planning to write a CFP about pair programming. And yeah, it's something that I've been thinking about doing for a while, and it seems kind of an evergreen topic. So I thought I would pull it out for this conference. And if it doesn't end up getting accepted, I can always resubmit again in the future. JOËL: I love that. So it sounds like you have a note or maybe an actual written notepad somewhere where you just, over the course of the year, build up ideas, and then you take a look at that when conference time comes around. STEPHANIE: Yeah, that's about it. I have just a very long-running note of half-formed thoughts. And then, when I give myself time to really reflect on how things have been going at work, I usually revisit it, and if any of them still resonate or stand out to me, I will go through and try to see if there's any content to come out of that. What about you? I know you are an extensive note-taker and ideas blogger. JOËL: I have to say I really like your approach of gathering ideas throughout the year. I've worked with many people who would love to give a talk at a conference as a professional goal but then get stuck in the I don't have any ideas. I don't know what I would talk about. And most people have a thing they could talk about. They just don't know it. And it sounds like you've done a really great job of gathering this info throughout the year so that when the time does come, you don't just freeze. You're like, no, here are the 10-20 things that I experienced or that I am an expert in or that I would love to share. And maybe there are two or three in there that would be very well-fitted for the conference you're looking at. So I love that idea. I have not done that myself personally, but maybe I should start doing that. STEPHANIE: What about you, Joël? What's up in your world? JOËL: So I've also been thinking a lot about RubyConf coming up, working on a few ideas for myself. And then, there are a few people that have reached out to me to help them craft ideas or get a little bit of feedback on their proposal. So I've been doing a lot of proposal reviews as well. STEPHANIE: What do you enjoy about reviewing other people's proposals? JOËL: I think for many people speaking at a conference is a really big, ambitious professional goal, and so helping people achieve that is really fulfilling for me. Some people might feel almost inadequate or unprepared. But because I know them, I know they've got good things to share. And so it's almost seeing the greatness in them that they don't quite see yet or that they don't feel confident about. And so being able to see that in their proposal and say, "Oh, there’s a core of a great idea right here, tweak it a little bit, and that'll give you a slightly better chance with the committee and help you towards that path of being on stage for the first time," is really exciting. STEPHANIE: I spoke at RubyConf last year, in 2021, virtually. And I remember that was my first time speaking at a conference. And I was worried that my talk was not super hardcore, technical enough. But my goal for my talk was to aim it towards other developers like me who are maybe mid-level and wanting to reach this whole audience of people who are attending these conferences to learn and to level up who aren't necessarily super senior experienced developers. And it was a really great experience. People seemed to really resonate with that. So I really encourage folks to speak about things that are resonating with them at whatever point in their careers because there are so many people out there who are probably in the same boat and want to hear what you have to say. JOËL: Absolutely. I'm curious, now that you have experienced the full cycle at least once, from ideas to crafting a submission, to getting accepted, preparing a talk, delivering the talk, and then recovering from that, what are maybe some lessons learned or some things you weren't expecting the first time you went into that that now you do know going into another cycle? STEPHANIE: Yeah, the power of community; I had a lot of support from WNB.rb, a woman and non-binary Ruby community. We crafted our CFPs together and then practiced our talks together and had a working group that met every couple of weeks to give feedback on our talks as we were working on them. And it was really awesome to have that accountability, to have that support, people to tell you that your talk is good and give you a thumbs-up. And I really want to continue investing in my community that way. And I really appreciate you asking this question because I guess I do have things I've learned and would want to share that with other people in my community, and yeah, just continue to encourage folks who may not have been traditionally encouraged to speak at conferences. JOËL: Community is so powerful. Even though I've spoken at multiple conferences, I still get nervous about my talks. I have a lot of self-doubt about whether my topic is good, whether I'm sharing it in a way that's going to be impactful. And I had a magical experience at RailsConf this year where a group of us were at a hotel lobby practicing our talks the night before. And I was just still so unsure about my talk. And the feedback that I got there gave me a huge boost of confidence that I was able to ride into the next day and give a talk that I think turned out rather well. But honestly, that was my favorite moment of the conference was 11:30 p.m., a group of people in the hotel lobby taking turns practicing their talks. STEPHANIE: Yeah, I love that. MID-ROLL AD: Debugging errors can be a developer’s worst nightmare...but it doesn’t have to be. Airbrake is an award-winning error monitoring, performance, and deployment tracking tool created by developers for developers that can actually help cut your debugging time in half. So why do developers love Airbrake? It has all of the information that web developers need to monitor their application - including error management, performance insights, and deploy tracking! Airbrake’s debugging tool catches all of your project errors, intelligently groups them, and points you to the issue in the code so you can quickly fix the bug before customers are impacted. In addition to stellar error monitoring, Airbrake’s lightweight APM helps developers to track the performance and availability of their application through metrics like HTTP requests, response times, error occurrences, and user satisfaction. Finally, Airbrake Deploy Tracking helps developers track trends, fix bad deploys, and improve code quality. Since 2008, Airbrake has been a staple in the Ruby community and has grown to cover all major programming languages. Airbrake seamlessly integrates with your favorite apps to include modern features like single sign-on and SDK-based installation. From testing to production, Airbrake notifiers have your back. Your time is valuable, so why waste it combing through logs, waiting for user reports, or retrofitting other tools to monitor your application? You literally have nothing to lose. Head on over to airbrake.io/try/bikeshed to create your FREE developer account today! JOËL: One thing we've been doing recently at thoughtbot is every other week book club discussion, and we've been looking through the book Ruby Science published by thoughtbot. Every week, we'll have someone who is a facilitator, who has done the reading and has prepared some questions for the group. And you recently facilitated a session on the topic of CASE expressions and why they might be a code smell. That sounds like a really controversial statement. How did you approach that topic? STEPHANIE: It's funny because I was looking at the upcoming chapters to pick a topic to facilitate for this book club, and CASE statements stood out to me because I was like, oh, I know what that is; that will be easy. [laughs] But it turned out to be a bit meatier than I thought it was going to be. I'd say that I didn't really consider them a code smell until I read the chapter of Ruby Science talking about them. So I was a bit surprised because they seem so common, which is probably also why I thought it would be an easy topic. JOËL: Would you say that reading the book or that particular chapter changed your mind? STEPHANIE: I think it did only because I hadn't necessarily given them a second thought or thought of them more deeply in that way. I think that, at least in my experience, you encounter CASE expressions pretty early in your career, and you think they're a cool tool for making your conditionals look a bit nicer. And it takes probably a bit more experience, a little bit more pain using them or trying to extend them that you start to have a bit of a more higher level awareness of what might be problematic about a CASE expression. But the book club that I facilitated, we had a really engaging discussion where most folks agreed that it was a code smell but also said that it depends. JOËL: Classic consultants. STEPHANIE: Truly. One thing that someone said that was a really nice takeaway for me was that CASE expressions get a bad rap in object-oriented languages because there are typically other tools or options you can reach for that might be preferred. And someone else said that it's probably a sign that you might be doing too much in the method. JOËL: Hmm. You mentioned that there are some tools and things that might be preferred over CASE expressions. What are the common alternatives that people say you should use instead of a CASE expression? STEPHANIE: I think one simple solution that we discussed in the book club for more straightforward cases would be a hash lookup to use instead of checking for equality via CASE statement. Another solution we talked about was polymorphism, which I think might refer back to the idea of having a bit of a higher level understanding of abstractions in the codebase and what things might look like in the future, especially when you might not have too many conditionals yet in your CASE expression. Another thing that really stuck out to me in our book club discussion was another thoughtboter mentioned Sandi Metz’s 99 Bottles of OOP. And in that initial solution, she presents a CASE statement as the perfectly fine solution for now. And I thought that was really interesting because, in some cases, that might be all we know about the problem, and that is perfectly fine. What do you think about that? JOËL: I love the idea of starting simple. Don't try to start with abstraction, especially if you're doing test-driven development. You have a test, make it go green, use the simplest thing, use duplication, use all the dirty tricks. And then from there, now that you know I have a test that was red and this code makes it go green, now the question isn't how do I solve the problem? It's how do I improve the solution? So we've kind of separated those two steps out, and I really like that. STEPHANIE: Yeah, I remember you mentioned that you had refactored something into using a CASE statement. And I'm curious if you want to share more about that. JOËL: Oh boy, this was a "fun" problem. Fun is in air quotes, by the way. It was a multi-step form, AKA a wizard in a Rails app, where every step submitted to the same controller, and the controller was a huge mess. It had to handle submissions from four or five different forms, some of which shared fields. And it was this huge, deeply nested conditional thing that checked if this field is present in params, that probably means we're on step three, except if this other field is also present, then it probably means we're on step four. But if this Boolean flag in the database is set to false, we might be on a variation of step three. And because there was branching, potentially, it was an absolute mess. By looking at the code, you could never know what step of the processing you were on. What I did is instead of all of these nested if else conditions, I wrote a flat CASE expression that just said, if step one, do step one logic or process step one form. If step two, process step two form and so on. So it was nice and flat. I was able to reuse some parts of the work across by making private methods or other objects, things like that. But you could easily tell in any part of the code what step you were processing. Which means if you get a new feature from the client that says, "Can you modify the behavior on step four?" Now you actually know where to go to. You go to this controller; you find the big CASE expression. You find the branch that says, "If step four," and then you drill down from there. STEPHANIE: So after refactoring that into a flat structure, did you find the code more readable? Did other folks on the project think so too? JOËL: Yes, it was unanimously loved because this is the part of the code that everybody feared to touch. It was the most awful, gnarliest code. And a few of us had touched it, and so if you did the git blame, our names would show up, which meant that anytime anybody got stuck in that code, they would reach out to us and say, "Hey, you're the last one who touched it. Can you fix this for me?" And it was a big game of not it. Cleaning it up made this code accessible to everybody on the team. STEPHANIE: And why do you think a CASE statement was the right solution in this particular case? JOËL: I think if it's a multi-step form that maybe had seven steps in it, you clearly have seven branches that you're working with. And so hiding that behind nested conditions where you try to reuse each other's branches just muddied the waters. We have a seven-way branching path; let's be honest about it upfront and do a seven-way CASE expression. STEPHANIE: One thing that we didn't really talk about as much in our book club discussion was that CASE statements can be quite readable, especially for newer developers. And even though we all did think it was a bit of a code smell, I recently encountered on my client project in a code review someone saying that they preferred a CASE statement in that situation because it was easier for them to grok. I think that's a benefit worth considering before trying to do something fancier in some cases. And I'm curious what you think about that. JOËL: I strongly agree that a CASE expression is a great place to start, especially when you have actually more than two branches. Your logic could go one of n ways. I generally like to branch earlier than later in a lot of code. It's better in my mind to have a seven-way branch at the top of your decision tree and then just straight lines down than this constantly looping back and branching again and looping back, trying to force everything down a single path when it really doesn't want to be. STEPHANIE: So, in this case, did you know that you had those seven branching paths upfront, or did you have to tease that out? JOËL: I did not know from the code. Honestly, it would be very difficult to infer that from the code. But from the product, I knew this is a multi-step form with seven steps. And so I knew what the branches were from the product description. But no, it was almost impossible to infer that from the code. Long-time listeners of The Bike Shed may remember an older episode where Steph and Chris discussed multi-step forms and how best to approach them in Rails and also in JavaScript. And one thing that did come up is that an ideal way to work with a multi-step form in Rails is to have every step be its own controller. So you have a view, it submits to a controller, which renders another view or redirects to another view which submits to another controller. And that is the direction that we went with this multi-step form eventually. Once the single controller had a big CASE expression in it, we slowly started moving each branch out to its own controller. And now we had the step one controller, the step two controller, the step three controller, and so on. And I think that was probably the best solution in the end. But we had to go through the CASE expression just to know what was safe to move out. Interestingly, this refactor is effectively replacing a conditional with polymorphism because all of our controllers are controller objects. They respond to the same interface. And so this gets classic refactor that Ruby Science suggests, which is what we did and kind of what Steph and Chris recommended if you had the luxury of starting from scratch all those episodes ago. STEPHANIE: Nice, I'm glad it turned out that way and was a lot more manageable. JOËL: It's really interesting when you're working with a situation like that where you've got really messy code, and you can make some improvements. And it's like, how far do you go? Especially because there's usually a backlog of new features that the customer wants you to implement. So I'm curious for you, Stephanie, how do you know when you've gone far enough in improving code, either in a refactor step for your own code for a feature you're writing or maybe you're trying to take a break and say I'm going to take a little bit of time today to improve this area of the code. How do you know how good is good enough? STEPHANIE: That's an interesting question. I think I encounter that in a couple of ways, either in my own work when I am tasked with a feature, and I start getting into the code, and it stresses me out and leaves me a bit confused and not sure where to go to work on my feature. That is usually a signal that I might need to pay some attention first and make the change easy and then making the easy change. The other common thing that I have experienced on teams is we collectively feel the pain of an area of the codebase. And maybe we talk about it at a developer meeting, and all agree that, yeah, we really want to give this part of the code some love and add it to the backlog. But it's tough in that case because, like you said, there are a lot of new features that stakeholders want. And we as developers want to be over here taking care of our little codebase [laughs], making sure that it is healthy and it feels good to work with. JOËL: I feel like I don't just want my codebase healthy; I want it pristine. STEPHANIE: Ooh, pristine. What does that mean to you? JOËL: I want it perfect, nice, and shiny. And, of course, it's never that, which is why it's always tempting to toss out the old code and start over and do it right this time. That's not a good thing. You have to be able to live with the messiness of everyday life and the fact that, okay, here's an idea. I think if your codebase is perfect, you've put too much work into it. You've gone too far, and you're beyond that; when is good enough good enough? STEPHANIE: Whoa, that's a big statement. JOËL: [laughs] Feel free to disagree with me here. STEPHANIE: I guess I'm curious what perfect is in this case. JOËL: I think it's subjective for the developers who are writing it. But oftentimes, the ones who are looking for perfection go way too far in their quest for that. STEPHANIE: Way too far at the expense of things like business value or other things? JOËL: I think in two ways; one, you probably ended up overengineering things to try to make it so perfect. Your design needs to have some amount of flex in it for the unknown. It's okay to have some rough corners because it's going to change. And you're going to have to redo that corner next week anyway. So you need to not go all the way in making everything absolutely perfect. The other thing is that if you are putting in the effort to make everything perfect, at some point, you hit diminishing returns. And that's not worth your time from a business perspective or even on a personal project where you're just trying to ship things. At some point, you need to make actual progress. STEPHANIE: I'm curious if you mainly hold yourself to those very high standards or if you also think about that when reviewing other people's code. JOËL: So I mentioned earlier that I want my code to be pristine. I want to be clear, that's a bad thing. I do not actually hold myself to that standard. And I try not to hold other people to that standard, either. It's sort of tempering idealism with pragmatism. So being able to say, look, can we cut scope and focus on just one thing? Or does this fulfill the need that we have? And will it hurt us if we leave it like this and come back to it later? Or that question I asked you at the beginning, is this good enough? And maybe we can come back to it eventually. STEPHANIE: I really struggle with that question sometimes because, in some ways, people talk about software as a craft. And if we were building it in a vacuum, we could fine-tune and hone it until it's this beautiful, perfect, pristine thing. But because we write software for real things in the real world, we are constrained by the needs of our users, or the business, or just the purpose of building software is for folks to use it. And in that case, part of the job is evaluating trade-offs and deciding when is good enough. But sometimes, when I'm by myself working or coding for a little while, I do get sucked into wanting to make this the best that it could be just for my own personal fulfillment and joy. And I have to pull myself out of it sometimes and take a step back and be like, is this good enough for now, good enough for other people to be able to understand, work with in the future? And sometimes, it also requires getting other people's input too. JOËL: That's really valuable. STEPHANIE: Yeah, the worst thing that could happen is squirreling away with your code, and then you emerge with something that was totally not what was asked for. [laughs] JOËL: Especially on a work project. On a personal project, it's often good to know why you're doing a thing. And so maybe you want to see how far you can get away with pushing a particular metric, whether it's you want to go extreme on the decoupling or 100% TDD, or maybe you want to try something like test && commit || revert which is a development methodology. And those are all great as learning experiments, and then you go as deep as you want. I'm going to make another hot take here, and again, feel free to tell me I'm wrong. I'm going to say that on your own personal projects, if you pursue perfection, even when it's not for work, pursuing perfection on personal projects dooms them to join the others on the pile of uncompleted projects on your GitHub. STEPHANIE: That is an interesting take. I think it depends on the goal of your personal project because I personally like to have my projects be a bit of a sandbox, and I have no expectations that they will end up being anything that other people would necessarily look at, even though I guess they just end up public on my GitHub and are just sitting there in a weird, unfinished state. But yeah, I like to use them as an opportunity to, like you said, practice those concepts that I am really excited to explore but might not necessarily have the opportunity to on whatever client project I'm currently working on. And sometimes, I end up just scrapping it, but the exercise itself was valuable for me. I'm curious, though, what types of personal projects you have that lead you to have that opinion. JOËL: I think the way I use personal projects is very similar to you in that they're generally for my own personal growth and entertainment. It's about the journey, not the destination. So I generally have no intention of making this a thing that other people will use. It's typically a way to try out a technique, or a concept, or an idea. And so, for those, going really far on a performance or quality metric can be the goal. And that's completely okay with the knowledge that I probably will not complete or ship this project. I've done a few others where I've done the opposite. I've joined Game Jam events where you typically have a hard deadline. This could be a longer one, like maybe a month or as short as a day or a weekend, and you have to build and ship something within that deadline. And then you have to really make some pragmatic choices. STEPHANIE: Yeah, that sounds like a lot of pressure. I don't know if I would necessarily thrive in that kind of environment. I really like to spend a lot of time thinking about my code and looking over it again, sometimes to the point where I might be a little bit too precious about it. I was reflecting on this recently, and I thought about back when I was earlier in my career and didn't have any idea of what clean or good code was or looked like. And I would just write the code that would make my future work and just put it up for review. And I was very blissfully naive, I think, at that point in my career where I wasn't self-conscious about it in any way. And I think I'm trying to find a good middle ground between being comfortable with whatever comes out when I do some work or write some code while also having more knowledge and experience being able to revisit it and give it a deeper look after some space and feeling good about it without spending too much precious time on it. JOËL: Yeah, it's that classic consulting; it depends. Learn to balance code quality idealism versus the pragmatic reality of your goal, which is I want to ship something, both on your personal project and at work. That perfect code is useless if you can't ship it for contexts where you actually care about shipping. And on that note, let's wrap up. The show notes for this episode can be found at bikeshed.fm. This show is produced and edited by Mandy Moore. 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. It really helps other folks find the show. If you have any feedback, you can reach us at @_bikeshed or reach me at @joelquen on Twitter or at [email protected] via email. Thank you so much for listening to The Bike Shed, and we'll see you next week. 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.Sponsored By:Airbrake: Deploy fearlessly and fix bugs faster with Airbrake Error & Performance Monitoring. Airbrake notifiers are available for all major programming languages and frameworks, and install in minutes, with an open-source SDK-based install and near-zero technical debt. Spend less time tracking down bugs and more time developing. Visit Frictionless error monitoring and performance insight for your app stack.Support The Bike Shed