STEPHANIE: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Stephanie Minn. JOËL: And I'm Joël Quenneville. And together, we're here to share a bit of what we've learned along the way. STEPHANIE: So, Joël, what's new in your world? JOËL: I've just returned from attending RailsConf 2023 in Atlanta, Georgia, and it was so much fun. I got a chance to attend some really good talks. I got a chance to connect with other people in the community. I always love the hallway track or any of the events that happen in the evenings after the conferences. It's a lot of fun. STEPHANIE: Nice. I found it funny that you emphasized just returned because you literally walked through the door of your home and then got online to record this podcast with me. [laughs] JOËL: Productivity. STEPHANIE: What were some of your highlights from the conference? JOËL: I really appreciated a talk by Elle Meredith about how to say no. And she gave...I think it was nine sort of different ways that you might want to have a conversation about saying no to a request. They're almost like design patterns but for interpersonal conversations. And so she covered situations where you might want to say no to maybe someone who's higher up in a hierarchy than you, so a manager. But also, what it's like sometimes when it's the other way around if you're a manager and you have to say no to someone who reports to you, and how to handle some of those conversations. So I really appreciated the nuance that she had to share. And I think the strategies were just really practical. STEPHANIE: That's awesome. Yeah, I know we, as developers, love some good patterns and frameworks. That actually reminds me of something else that I'd read recently, a newsletter called Culture Study by this journalist Anne Helen Petersen. She recently sent out a dispatch about how to say no or decline requests through email. And I was just thinking that it's such a challenging thing to do. But having a script or seeing examples of how other people do it is really helpful to just make it easier the next time that you want to say no, but then you're not sure how. JOËL: It's not easy to say no. I think most people want to please, and it's much easier to say yes. And maybe even you want to believe that you can say yes, that you can do everything with limited resources and not have to prioritize. And then, of course, reality hits you, and you're in a worse situation than if you'd said no upfront or had at least an honest conversation about the limitations that we have and the prioritization that needs to happen. STEPHANIE: Yeah, absolutely. So, over in the thoughtbot Slack, I saw a lot of really awesome praise and even photos from the conference, including stuff about a thoughtbot-sponsored event that we hosted over at RailsConf. What was that like? JOËL: That was so much fun. So we hosted an event out in Centennial Park after day one and just had some lawn games, some snacks, some drinks. And people just came to hang out in the park and had a good time. And I got to chat with a lot of people. I think it somehow just felt really relaxed yet really social. Sometimes when you're in the hallway in the convention center, you'll see groups of people talking, but also kind of people awkwardly walking around and struggling to connect. And there are just so many people around. And if you don't know anyone, it can be really hard to kind of break in. And I felt that this setting...I'm not sure exactly why; maybe it was a smaller amount of people. Maybe it's because it's a more relaxed atmosphere. You're outside. Everyone was kind of mingling and talking and seemed to be having a good time. STEPHANIE: That's really cool. I'm so bummed to have missed it. Yeah, I hear you about the hallway track. It's like you're still kind of, you know, either in a convention center or a hotel, so there is just that vibe of formality. And taking it out of that venue and making it super casual, you know, I think that also almost allows people to not talk about tech, or the conference, or anything work-related and just have fun. JOËL: Definitely. Although there's definitely fun that happens in all sorts of ways after the conference as well. One of the evenings, I went out with a few other thoughtboters and some other attendees at the conference; we went to a karaoke bar. STEPHANIE: That sounds like a lot of fun. I think that's become almost a tradition for the thoughtbot crew whenever we do things out in the world, karaoke. And I'm trying to get escape rooms to be a conference tradition among my group of friends. So you and I, we participated in escape room the last conference that we were at together, and that was a lot of fun. So this is a thing that I hope to keep doing [laughs] next time I see you in person. JOËL: We didn't just participate. We broke the record. STEPHANIE: It's true. Yeah, I didn't want to brag on the podcast. But we did break the record. And we got to, like, write our names on a little poster to put in the office. And I hope it's still there, at that escape room company in Providence, Rhode Island. JOËL: I'd be curious to hear from some of our listeners what some of their traditions are when they go to a conference. Do you have something that you like to do with your conference friends when you meet up? Let us know at hosts@bikeshed.fm. And we'll give you a shout-out on one of the upcoming episodes. So, Stephanie, what's new in your world? STEPHANIE: Speaking of friends and community, I have a podcast to plug today. So our friends and fellow thoughtboters and married couple Aji and Mina Slater, they just launched a podcast called The Tightly Coupled Book Club Podcast. And what they're doing is reading The Rails Guides from cover to cover and kind of treating it like a book club and having a discussion about the documentation as they read it together. And I listened to the first two episodes this morning, and I really enjoyed it. I thought it was such a cool idea and a really great format as a person who enjoys talking about books and things I've read with you. I mean, sometimes we've joked that this is kind of like our two-person book club. But it's really cool to listen in on other people who are, you know, are really knowledgeable, or have a lot of experience about a thing, and then also share their experience reading something as they come across it. I thought that was really interesting too. There's a real-time aspect of it that I liked. JOËL: I love the idea of taking the book club concept and then reading the Rails Guides. What a really original idea. STEPHANIE: It's funny because the Rails Guides do kind of read like a book. I went on to the documentation today just to kind of give myself a refresher as I was listening. And you can download the guides on your Kindle. So, in some ways, they've kind of leaned into that format. And since a lot of it is also just, like, prose, it is more like paragraphs rather than quick bits or API reference. JOËL: Right, right. It's something that's meant to be read in larger chunks rather than just found through a search and then referencing one entry in a list of methods or something like that. STEPHANIE: Yeah. I think my other favorite part about doing an idea like this is I think we all kind of have our own different experiences with the Rails official docs and guides. And I really liked that they're just like, yeah, like, you know, the way that an individual developer approaches the documentation can be totally different. And they kind of talk about how they do it or how they don't do it. Kind of all with the intention of wanting to better understand Rails and also wanting to better support people who want to get into Rails and are kind of entering the universe from the documentation for the first time. JOËL: Yeah, and I think the Rails Guides, in particular, are often that first point of contact for a lot of newer Rails developers. I know that, for myself, when I was first getting into Rails, I was on those guides all the time. Between that and cherry-picking examples from the Michael Hartl Rails tutorial, that's kind of how I learned Rails. STEPHANIE: Yeah, I like that a lot. For other folks who want to hear more about just Mina and Aji's experience with the Rails Guides, you should all check out that podcast. 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: So this is a conversation we had the other day, and I'm curious what your take is on this. If someone is submitting a phone number through a form, how would you store that in the database? Would you store it as a string? Because sometimes it comes with some extra formatting. Would you normalize it and try to store it as an integer because it's a number? What's your take? STEPHANIE: Okay, to answer that question, I think I'm going to gripe a little bit first because the thing with phone numbers is that there are so many ways to format them. So, when you were saying, like, oh, you know, how would you input a number? I'm like; I don't know. Even if I were to write down a phone number on any given day, would I add the parentheses? Would I include the dashes? Do I add the country code? It honestly really depends. And it's just totally different just based on what the form is asking of me or what channel I'm doing it in. So I think phone numbers is an interesting example because there are so many ways of representing it. But I think that actually also answers the way that I would do it is saving it as a string. Because while a phone number is made up of numbers, it's not exactly a numerical value. JOËL: You're hitting on something pretty deep here about the fundamental nature of what a phone number is. What do you mean by saying it's not numeric? STEPHANIE: I guess it doesn't mean anything in mathematical terms, I suppose, is what I was trying to get at. Like, it happens to be in the U.S., at least, you know. JOËL: Always gotta qualify that, right? STEPHANIE: Right. It happens to be a 10-digit identifier, I suppose. I almost said number there, but I think I'm trying to avoid it for the sake of my argument. But it's not necessarily something we would try to do arithmetic on. It's not something that... JOËL: I'm kind of laughing at the idea of trying to do math on a phone number, adding two phone numbers together, doing some sort of, I don't know, add 20% to your phone number. Like, what does that even mean? STEPHANIE: Yeah, absolutely. It's something that mostly remains static, right? It's like when it changes, that means something different. It means that the way that you would reach someone via a phone call or just that kind of communication is more of an event that has changed rather than the value transforming doesn't mean anything on its own. JOËL: So you mentioned that this is not the kind of number that you can do arithmetic on. That kind of reminded me of an article from the UK's I want to say government design guidelines and how to design web forms that are going to be used by any service that's part of the UK Government. And they have this category of numbers that they call non-incrementable. And their suggestion is that even though these things are represented as numbers, they should not use the HTML number input for them because you're never going to hit that little plus or minus arrow to go up and down and change the number. And that, instead, it should just be a regular text input. STEPHANIE: Yeah, that's interesting because the idea of incrementing, for me, is definitely more attributed to the idea of a numerical value where it's a number and then what that number represents. So it could be weight. It could be money. It could be some other measurement. Another one could be quantity, right? If you have a shopping cart or something, and you're incrementing that value because you want to buy more of whatever item. That, to me, is where I see that type of input more frequently. And so it would make sense that, you know, a phone number where the person is usually inputting a fixed thing that, you know, because that is not something that they can change, or they are wanting to change in that form, would be represented with a different HTML input. JOËL: I don't know about you, but I feel like when I'm having a conversation with someone, occasionally, they'll just sort of mention something that my ears will kind of prick up because it feels like a keyword. And you mentioned earlier the keyword identifier, that a phone number to you feels more like an identifier than a number. And I want to dig into that because that feels really key to this conversation. STEPHANIE: Yeah. I think there are a lot of things that happen to have numbers in them because that's the characters we use to denote some kind of unique or mostly unique value [laughs] of something. So, like, most people have a phone number, some people don't, some people may have multiple. But, usually, that phone number is tied to a person or a business or some kind of...it's like a record of how to reach someone. JOËL: Yeah. I guess to a certain extent, a phone number is like a...it's not an ID for a person, but it's an ID for a phone, or for a SIM card, or a line, an account, something like that that's publicly shared out. But even though it's publicly shared out, it's effectively...I guess you might call it a third-party ID by the phone companies. If they're supposed to be globally unique, you might say the global telecoms consortium that have come up with this set of rules. STEPHANIE: [laughs] Yes, we're clearly not telecommunications experts over here [laughs] or just have a, you know, regular human-level understanding of how telephones work. [laughs] JOËL: But I think that's a really interesting idea because I think my instinct with phone numbers is I want to normalize them when I get them through a form. And yet I have very strong instincts that anytime I get an ID from a third-party service, let's say I'm interacting with an API, and I get a new user through there. The ID from the third-party service is there. When I save it into my database, I will create a new primary key for that row because I want to own my own primary keys. But I will have a row for that third-party ID, and I will make sure to not modify it. I will store it in its raw form. So why do I feel the need to normalize phone numbers? If they are third-party IDs, then maybe I should just be storing them in raw format as entered by the user. At the very least, maybe I should store them as strings rather than trying to turn them into numbers. STEPHANIE: Yeah, that's interesting. I mean, I do think phone numbers are a bit of an exception here in that you could think of it as a third-party ID. But it's also so ubiquitous in how our society functions. And the way people use it, it's like it's not a single service that this is owned by where you want to make sure that you're capturing it the way that that third party would. It's something that is so commonplace that it can end up having different forms because of the way that users interact with it. JOËL: I think one thing that's really interesting with third-party IDs is that even though they might come back as numbers like you said earlier, we don't do math on them. The main thing you ever do with a third-party ID is use it to make requests back to that third-party service. So, if I have a user I've pulled from some other service and I ever want to talk to that service again, I would need to use that third-party ID that I have stored in order to make sure that everything stays in sync. That's kind of what we do with a phone number, right? We store it. And then the reason we might want to store a phone number is because we might want to programmatically make a phone call or send a text message that's interacting back with that telecom's world. We might want a human to do that. But the effect is still kind of the same. We're not doing any transformations or work on it internally within the software. It's always when we want to make some kind of phone call, either manually by a person or automatically with a computer. And the only way we can do that and reach the right person is by using the ID that was given to us. STEPHANIE: Yeah, that's a really good point. I mean, I have seen many applications that do hand-roll their own validation or normalization on phone numbers. But I do think that there is a library for this published by Google I think. It's called Libphonenumber. And so it's an open-source library for parsing, validating, and formatting phone numbers for, I think, most countries. It's very comprehensive. JOËL: I think the difference maybe with a phone number and a form where you might want to do a little bit of polishing on it is that it is manually entered by a user. It definitely needs validation because a user manually typing in an ID into a form absolutely could have an error in it. Normalizing it for storage purposes, maybe, but at the very least, yeah, it needs to be validated because someone hand-typing in an ID is not something you want to rely on too heavily. STEPHANIE: Yeah. I mean, I'm curious if we can extrapolate this conversation further beyond phone numbers. It sounds like we're talking about this idea that values with numbers in them should not always be treated as integers. JOËL: Yes. And I have a great example of that that has actually burned me before. STEPHANIE: Ooh, what is it? JOËL: Zip codes. So, again, prefixing this, in the U.S., zip codes are typically a five-digit number. There's a variant that has more digits in it. And you'd think that you can store that...it's a five-digit number; you can store that in an integer column. And that does not work because you can start with a zero. And so if you store that as an integer, then what you really have is a four-digit integer. And then, when you try to put that back into an address, things get messed up. So it's really important to store U.S. zip codes as strings so that you can keep that leading zero if you need it. And, of course, the moment you introduce international zip codes, or I think postal codes is what most countries call it, now, all of a sudden, you've got letters in addition to numbers. I wanted to share one of the most delightful postcode bits of knowledge that I have. STEPHANIE: Please. JOËL: Which is that in Canada, postal codes alternate letters and numbers. And Canada decided that Santa Claus needed his own Canadian zip code. And his zip code is H0H 0H0. STEPHANIE: [laughs] JOËL: H0H 0H0 STEPHANIE: Of course it is. I like that a lot. Makes sense that he would reside in Canada, up in the frigid, chilly north. So you've mentioned that you were burned by this. Does that mean you were working with an application that did store postal codes as integers? And what was the impact or consequence of that? JOËL: So I was building a feature that required interacting with a zip code. And, as one does, I tested it out in development. And, as one also often does, I put in my own address. Now, I happen to live in Boston, and my zip code starts with a zero. So I happen to live in the one place that has that weird edge case. And immediately when I saw in dev how things happen, I was like, wait a minute, that's broken. That's not going to work. STEPHANIE: Yeah. Wow. I wonder if that has just been impacting users for a long time before that discovery. JOËL: It probably depends on the application, right? I guess you could...if you introduce that as a problem, you could try to add hacks on hacks to make it better. So you store it as an integer, but then when you get the integer out of the database, and you need to use it as an address, you then reformat it back. So you left-pad the number with zeros. STEPHANIE: Yeah, I can see some really interesting ways of trying to work around that. JOËL: But yeah, I think the best practice is definitely store your zip/postal codes as a string, not as an integer. STEPHANIE: I wonder what it is about these types of values that make us think that they are numbers or want to store them as integers. I think that the Rails default is to store primary keys in integers. And if you wanted to use UUIDs, for example, instead, you do have to have done that initial setup. I'm curious about the origin of using ints. And I know that that's like a whole story [laughs] in terms of the bite-size of that value. But yeah, it's just one of those origin stories that I'm wondering if that has kind of impacted our understanding of what primary keys look like. JOËL: At its core, I think this goes back to what we were talking about earlier. Primary keys the default in Rails is an auto-incrementing integer. And we store it as an integer so that the database can do a plus-one on it every time we insert another record. We don't want to do that with zip codes or with phone numbers. Now, quite possibly, within the U.S. Postal Service or whatever the standard is for establishing phone numbers, they might, because these are numbers, they might be doing some kind of incrementing somewhere. But there are patterns, definitely, that have been established. But they're not always necessarily incrementing. And they're not transparent to us in a way that we would care about them normally in an app. And we might care that, oh, we know that if it has a leading zero in the zip code, you're in this broad region or something like that. But, again, this is not really doing math so much as it's knowing the pattern in the ID. I know some ID-like numbers have checksum logic built into them; credit card numbers are something like this. So I don't know if you've ever tried to type in your credit card number in a form. Like, the outline goes red. It'll tell you there's an error and you've not submitted it. Nobody's making a background request to your bank. And the bank is like, wait a minute; this is a bad card number. The validation logic on the front end just immediately was able to tell the number is wrong. And that's because there are some checks and logic built in such that your number can be almost like self-verifying. We don't know that it's Stephanie's number, but we do know that it is a valid Mastercard. STEPHANIE: Yeah, that's really interesting because, you know, I know that there are just various combinations of digits in a credit card number that are straight up and valid. And I think that's another example of something that is more like an identifier as well. JOËL: You're right. I think so. It identifies a particular card, a piece of plastic that you have, and maybe line of credit or something like that. I'm not sure what the underlying modeling is exactly. But as a user of that credit card, [laughs] it represents a piece of plastic in my pocket. And that is a number that other services can use to talk to the card issuer. So it's an ID that you can use to make requests to Visa, or MasterCard, or whoever. So I think you're right; that does fall under the umbrella of a third-party ID. I think I probably would tend to try to store that as a string based on this conversation rather than as an integer, even though it is all numbers. I say that, though, and, of course, now I'm going to get people tweeting at me saying, "Did you know that American Express sometimes put a letter in their credit card numbers?" STEPHANIE: Oh, man. That would really throw a wrench in my understanding of how credit cards work. JOËL: [laughs] But, again, if you store it as a string, it doesn't matter. STEPHANIE: I'm really interested in the idea that a lot of these things we're talking about, you know, are often collected in forms and saved in our applications. Because we need to save information about our users in the context of whatever domain our application is working in. And I can kind of see it going a couple of ways where it's either, like, don't give that too much thought. Or we try to introduce a library that does a lot to make sure that it's kind of covered all of the different cases. Or, like, it's really covered kind of how we've been talking about credit card numbers and phone numbers, like, a really wide breadth of logic that exists because of the way that they are very prevalent in the human world, at least. And I'm curious, at what point do you think, you know, like, writing that first migration, how much energy would you put into making sure that those values are normalized correctly or that you're doing the right thing with those pieces of data? JOËL: I think, based on this conversation, I would probably lean into doing minimal normalization. And I think the thing that's special about this type of number that we're talking about is that we don't need to do any logic on it internally. We typically only use it when calling out to some third-party system. And assuming that third-party system will accept that identifier in its raw form, non-normalized, then why do I need to care or put that effort in? STEPHANIE: Yeah, that's a really interesting point. I think it definitely depends on what you're doing with it, right? Like, if you're a payment platform, obviously, you want to make sure that you get those credit card [laughs] values right and the way that you operate on them is as robust as possible. But for most applications, you might just be displaying the phone number, and that's about it. And minimal normalization and just formatting based on the way that your application handles it seems reasonable enough. JOËL: I think the main thing you need to be able to do with that number is to make a call to that third-party service system and have it work. So a phone number you need to be able to call it and connect to the right person on the other end. With a credit card number, you need to be able to charge it, and we need to be able to charge it successfully. That's really the main thing that you're concerned with it. If you can do that without normalizing, then you're fine. Now, you might need validating, which I think is a separate thing from normalizing. And that's really interesting because you can get all this fancy validation logic to check that there's the correct number of digits in the phone number or in the credit card and all that. And that's great. But you can also sometimes just try it. So I think we see this really commonly with things like email validation where we don't really trust that we can validate emails. Instead, we send you an email with a confirmation link. And the validation is that you were able to receive that and click that confirmation link. STEPHANIE: Oh yeah, that's an interesting way. I've never thought about that. But a way that we've solved that problem without having to accommodate every single way a person might try to input their email. JOËL: Similarly, you could do for a phone number, send a text with a confirmation number. And if you can receive that, then your phone number is good. And if you can't, then try to input your phone number again, assuming I'm dealing with a provider that can send to most numbers. I don't have to deal with all of the region-specific variations on how phone numbers work. Payments are really interesting because, typically, that is the main mode that you're trying to use them. It's not even like a validation thing. It's that we're going to make a call to your bank right now. And if this card gets declined, you're going to have to put your number in again. STEPHANIE: Yeah, I've certainly seen some differing trends over time around how those inputs are validated, though, right? Like, there are some websites that give you that real-time feedback. And, as a user, I think for me, it depends on whether or not I like it, right? It's, like, I've certainly encountered forms where I am like, oh, I'm appreciative that they're doing some input masking so that I don't accidentally type in a value that they are not willing to accept, and other times where the validation ends up getting in the way. When do you think that real-time feedback is important? JOËL: So I think it's all about shortening the cycle of making a mistake and fixing it. So, you know, it's annoying if you typo your credit card number, and then you submit it. And it takes a few seconds to go back to the provider. And then, oh, it comes back up and says, "Sorry, that was wrong." And then you've got to figure out what went wrong. If they had some logic that did that checksum math or something like that and said, "Wait a minute, this number is wrong," then you could fix it immediately without having to do a full-page submit and potentially lose or have to reload data that you filled in and if it's a larger form. So to get functional, you don't need that. But it's a nice layer on top of things to be able to have a shorter feedback loop where you immediately get feedback that tells you, wait a minute, that card number is not quite right. Maybe consider don't hitting that submit button. STEPHANIE: Yeah, I think it also definitely depends on the goals of your system. I'm remembering now that article you mentioned from the UK government's design system. I was perusing that. And I found that they have some very explicit guidelines around form inputs because part of their goal is to make this portal as accessible as possible for all of their citizens. And one thing that I remember was really interesting about how they considered their users was how if you enter a phone number, you can use the phone keypad-style of input. And the justification was that a lot of people are using this on mobile, so we want to make sure that we make this as accessible as possible for them since not everyone has access to a computer. And I thought it was really cool that they justified their reasoning. And I think they even have all of this stuff open to feedback. So I had found a GitHub issue, I think, called, like, telephone numbers. And they're like, hey, like, we want to hear your thoughts about how telephone numbers should be received as inputs and whether this is working for you. And I thought that was a really committed way to make sure that the way that the system is implemented really reflects the user's needs. JOËL: Yeah. And what's really cool about HTML is that we now have the ability to kind of decouple some of these things. And so, you might be typing in a text input, but you want to limit certain characters. You only want to be able to type in number characters. Does that mean that you have to use a number input? Well, not necessarily. With HTML5, you can put a regular expression pattern to limit what can be typed in this input. You can also have an input mode, which, for mobile, will control which keyboard shows up. Or it won't control; it tells the mobile operating system or the browser what you would like them to show, and it's up to them to implement that. And so, like you were saying, for a phone number, even if you're typing it into a, let's say, text input because you want to be able to...maybe the user wants to put in whitespace, or dashes, or whatever. But you still want that number pad to show up by putting an input mode numeric on it. You can get that keyboard, even though you're not in a numeric input. STEPHANIE: Yeah, I think that's a blessing and a curse that we do have these separate layers of abstraction, right? Because, on one hand, it gives us more flexibility. And then I've also seen it just run amok [laughs] and cause a lot of confusion about what being the source of truth, but I think that's a conversation for another day. JOËL: Yep. Form design accessibility, keyboard inputs. So yeah, I think coming back to the core question of today, when is a number maybe not a number? STEPHANIE: That's a big question that I think only the developer with the task at hand can answer. JOËL: On that note, shall we wrap up? STEPHANIE: Let's wrap up. Show notes for this episode can be found at bikeshed.fm. JOËL: This show has been produced and edited by Mandy Moore. STEPHANIE: 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. JOËL: If you have any feedback for this or any of our other episodes, you can reach us @_bikeshed, or you can reach me @joelquen on Twitter. STEPHANIE: Or reach both of us at hosts@bikeshed.fm via email. JOËL: Thanks so much for listening to The Bike Shed, and we'll see you next week. ALL: Byeeeeeeee!!!!!! ANNOUNCER: This podcast is brought to you by thoughtbot, your expert strategy, design, development, and product management partner. We bring digital products from idea to success and teach you how because we care. Learn more at thoughtbot.com.