POPULARITY
We released episode one of this podcast on June 11, 2012. Now, more than a decade later, we're celebrating the 500th episode of our show. In honor of this milestone, Victoria, Will, and Chad caught up with each of the past hosts of the show: Ben Orenstein, Chris Toomey, and Lindsey Christensen. We chatted about what they're up to now, what they liked and learned from hosting the show, their time at thoughtbot, and more! Follow thoughtbot on X (https://twitter.com/thoughtbot) or LinkedIn (https://www.linkedin.com/company/150727/). Become a Sponsor (https://thoughtbot.com/sponsorship) of Giant Robots! Transcript: VICTORIA: This is the Giant Robots Smashing Into Other Giant Robots Podcast, where we explore the design, development, and business of great products. I'm your host, Victoria Guido. WILL: And I'm your other host, Will Larry. CHAD: And I'm your other host, Chad Pytel. We released episode one of this podcast on June 11, 2012. Now more than a decade later, were celebrating this: the 500th episode of our show. In honor of this milestone, Victoria, Will, and I caught up with each of the past hosts of the show: Ben Orenstein, Chris Toomey, and Lindsey Christensen. We chatted about what they're up to now, what they liked and learned from hosting the show and their time at thoughtbot, and more. First up: Ben Orenstein. Ben was the very first host of the show back in 2012 when he was a developer at thoughtbot. He is now the co-founder and Head of Product at Tuple, a remote pair programming tool for designers and developers. Ben, it's great to talk to you again. It's been a while since you and I talked. How have you been? BEN: I've been decent, yeah. It's fun to be back to my roots a little bit. I told some folks that I work with that I was coming back to the pod for the 500th Episode, and they were stoked. So, it's kind of a treat to get to be on these airwaves again. CHAD: What have you been up to since you left this show and thoughtbot? BEN: Well, I started a company. So, I was at thoughtbot for a while; I think it was seven years. And I eventually sort of struck out to start my own thing–had a false start or two here and there. And then, I ended up starting a company called Tuple, and we still exist today, fortunately. Tuple is a tool for doing remote pair programming. We started off on macOS and then wrote a Linux client. And we're launching a Windows client now. But it's sort of, like, screen sharing with remote control for developers who are actually writing code and want to have great, low latency remote control and who care about screen share quality and that sort of thing. I started that about five years ago with two co-founders. Today, we are a team of 11, I think it is. And it's been going well. Our timing was really great, it turned out. We launched a little bit before COVID. So, remote work turned into a lot more of a thing, and we were already in the market. So, that helped us a ton. It was quite a wild ride there for a bit. But things have calmed down a little lately, but it's still fun. I'm, like, really enjoying being a co-founder of a software company. It was what I've always sort of wanted to do. And it turns out it actually is pretty fun and pretty great. Although there are, of course, the ups and downs of business ownership. It is never quite as calm or relaxing as being an employee somewhere else. CHAD: You started Tuple instigated by...full disclosure: thoughtbot's an early customer of Tuple. We're still a customer. We use it a lot. BEN: Woo-hoo. I appreciate that. Thank you. CHAD: If I remember right, you started and were sort of instigated to create Tuple because there was a prior product that then Slack bought, and then it started to degrade. And now, it no longer exists in the same way that it did before. BEN: Yeah. So, there was this tool called Screenhero, which I actually started using -- CHAD: [inaudible 02:14] BEN: Yeah, first at thoughtbot. Some other thoughtboter introduced me to it, and we would use it for pair programming. And I was like, oh, this is nice. And then yeah, Slack kind of acqui-hired it and more or less ended up shutting the product down. And so, there was this gap in the market. And I would ask my friends, I would ask thoughtboters and other developers, like, "What are you using now that Screenhero is gone?" And no one had a good answer. And so, after a while of this thing sort of staring me in the face, I was like, we have to try to solve this need. There's clearly a hole in the market. Yeah, so we were heavily inspired by them in the early days. Hopefully, we've charted our own path now. But they were definitely...the initial seed was, you know, let's do Screenhero but try to not get bought early or something. CHAD: [laughs] How did you or did you feel like you captured a lot of the Screenhero customers and reached them in those early days? BEN: I think so. The pitch for it was sort of shockingly easy because Screenhero had kind of blazed this trail. Like, I would often just be like, "Oh, we're making a thing. Do you remember Screenhero?" And they'd go, "Oh yeah, I loved Screenhero". I'd be like, "Yeah, we're going to try to do that." And they'd be like, "Nice. Sign me up." So, it for sure helped a ton. I have no idea what percentage of customers we converted. And they were a pretty large success, so probably a small fraction, but it definitely, like, made the initial days much easier. CHAD: Yeah. And then, like you said, COVID happened. BEN: COVID happened, yeah. I think we had been around for about a year when COVID hit. So, we were getting our feet underneath us. And we were already, like, the company was already growing at a pretty good rate, and we were feeling pretty good about it. I don't think we had quite hit ramen profitable, but we were probably pretty close or, like, flirting with it. Yeah, the business, like, I don't know, tripled or quadrupled in a matter of months. We had a few big customers that, like, just told everyone to start using Tuple. So, we had, like, thousands and thousands of new users kind of immediately. So, it was a crazy time. Everything melted, of course. We hadn't quite engineered for that much scale. We had a really rough day or so as we scrambled, but fortunately, we got things under control. And then had this, like, very nice tailwind. Because we started the company assuming that remote work would grow. We assumed that there would be more remote developers every year. And, you know, it's probably maybe 5% of dev jobs are remote or maybe even less, but we expect to see this number creeping up. We don't think that trend will reverse. And so, COVID just, like, it just yanked it, you know, a decade in the future. CHAD: You haven't tripled or quadrupled your team size, have you? BEN: No. Well, I mean, I guess, I mean, we started as 3, and now we're 11, so kind of. CHAD: [laughs] Yeah, that's true. BEN: Expenses have not grown as fast as revenue, fortunately. CHAD: That's good. That's basically what I was asking [laughs]. BEN: Yeah, yeah. We're still a pretty small team, actually. We have only, like, four or five full-time engineers on the team at the moment, which is kind of wild because we are now, you know, we have three platforms to support: Linux, Windows, and Mac. It's a pretty complicated app doing, like, real-time streaming of audio, webcams, desktops, caring about OS-level intricacies. So, I think we will be hiring more people soon, although we haven't said that for a long time. We sort of have always had a bit of a hire-slow mentality to try to get the right team members and, like, feel a real pain before we hire someone into it. But we have been getting a bit more aggressive with hiring lately. VICTORIA: Well, I really appreciate Tuple. I installed it when I first started working here at thoughtbot. And we have random pairings with everyone across the company. So, I'll randomly get to meet someone halfway across the world who's working on similar projects. And I think they really enjoy that I have a tool they like working to share what they're working on. So, I want to thank you for that. And I'm curious about when you really started to scale during COVID, what were some of the technology architecture trade-offs you came across, and where did you land with it? BEN: Well, we got fairly...I don't know if it was lucky, but we...for a long time, for years, even through COVID, maybe the first four years of the company, all Tuple calls were purely peer-to-peer. And there was no server that we owned intermediating things. This was, like, kind of one of the keys of, like, not having expenses. The scale of revenue was we could have lots more calls happen. And it wouldn't cost us bandwidth or server capacity. To this day, still, for any calls with three or fewer participants, they're purely peer-to-peer. And this is nice for latency purposes because it just...we can find the most direct path to the internet between two people. It's also nice from our cost perspective because we don't need to pay to send that data. And that was hugely useful as call volume went up immensely. Didn't have to worry too much about server load and didn't have to worry too much about bandwidth costs. CHAD: Today, is there a central service that makes the initial connection for people? BEN: Yes, yeah, yeah. So, there is a signaling server. So, when you launch the app, you sign in, and you see, like, oh, which of my co-workers are online? So, there is actually a Rails app that handles that, actually, increasingly less the Rails app. We have now...I think it's a Go service that actually manages all those. I'm further and further from the code every year. Some of the technical questions might be a little bit beyond me, or I might have slightly out-of-date info. But back to the architecture question for a second, we did a pretty big refactor when we decided to go from just being a Mac client to supporting other platforms, where we split out a cross-platform real-time communication engine written in C++ so that we could use that for all of the heavy lifting, all the managing of the connections, and the tricky bandwidth estimation, and all this stuff, and use that across different platforms. And so, today, you have the cross-platform engine, and then on top of that is a, like, a less specific layer for each of the operating systems that we support. CHAD: So, you mentioned you're less and less in the code these days. So, what do you spend your time doing then? BEN: It's a mix of things. These days, it's basically mostly -- CHAD: Just cocktails on the beach, right? BEN: Cocktails, yes [laughs], cocktails on the beach, appearing on podcasts trying to sound important and impressive, yeah. Mostly product work. So, right before this, I just got off a call with some folks from The Browser Company. They are some of our first alpha users for our new Windows clients. So, I hopped on the call with them and, like, watched three of them install the product and inevitably run into some bugs. And, you know, chatted through those with the engineer that was working on it, prioritized some stuff, made some decisions about what's coming up next, and what we're going to ignore. So, mostly product work these days. For the first five years of the company, I was CEO, so I was doing kind of everything: marketing, and also hiring, and also product. About two months ago, I stepped down as CEO, and one of my other co-founders, Spencer, stepped up. And so, now my focus has narrowed to be mostly just product stuff and much less on the marketing or hiring side. VICTORIA: Yeah, you mentioned that it was a little more comfortable to be an employee than to be a founder. I don't know if you could say more about that because, certainly, a lot of engineers are smart enough and capable enough to run their own company. But what really informed your choice there, and do you regret it? [laughs] BEN: I definitely don't regret it. thoughtbot was a close second in terms of wonderful professional experiences. But running my own thing has been the most interesting professional thing I've done by a big margin. It has also been more stressful. And, Chad, I don't know if you remember, I think, like, maybe eight years ago, you tweeted something like, if you want to sleep well at night, and, like, value that, like, peace of mind, like, don't start a company or something. I have experienced that. CHAD: [laughs] BEN: A lot more, yeah, like waking up in the middle of the night worrying about things. It feels a little bit like the highs are higher; the lows are lower. Being an employee somewhere, it's like, if this company fails, I know I can go get another job, right? Like, you're a developer. You're extremely employable. But as the owner of the company, if the company fails, like, a huge chunk of your net worth is gone. Like, this thing you poured your life into is gone. It's way more stressful and traumatic to have that happen, or have that threatened to be happening, or just imagine that happening. So, overall, I have found the trade-off to be totally worth it. It's awesome to make your own decisions and chart your own path. And when it works, it can work in a way that being a salaried employee can't. So, I'm happy with those trade-offs. But I think that is a good question for people to ask themselves as they consider doing something like this is, like: is that the kind of trade-off that you want to make? Because it has significant downsides for sure. WILL: I am a big fan of Tuple also. I love it. It [inaudible 10:08] easy, especially with remote work. You hit the jackpot with COVID and remote work, so kudos for that [laughs]. Was there anything...because I know from our previous companies, about over...hopefully a lot more of the good stuff than the bad stuff. But was there anything that you learned? Because you were at thoughtbot for seven years. Was there anything that you're like, oh my gosh, I learned that, and it's helped me till this day while I'm running my company? BEN: Yeah, quite a bit, actually. I think it'd be hard to tease apart exactly which lessons, but I do...so I ran Upcase for thoughtbot and also FormKeep. So, I got a chance to kind of run a small division of the company, while still being a normal employee and, like, having not much of that risk. And I think that was a really wonderful opportunity for me to, like, practice the skills that I was interested in. Just, like, how do you market a thing? How do you design a product and have it be good? How do you prioritize user feedback? There were a ton of lessons from those days that I feel like made me better at running our company when we actually took a shot at it. So, there were, like, the specific things that I learned by the work I was doing there. But then just, like, I mean, I think I am the programmer I am today because of, like, the weekly dev discussions that happened. Like, spending so much time with Joe Ferris and, like, trying to copy as much of his brain as possible, like, really, like, imprinted on me as, like, a programmer. And also, just, like, a lot of the sort of cultural things from my time at thoughtbot of, like, you should be sharing the things you're learning. Like, writing blog posts is a great use of time. Like, doing open-source work is a great use of time. And maybe you can't directly trace how doing, like, working in public or sharing information benefits the company. It's hard to, like, attribute it from a marketing sense. But if you sort of have faith that in the large, it's going to work out, it probably will. That feels like a thoughtbot lesson to me, and I think it has served us really well; where I recorded a weekly podcast for a long time called The Art of Product. I'm recording a new podcast called Hackers Incorporated with Adam Wathan of Tailwind fame. And I don't ever think, like, hmm, how many new leads do we think we get per episode, and how many hours has that taken? What's the ROI? I just have this sort of reflex that I developed from thoughtbot time of, like, you should be putting stuff out there, or you should be giving back. You should help other people. And that will probably help your business and make it work in the long term. CHAD: That's a good lesson [laughs]. One of the other things, you know, while you were a host of Giant Robots, you were the first host. I remember, you know, encouraging you to be the first host, and I think we talked about that in one of the episodes along the way. But we also transitioned the format a little bit, especially as you started to work on products here; you know, it was more about the building of those products and following along with those. And one of the things that sort of half-jokingly defined, I think, your impact on a lot of products was pricing, experimenting with pricing, learning about pricing, increasing prices more than people were maybe comfortable doing so. How has that worked out with Tuple, pricing in particular? BEN: It's really hard to say. It's hard to know what, like, the other path would have been through the world-. We sort of decided from, like, the early days that we wanted to have, like, a fairly premium price. Like, we wanted to be the product that was really good and was, like, a little bit annoyingly expensive, but you still paid for it because it felt worth it. And I think people could debate in both directions whether we nailed that or not. We have had a price increase that we ended up rolling back. We went, like, a little too far one time and said, "You know what? I think we're a little bit over," and we reverted that. But I would say even today, we are still a fairly pricey product. I mean, I'm pretty happy with how the company has done. I can't prove to you that, like, if the price were half what it is, we would have, you know, better success or not. CHAD: I think it'd be very hard to make the argument that if it was half that, you would have double the number of customers. BEN: Yeah, that's probably not true. CHAD: Not with the customers that you have, who are companies that will pay for products that they use as much as Tuple. BEN: Yeah, I'm happy serving the kind of companies, and they end up being mostly tech companies that really value developer happiness. When their developers come to them and they say, "We don't want to pair over Zoom. We like this thing. It's better. It feels nicer to use," they say, "Okay," and they buy the tool for them. There are places where that's not the case. And they say, "We already have a thing that does screen sharing. You're not allowed to buy this." We don't invest a lot of time trying to sell to those people or convince them that they're wrong. And I'm pretty happy serving sort of the first group. CHAD: So, you've mentioned that you've still been podcasting. To be honest, I didn't realize you were starting something new. Is it live now? BEN: It is live now, yeah. CHAD: Awesome. Where can people find that? BEN: hackersincorporated.com. It's about the transition from developer to founder, which is kind of what we've been touching on here. Yeah, hopefully, the audience is developers who want to start something or have started something who are maybe a little bit further behind progression-wise. And it's kind of, like, I have some lessons, and Adam has some lessons, and, you know, we don't think that we're experts. But sometimes it's useful to just hear, like, two people's story and sort of see, like, what seemingly has worked for them. So, we've been trying to share things there. And I think people will find it useful. VICTORIA: I was going to ask you for a lesson, maybe give us a little sample about how would you advise someone who's built a product and wants to market it, and it's targeted towards developers since you mentioned that previously as well. BEN: Yeah, in a way, the question already contains a problem. It's like, oh, I built the product; now how do I market it? It's a little bit indicative of a very common failure mode for developers, which is that. They sort of assume, okay, after you make the product, you then figure out how you're going to market it. And marketing is sort of a thing you layer on later on when you realize that just, like, throwing it on Twitter or Product Hunt didn't really work. When we started building Tuple, I was out there marketing it already. So, I had two co-founders, so this is a luxury I had. My two co-founders were writing code, and I was out doing stuff. I was recording podcasts. I was tweeting about things. I was making videos. I was giving conference talks. And I was getting people to hear about our product well before it was done. In fact, I was even selling it. I was taking pre-orders for annual subscriptions to the app while it was still vaporware. So, I would say, like, you basically can't start marketing too early. If you start marketing early and no one really cares, well, then you don't really have to build it probably. I would actually even go a little further and say, like, I started marketing Tuple before we had a product available. But in reality, I started marketing Tuple seven or so years before that when I started publishing things through thoughtbot. It's like when I was traveling around giving talks about Ruby, and when I was making screencasts about Vim, and when I was running Upcase, I was, over time, building an audience. And that audience was useful for thoughtbot, and it also was useful for me so that when I left, I had something like 10,000 Twitter followers or something, a few thousand people on our mailing list. But there were a lot of developers that already sort of knew me and trusted me to make fairly good things. And so, when I said, "Hey, I've made a new thing, and it's for you," I really benefited from those years of making useful content and trying to be useful on the internet. And in the early days, we had people sign up, and they would say, "I don't even really think I'm going to use this. But I've learned so much from you over the years that I want to support you, so I'm going to pay for a subscription." VICTORIA: I like your answer because I think the same thing when people ask me, like, because I am an organizer for Women Who Code, and I know all these great people from showing up for years in person months over months. And so, then people will ask, "Oh, how do I recruit more women in my company?" I'm like, "Well, you got to start showing up [laughs] now and do that for a couple of years, and then maybe people will trust you," right? So, I really like that answer. WILL: How has your relationship with Chad continued to grow since you left? Because seven years at the company is a lot. And it seems like you're still on really, really good terms, and you're still friends. And I know that doesn't happen at every company. BEN: I mean, it was tough deciding to leave. I think, like, both of us felt pretty sad about it. That was the longest I'd ever worked anywhere, and I really enjoyed the experience. So, I think it was tough on both sides, honestly. But we haven't kept in that much touch since then. I think we've emailed a handful of times here and there. We're both sociable people, and we sort of get each other. And there's a long history there. So, I think it's just easy for us to kind of drop back into a friendly vibe is sort of how I feel about it. CHAD: Yeah. And the way I explain it to people, you know, when you're leading a company, which Ben and I both are, you put a lot of energy into that and to the people who are on that team. If you're doing things right, there's not really hard feelings when someone leaves. But you need to put in a lot of effort to keep in touch with people outside of the company and a lot of energy. And, to be honest, I don't necessarily do as good a job with that as I would like because it's a little bit higher priority to maintain relationships with them, the people who are still at thoughtbot and who are joining. BEN: What you're saying is I'm dead to you [laughter]. That's CEO, for you're dead to me. CHAD: No. It's just...no hard feelings. BEN: Totally. CHAD: I think one of the things that has been great about the show over the years is that we haven't been afraid to change the format, which I think has been important to keeping it going. So, there is sort of; in fact, the website now is organized into seasons. And I went back and re-categorized all the episodes into seasons. And when the seasons were made up of, like, sort of the format of the show or particular hosts...when we started, it was just an interview show, and it was largely technical topics. And then we started The Bike Shed, and the technical topics sort of moved over there. But it also went with your interests more under the product and business side. Then you started working on products at thoughtbot, so it started to go even more in that. And I think Chris joined you on the show, and that was sort of all about those topics. BEN: Yeah, that makes sense. I think if you don't let the hosts kind of follow their interests, they're going to probably burn out on the thing. It's not fun to force yourself, I think, to record a podcast. CHAD: Yeah. And then when you left, you know, I took over hosting and hosted by myself for a while, went back to the interview format, but then was joined by Lindsey for a little while. We experimented with a few different things: one, interviews, but then we did a whole, just under a year, where we followed along with three companies. And each month, we would have an interview episode where we talked to them, all three companies, about the same topic. And then, we also did an episode with just Lindsey and I talking about that topic and about what we learned from the startup companies that we were following along with for the year. And now we're back to interview freeform, different guests, different topics. It seems like we're going to stick with that for a little while. But, obviously, as Will and Victoria have said, like, we'll probably change it again in some way, you know, a year, two years, three years from now. VICTORIA: Yeah, and I'm definitely bringing my interest around DevOps and platform engineering, so you'll see more guests who have that focus in their background. And with that, sometimes my interview style is more; how do I ask a question that I can't read from your developer docs and that I might not understand the answer to? [laughs] That's kind of where I like to go with it. So yeah, I'm really excited about...it's probably one of my favorite parts of my job here at thoughtbot because I get to meet so many interesting people. And, hopefully, that's interesting to everyone else [laughs] and our guests, yeah. BEN: Totally. Well, I dramatically underestimated how awesome it would be to meet all kinds of cool people in the industry when I started the podcast. I didn't truly connect in my head, like, wait a second, if I have a 45-minute conversation with, like, a lot of prominent, awesome people in our field, that's going to be really interesting and useful for me. So, I think, yeah, it's nice to be in the hosting seat. VICTORIA: And it's so surprising how I'll meet someone at a conference, and I'll invite them onto the podcast. And the way it winds up is that whatever we're talking about on the show is directly relevant to what I'm working on or a problem that I have. It's been incredible. And I really appreciate you for coming back for our 500th Episode here. CHAD: Ben, thanks very much again for joining us, and congratulations on all the success with Tuple. And I wish you the best. BEN: Thank you so much. Thanks for being a continuing customer. I really appreciate it. CHAD: Next, we caught up with Chris Toomey, who had a run as co-host of the show with Ben throughout 2016. CHRIS: Hi there. Thanks for having me. So, we're talking with all of the past hosts. I know you joined the show, and you were on it with Ben. And then you moved over to The Bike Shed, right? CHRIS: Yeah. So, I had co-hosted with Ben for about six months. And then I think I was transitioning off of Upcase, and so that ended sort of the Giant Robots “let's talk about business” podcast tour for me. And then, I went back to consulting for a while. And, at some point, after Derek Prior had left, I took over as the host of The Bike Shed. So, I think there was probably, like, a year and a half, two-year gap in between the various hostings. CHAD: Are you doing any podcasting now? CHRIS: I'm not, and I miss it. It was a lot of fun. It was, I think, an ideal medium for me. I'm not as good at writing. I tend to over-edit and overthink. But when you get me on a podcast, I just start to say what's in my head, and I tend to not hate it after the fact. So [chuckles], that combination I found to be somewhat perfect for me. But yeah, lacking that in my current day-to-day. CHAD: Well, what's been taking up your time since you left? CHRIS: I had decided it was time to sort of go exploring, try and maybe join a startup, that sort of thing. I was sort of called in that direction. So, just after I left thoughtbot, I did a little bit of freelancing, but that was mostly to sort of keep the lights on and start to connect with folks and see if there might be an opportunity out there. I was able to connect with a former thoughtbot client, Sam Zimmerman, who was looking to start something as well. And so, we put our act together and formed a company called Sagewell, which was trying to build a digital financial platform for seniors, which is a whole bunch of different complicated things to try and string together. So, that was a wonderful experience. I was CTO of that organization. And I think that ran for about two and a half years. Unfortunately, Sagewell couldn't quite find the right sort of sticking point and, unfortunately, shut down a little bit earlier in this year. But that was, I would say, the lion's share of what I have done since leaving thoughtbot, really wonderful experience, got to learn a ton about all of the different aspects of building a startup. And I think somewhat pointedly learned that, like, it's messy, but I think I do like this startup world. So, since leaving Sagewell, I've now joined a company called August Health, which has a couple of ex-thoughtboters there as well. And August is post their Series A. They're a little bit further along in their journey. So, it was sort of a nice continuation of the startup experience, getting to see a company a little bit further on but still with lots of the good type of problems, lots of code to write, lots of product to build. So, excited to be joining them. And yeah, that's mostly what's taking up my time these days. CHAD: So, I know at Sagewell, you made a lot of technical architecture, team decisions. It was Rails in the backend, Svelte in the frontend, if I'm not mistaken. CHRIS: Yep, that's correct. CHAD: You know, hindsight is always 2020. Is there anything you learned along the way, or given how things ended up, that you would do differently? CHRIS: Sure. I was really happy with the tech stack that we were able to put together. Svelte was probably the most out there of the choices, I would say, but even that, it was sort of relegated to the frontend. And so, it was a little bit novel for folks coming into the codebase. Most folks had worked in React before but didn't know Svelte. They were able to pick it up pretty quickly. But Inertia.js was actually the core sort of architecture of the app, sort of connected the frontend and the backend, and really allowed us to move incredibly quickly. And I was very, very happy with that decision. We even ended up building our mobile applications, both for iOS and Android. So, we had native apps in both of the stores, but the apps were basically wrappers around the Rails application with a technology similar to Turbolinks native–if folks are familiar with that so, sort of a WebView layer but with some native interactions where you want. And so, like, we introduced a native login screen on both platforms so that we could do biometric login and that sort of thing. But at the end of the day, most of the screens in the app didn't need to be differentiated between a truly native mobile app and what like, mobile WebView would look like. So, we leaned into that. And it was incredible just how much we were able to do with that stack and how quickly we were able to move, and also how confidently we were able to move, which was really a nice thing. Having the deep integration between the backend and the frontend really allowed a very small team to get a lot done in a short time. CHAD: Does that code live on in any capacity? CHRIS: No. CHAD: Oh. How does that make you feel? [chuckles] CHRIS: It makes me feel very sad, I will say. That said, I mean, at the end of the day, code is in service of a business. And so, like, the code...there are, I think, probably a couple of things that we might be able to extract and share. There were some interesting...we did some weird stuff with the serializers and some, like, TypeScript type generation on the frontend that was somewhat novel. But at the end of the day, you know, code is in service of a business, and, unfortunately, the business is not continuing on. So, the code in the abstract is...it's more, you know, the journey that we had along the way and the friends we made and whatnot. But I think, for me, sort of the learnings of I really appreciate this architecture and will absolutely bring it to any new projects that I'm building from, you know, greenfield moving forward. VICTORIA: I'm curious what it was like to go from being a consultant to being a big player in a startup and being responsible for the business and the technology. How did that feel for you? CHRIS: I would say somewhat natural. I think the consulting experience really lent well to trying to think about not just the technical ramifications but, you know, what's the business impact? How do we structure a backlog and communicate about what features we want to build in what order? How do we, you know, scope a minimal MVP? All those sorts of things were, I think, really useful in allowing me to sort of help shape the direction of the company and be as productive of an engineering team as we could be. CHAD: A lot of the projects you worked on at thoughtbot were if not for startups, helping to launch new products. And then, a lot of the work you did at thoughtbot, too, was on Upcase, which was very much building a business. CHRIS: Yes. I definitely find myself drawn in that direction, and part of like, as I mentioned, I seem to be inclined towards this startup world. And I think it's that, like, the intersection between tech and business is sort of my sweet spot. I work with a lot of developers who are really interested in getting sort of deeper into the technical layers, or Docker and Kubernetes and orchestration. And I always find myself a little bit resistant to those. I'm like, I mean, whatever. Let's just...let's get something out there so that we can get users on it. And I am so drawn to that side, you know, you need both types of developers critically. I definitely find myself drawn to that business side a little bit more than many of the folks that I work with, and helping to bridge that gap and communicate about requirements and all those sort of things. So, definitely, the experience as a consultant really informed that and helped me have sort of a vocabulary and a comfort in those sort of conversations. WILL: How did Upcase come about? Because I know I've talked to numerous people who have gone through Upcase. I actually went through it, and I learned a ton. So, how did that come about? CHRIS: I think that was a dream in Ben Orenstein's eye. It started as thoughtbot Learn many, many years ago. There was a handful of workshops that had been recorded. And so, there were the video recordings of those workshops that thoughtbot used to provide in person. Ben collected those together and made them sort of an offering on the internet. I think Chad, you, and I were on some podcast episode where you sort of talked about the pricing models over time and how that went from, like, a high dollar one-time download to, like, $99 a month to $29 a month, and now Upcase is free. And so, it sort of went on this long journey. But it was an interesting exploration of building a content business of sort of really leaning into the thoughtbot ideal of sharing as much information as possible, and took a couple of different shapes over time. There was the weekly iterations of the video series that would come out each week, as well as the, like, longer format trails, and eventually some exercises and whatnot, but very much an organic sort of evolving thing that started as just a handful of videos and then became much more of a complete platform. I think I hit the high points there. But, Chad, does that all sound accurate to you? CHAD: Yeah, I led the transition from our workshops to Learn, which brought everything together. And then, I stepped away as product manager, and Ben took it the next step to Upcase and really productized it into a SaaS sort of monthly recurring billing model and took it over from there. But it still exists, and a lot of the stuff there is still really good [laughs]. CHRIS: Yeah, I remain deeply proud of lots of the videos on that platform. And I'm very glad that they are still out there, and I can point folks at them. VICTORIA: I love that idea that you said about trying to get as much content out there as possible or, like, really overcommunicate. I'm curious if that's also stayed with you as you've moved on to startups, about just trying to get that influence over, like, what you're doing and how you're promoting your work continues. CHRIS: I will say one of the experiences that really sticks with me is I had followed thoughtbot for a while before I actually joined. So, I was reading the blog, and I was listening to the podcasts and was really informing a lot of how I thought about building software. And I was so excited when I joined thoughtbot to, like, finally see behind the curtain and see, like, okay, so, what are the insider secrets? And I was equal parts let down...actually, not equal parts. I was a little bit let down but then also sort of invigorated to see, like, no, no, it's all out there. It's like, the blog and the open-source repos and those sort of...that really is the documentation of how thoughtbot thinks about and builds software. So, that was really foundational for me. But at the same time, I also saw sort of the complexity of it and how much effort goes into it, you know, investment time Fridays, and those sort of things. Like, a thoughtbot blog post is not a trivial thing to put up into the world. So many different people were collaborating and working on it. And so, I've simultaneously loved the sharing, and where sharing makes sense, I've tried to do that. But I also recognize the deep cost. And I think for thoughtbot, it's always made sense because it's been such a great mechanism for getting the thoughtbot name out there and for getting clients and for hiring developers. At startups, it becomes a really interesting trade-off of, should we be allocating time to building up sort of a brand in the name and getting ourselves, you know, getting information out there? Versus, should we be just focusing on the work at hand? And most organizations that I've worked with have bias towards certainly less sharing than thoughtbot, but just not much at all. Often, I'll see folks like, "Hey, maybe we should start a blog." And I'm like, "Okay, let's just talk about how much effort that [laughs] actually looks like." And I wonder if I'm actually overcorrected on that, having seen, you know, the high bar that thoughtbot set. CHAD: I think it's a struggle. This is one of my [laughs] hot topics or spiels that I can go on. You know, in most other companies, that kind of thing only helps...it only helps in hiring or the people being fulfilled in the work. But at most companies, your product is not about that; that's not what your business is. So, having a more fulfilled engineering team who is easier to hire—don't get me wrong, there are advantages to that—but it doesn't also help with your sales. CHRIS: Yes. CHAD: And at thoughtbot, our business is totally aligned with the people and what we do as designers and developers. And so, when we improve one, we improve the other, and that's why we can make it work. That is marketing for the product that we actually sell, and that's not the case at a SaaS software company. CHRIS: Yes, yeah, definitely. That resonates strongly. I will say, though, on the hiring side, hiring at thoughtbot was always...there was...I won't say a cheat code, but just if someone were to come into the hiring process and they're like, "Oh yeah, I've read the blog. I listen to the podcast," this and that, immediately, you were able to skip so much further into the conversation and be like, "Okay, what do you agree with? What do you disagree with? Like, let's talk." But there's so much. Because thoughtbot put so much out there, it was easy to say, like, "Hey, this is who we are. Do you like that? Is that your vibe?" Whereas most engineering organizations don't have that. And so, you have to try and, like, build that in the context of, you know, a couple of hour conversations in an interview, and it's just so much harder to do. So, again, I've leaned in the direction of not going anywhere near thoughtbot's level of sharing. But the downside when you are hiring, you're like, oh, this is going to be trickier. CHAD: Yeah. One of the moments that stands out in my mind, and maybe I've told this story before on the podcast, but I'll tell it again. When we opened the New York studio, it was really fast growing and was doing a lot of hiring. And one of the people who had just joined the company a couple of weeks before was doing an interview and rejected the person was able to write an articulate reason why. But it all boiled down to this person is, you know, not a fit for thoughtbot. Based on what they were able to describe, I felt very confident with the ability or with the fact that they were able to make that call, even though they had been here only a couple of weeks, because they joined knowing who we were, and what we stand for, and what our culture and our values are, and the way that we do things, and all that kind of thing. And so, yeah, that's definitely a huge benefit to us. VICTORIA: I've certainly enjoyed that as well, as someone who hires developers here and also in meeting new companies and organizations when they already know thoughtbot. That's really nice to have that reputation there, coming from my background—some really more scrappier startup kind of consulting agencies. But, you know, I wanted to talk a little bit more about your podcasting experience while you're here. So, I know you were on both The Bike Shed and Giant Robots. Which is the better podcast? [laughter] So, what's your...do you have, like, a favorite episode or favorite moment, or maybe, like, a little anecdote you can share from hosting? CHRIS: Well, I guess there's, like, three different eras for me in the podcasting. So, there's Giant Robots with Ben talking more about business stuff, and I think that was really useful. I think it was more of a forcing function on me because I sort of...Both Ben and I were coming on; we were giving honest, transparent summaries of our, like, MRR and stats and how things were growing, and acted as sort of an accountability backstop, which was super useful but also just kind of nerve-wracking. Then, when I joined the Bike Shed, the interviewing sequence that I did each week was just a new person that I was chatting with. And I sort of had to ramp them up on, hey, here's a quick summary on how to think about podcasting. Don't worry, it'll be great. Everybody have fun. But I was finding each of the guests. I was sort of finding a topic to talk about with them. So, that ended up being a lot more work. And then, the last three years chatting with Steph that was by far my favorite. There was just such a natural back-and-forth. It really was just capturing the conversations of two developers at thoughtbot and the questions we would ask each other as we hit something complicated in a piece of code or, "Oh, I saw this, you know, article about a new open-source repository. What do you think about that?" It was so much easier, so much more natural, and, frankly, a lot of fun to do that. And, two, I actually do have an answer to the favorite podcast episode, which is the first episode that Steph was ever on. It was before she actually joined as a co-host. But it was called “What I Believe About Software.” And it was just this really great, deep conversation about how we think about software. And a lot of it is very much, like, thoughtbot ideals, I would say. But yeah, Steph came in and just brought the heat in that first episode, and I remember just how enjoyable that experience was. And I was like, all right, let's see if I can get her to hang out a little bit more, and, thankfully, she was happy to join. WILL: What was your favorite position, I guess you can call it? Because you say you like the mixture of business and, you know, development. So, you've been in leadership as development director, CTO. You've been a web developer. You've been over content, like, with Upcase. What was your favorite position [inaudible 16:43] you were doing, and why was it your favorite? CHRIS: The development director role feels like sort of a cheating answer, but I think that would be my answer because it contained a handful of things within it. Like, as development director, I was still working on client projects three days a week. And then, one day a week was sort of allocated to the manager-type tasks, or having one-on-ones with my team sort of helping to think about strategy and whatnot. And then, ideally, still getting some amount of investment time, although the relative amounts of those always flexed a little bit. Because that one sort of encompassed different facets, I think that's going to be my answer. And I think, like, some of what drew me to consulting in the first place and kept me in that line of work for seven years was the variety, you know, different clients, as well as, even within thoughtbot, different modes of working in podcasts or video. Or there was a bootcamp that I taught, a session of Metis, which that was a whole other experience. And so, getting that variety was really interesting. And I think as sort of a tricky answer to your question, the development director role as a singular thing contained a multitude, and so I think that was the one that would stand out to me. It's also the most, you know, the one that I ended on, so [laughs] it might just be recency bias, but yeah. VICTORIA: Oh, I love that. Is there anything else that you would like to promote on the podcast today? CHRIS: No, although as you ask the question, I feel like I should, I don't know, make some things to promote, get back into some, I don't know, content generation or something like that. But for now, no. I'm, you know, diving into the startup life, and it's a wonderful and engrossing way to do work, but it does definitely take up a lot of my headspace. So, it's an interesting trade-off. But right now, I don't know; if folks are online and they want to say hi, most of my contact information is readily available. So, I would love to say hi to folks, anyone that listened in the past or, you know, has any thoughts in the now. Would love to connect with folks. But otherwise, yeah, thank you so much for having me on. CHAD: In 2017, I took over from Ben as solo host of the show but was joined by Lindsey Christainson as cohost in 2019. After some time away from thoughtbot, Lindsey is back with us and we sat down to catch up with her. VICTORIA: Why don't you tell me about your current role with thoughtbot? LINDSEY: I am currently supporting marketing and business development at thoughtbot, as well as working as a marketing consultant for thoughtbot clients. VICTORIA: Great. And I understand that you had worked with thoughtbot many years ago, and that's when you also came on as a co-host of Giant Robots. Is that right? LINDSEY: Yeah, a couple of years ago. I left thoughtbot in spring of 2021. And I forget how long my stint was as a co-host of Giant Robots, but over a year, maybe a year and a half, two years? CHAD: Yeah, I think that's right. I think you started in 2019. LINDSEY: Yeah. Yeah, that sounds right. And Chad and I were co-hosts, I think, similar to the setup today in which sometimes we hosted together, and sometimes we were conducting interviews separately. CHAD: And then we sort of introduced a second season, where we followed along with a batch of companies over the course of the entire season. And that was fun, and we learned a lot. And it was nice to have consistent guests. LINDSEY: Yeah, that was a lot of fun. I really liked that format. I don't know; they almost were, like, more than guests at that point. They were just like other co-hosts [laughs] that we could rely on week in, week out to check in with them as they're working on early-stage companies. So, every time we checked in with them, they usually had some new, exciting developments. WILL: I really like that idea. How did y'all come up with that? CHAD: I'm not sure. I think a few years before I had taken over hosting of the show, and I forget...my memory maybe is that I went to Lindsey and said, "You know, let's do something different." But I'm not sure. Does that match your memory, Lindsey? LINDSEY: Yeah, I think there were two main drivers; one was I think you were feeling like you were having similar conversations in the interviews every time. Like, you couldn't get to a certain depth because every time you were interviewing someone, you were doing, like, the, "Well, tell me your founding story." And, you know, how did you raise funding? It kind of got a little bit repetitive. And then, on the side, the few we had done together, I think we both really enjoyed. So, we were thinking, like, what's the format in which the two of us could co-host together more regularly? Because I'm a pleasure to talk to [laughter]. I think you were like, I need to talk to Lindsey more. [inaudible 3:13] VICTORIA: What is your hosting style? How would you describe your approach to hosting a podcast? LINDSEY: I mean, obviously, it's a podcast about products and business. I think as a marketer, I am, you know, drawn a lot to the marketing side, so tending to ask questions around go-to-market audience, users. That's always just, like, a particular interest of mine. But then also, like, the feelings. I love asking about the feelings of things, you know, how did it feel when you started? How did it feel when you made this tough decision? So, that's another thing I think I noticed in my interviews is asking about some of the emotions behind business decisions. VICTORIA: And I like hearing about how people felt at the time and then how they felt afterwards [laughs]. And, like, how people around them supported each other and that type of thing. That's really fun. I'm curious, too, from your marketing background and having to do with podcasts like; some founders, I think, get the advice to just start a podcast to start building a community. But I'm curious on your thoughts about, like, how does podcasting really play into, like, business and marketing development for products? LINDSEY: Oh yeah. It's become definitely, like, a standard channel in B2B these days. I feel like that it's pretty typical for a company to have a podcast as one way that they engage their audience and their users. In marketing, you're really vying for people's attention, and people's attention span is getting shorter and shorter. So, like, if you have an ad or a blog, you're getting, like, seconds, maybe minutes of someone's attention. And whereas something like a podcast offers a unique channel to have someone's undivided attention for, you know, 30 minutes, an hour, and if you're lucky, you know, checking back in week over week. So, it became a really popular method. That said, I think you're probably also seeing the market get saturated [laughs] with podcasts now, so some diminishing returns. And, you know, as always, kind of looking for, you know, what's the next way? What's the next thing that people are interested in in ways to capture their attention? CHAD: What is the next thing? LINDSEY: I don't know, back to micro-content? TikTok videos -- CHAD: Yeah, I was going to say TikTok, yeah. LINDSEY: Yeah, you know, 10-30 seconds, what can you communicate? VICTORIA: I see people live streaming on Twitch a lot for coding and developer products. LINDSEY: Yeah, I think we've seen some of that, too. We've been experimenting more at thoughtbot with live streaming as well. It's another interesting mechanism. But yeah, I don't know, it's interesting. It's another form of, like, community and how people engage with their communities. So, it's always evolving. It's always evolving, and sometimes it's not. Sometimes, people just do want to get in a room together, too, which is always interesting. WILL: What has been, in your experience, the good the bad? Like, how do you feel about the way that it has shifted? Because I think you started in, like, 2000, like, kind of earlier 2000, 2005, something around there. And it was totally different than now like you're saying. Because I feel like, you know, Channel 5 30-second ad, you know, with some of the marketing depending on what you're doing, to now to where you're, like, you're paying influencers to advertise your product, or you're doing an ad. Or it's more social media-driven and tech-driven. What has been your opinion and feelings on the way that it has grown and evolved? LINDSEY: Marketing, in general, yeah, I graduated college in 2005 and started my marketing career. And yeah, you could, like, actually get people to click on banner ads back then, which was pretty [inaudible 07:14] [laughs]. WILL: I forgot about banner ads [laughs]. LINDSEY: I don't know, yeah. I don't know. In order for myself to not just get too frustrated, I think I've got to, like, view it as a game kind of. What new things are we going to try? You know, what do we see work? But it can really depend. And I've always been in B2B side of things. And consumer, I'm sure, has its own kind of evolution around how people engage and how they consume content and byproducts. But in B2B, you know, it can really depend on industry too. You know, I'm working with a client right now in the senior living space, and they're really big in in-person conferences. So, that's how people consume, get a lot of their information and, make connections, and learn about new products. So, it's been interesting to work in an industry that what might be considered, like, a little bit more old-school channels are still effective. And then just thinking about how you weave in the new channels with the existing ones without ignoring them. They might get information in conferences, but they're still a modern human who will then, you know, search online to learn more, for example. VICTORIA: It reminds me of a phrase I like to say, which is that, like, technology never dies; you just have more of it. There's just more different options and more different ways to do things. And some people are always, you know, sometimes you have to be flexible and do everything. CHAD: So, tell us more about what you did in between...after you left thoughtbot, what did you do? LINDSEY: I was heading up B2B marketing for a company called Flywire, which is headquartered in Boston but is a global company now. And they were just kind of starting their B2B business unit, which, as I mentioned, B2B is my personal specialty. I had been connected to their CMO through the Boston startup community. And yeah, I was helping them kind of launch their go-to-market for B2B. The industries they were in before...they got their start in higher education and then expanded in healthcare and found a niche in luxury travel, and then we were figuring out the B2B piece. But yeah, I was there for about a year and a half. They actually went public the second week I was there, which was an interesting [laughs] experience. I knew they were, like, on that journey, but it was kind of funny to be there the second week, and people were, like, "Congrats." And I was like, "Well, I definitely didn't have anything to do with it because I just finished my onboarding, but thank you," [laughs]. CHAD: One of the things that really impressed me when you joined thoughtbot was the way in which you learned about who we were and really internalized that in a way where you were then able to pretty meaningfully understand our market, our positioning in the market, and come up with new strategies for us. I assume that's something you're good at in general [laughs]. How do you approach it? How did you approach it when you joined Flywire, for example? And how was it the same or different than how you approached thoughtbot? LINDSEY: Ooh, yeah, that's a good question. And I appreciate that comment because it's difficult. But I think, yeah, with any new organization that I'm joining, you know, I think starting out with your kind of mini-listening tour of your key stakeholders across, you know, the different departmental focuses to get a sense of, what are the challenges? What are the opportunities? It's actually like, you know, it's the SWOT analysis, kind of trying to fill in your own mind map of a SWOT analysis of where the company is. What are the major hurdles you're facing? Where are people trying to go? What have they tried that's worked? What have they tried that's failed? But then, like, I think for the culture component, I think a part of that maybe is, like, feel, and maybe something that I do have a knack for. Again, maybe this is, like, you know, emotional intelligence quotient, where it's like, you know, but it's the company, you know, who is this company? What is important to them? How do they work and go about things? I know thoughtbot is certainly very unique, I think, in that arena in terms of being, like, a really value-driven company, and one where especially, like, marketing and business work is, like, distributed across teams in a really interesting way. You know, I'm sure the fact that it fascinated me and was something I could get passionate and get behind was something that also helped me understand it quickly. CHAD: I was excited that...or it was sort of a coincidence because I had reached out to you and without realizing that you had left Flywire. And Kelly, who had been doing a combined sales and marketing role, was going on parental leave. And so, it was fortuitous [laughs] that you were able to come back and help us and provide coverage, like, Kelly was out. LINDSEY: Yeah, it definitely felt like stars aligned moment, which, you know, I'm pretty woo-woo, so I believe in [laughter]...I believe in that kind of thing. You know, yeah, it was wild. It really did feel like your email came out of nowhere. And, you know, I mentioned it, obviously, to my partner and my friends. And they were like, "Oh, he definitely knows, like, that you left your last company." And I'm like, "I actually don't think he does [laughter]. I actually don't think he does." Yeah, and then we started chatting about me coming back to help. And it was great. thoughtbot makes it hard to work anywhere else [laughs]. So, I was happy to come back. I missed the team. CHAD: And one of the exciting things, and you've mentioned it, is you're not just doing marketing for thoughtbot now. We have started to offer your services to our clients. LINDSEY: Yeah, I'm super excited about this. And it's something I'd started thinking about. I had decided to take some time off between Flywire and my next thing and had started thinking about doing marketing, consulting. And as I'm doing that, I'm thinking a lot about how thoughtbot does consulting and, you know, wanting to emulate something like that. So, I started back up at thoughtbot. That wasn't part of the plan. I was just going to, you know, fill in for Kelly and help with marketing things. But then, you know, a good opportunity arose to work on a client, and I was really excited. When, you know, Chad, you and I chatted through it, we came to the conclusion that this was something worth exploring under the, you know, thoughtbot umbrella. And it's been a really great experience so far. And we now have brought on another client now. And if you're listening and need early-stage B2B marketing support, reach out to lindsey@thoughtbot.com. CHAD: Definitely. And Lindsey is pretty good, so you're going to like it [laughs]. LINDSEY: Yeah, you're going to like the way you look. WILL: Yeah, definitely. Because I can even feel your presence here, you know, coming back. Because even like, you know, the market where it's at now and some of the suggestions that, you know, you've been helping us. For example, like, I do a lot of React Native, and you're like, "Hey, you know, blog posts have done a lot of traction, you know, let's get some more blog posts out in the market to help with the traffic and everything." So, the question I have with that is, like, thank you for even suggesting that because it's, like, those little things that you don't even think about. It's like, oh yeah, blog posts, that's an easy transition to help the market, clients, things like that. But with the market the way it is, what has been your experience working during this time with the market? I don't know if you want to call it struggling, but whatever you want to call it that, it's doing [laughs]. LINDSEY: Yeah, I mean, the economy is difficult now. We also went through a really tough spot when I was here last time. During COVID, you know, we faced a major company challenge. And, I mean, I'll let Chad speak to it, but I would imagine it's probably one of the bigger, like, economic inflection points that you faced. Would you say that? CHAD: Yeah, definitely. The thing about it that made it worse was how quickly it happened. You know, it was something that you didn't see coming, and then, you know, about 40% of our business went away in a single month. That's the kind of thing that was a real shock to the system. I think the thing that made it difficult, too, was then the aspects of COVID, where we were no longer able to go into our studios. We were all working remotely. We were isolated from each other. And so, that made executing on what needed to be done in order to make the company survive additionally challenging. LINDSEY: Yeah, so I think, like, going through that experience, also, and seeing how the team and the leadership team rallied together to get through it. And then, you know, ultimately, I think 2021 and 2022 have, like, really good years. That was a really positive experience. And something I'll definitely take with me for a while is just, like, keeping a cool head and just knowing you have, like, really smart, talented folks with you working on it and that you can get through it. And just, like, doing some, I mean, we relied on what we did best, which was, like, design thinking, using design exercise to think about, like, how we might re-organize the company, or what other services we might try launching, or how might we re-package, you know, larger services into smaller more palatable services when people have, like, kind of tighter purse strings. So, that was, like, a great educational experience, and I think something we just continue to do now: be open to change, be open to changing how we package services, what clients we go after, and coming at it with, like, an agile, experimental mindset and try to find out what works. VICTORIA: I really appreciate that. And it aligns now with the new service we've developed around you and the marketing that you provide. And I'm curious because I've had founders come up to me who say they need help with marketing or they need to, like, figure out their marketing plans. So, say you've met a founder who has this question, like, what questions do you ask them to kind of narrow down what it is they really need and really want to get out of a marketing plan? LINDSEY: I've been thinking about this a lot recently. And, like, obviously, I see other marketing leaders in the market. Marketers like to talk about what they do on LinkedIn [laughs], so I get to...I read a lot about different people's approaches to this. And some people kind of go in and are like, okay, this is what you need. This is how we're going to do it, and they start executing on it. And I really do take a very collaborative approach with founders. I think they're, especially in early stage, they're your most important asset in a way, and a lot of their intuition around the market and the business, you know, it's gotten them to where they're at. And so, I think starting from the point of, like, taking what they view as priorities or challenges, and then helping them better explore them or understand them with my own marketing experience and expertise, to
It's updates on the work front today! Stephanie was tasked with removing a six-year-old feature flag from a codebase. Joël's been doing a lot of small database migrations. A listener question sparked today's main discussion on gerunds' interesting relationship to data modeling. Episode 386: Value Objects Revisited: The Tally Edition (https://www.bikeshed.fm/386) RailsConf 2017: In Relentless Pursuit of REST by Derek Prior (https://www.youtube.com/watch?v=HctYHe-YjnE) REST Turns Humans Into Database Clients (https://chrislwhite.com/rest-contortion/) Parse, don't validate (https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/) Wikipedia Getting to Philosophy (https://en.wikipedia.org/wiki/Wikipedia:Getting_to_Philosophy) 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. STEPHANIE: And I'm Stephanie Minn. And together, we're here to share a bit of what we've learned along the way. JOËL: So, Stephanie, what's new in your world? STEPHANIE: So, this week, I've been tasked with something that I've been finding very fun, which is removing a six-year-old feature flag from the codebase that is still very much in use in the sense that it is actually a mechanism for providing customers access to a feature that had been originally launched as a beta. And that was why the feature flag was introduced. But in the years since, you know, the business has shifted to a model where you have to pay for those features. And some customers are still hanging on to this beta feature flag that lets them get the features for free. So one of the ways that we're trying to convert those people to be paying for the feature is to, you know, gradually remove the feature flag and maybe, you know, give them a heads up that this is happening. I'm also getting to improve the codebase with this change as well because it has really been propagating [laughs] in there. There wasn't necessarily a single, I guess, entry point for determining whether customers should get access to this feature through the flag or not. So it ended up being repeated in a bunch of different places because the feature set has grown. And so, now we have to do this check for the flag in several places, like, different pages of the application. And it's been really interesting to see just how this kind of stuff can grow and mutate over several years. JOËL: So, if I understand correctly, there's kind of two overlapping conditions now around this feature. So you have access to it if you've either paid for the feature or if you were a beta tester. STEPHANIE: Yeah, exactly. And the interesting thought that I had about this was it actually sounds a lot like the strangler fig pattern, which we've talked about before, where we've now introduced the new source of data that we want to be using moving forward. But we still have this, you know, old limb or branch hanging on that hasn't quite been removed or pruned off [chuckles] yet. So that's what I'm doing now. And it's nice in the sense that I can trust that we are already sending the correct data that we want to be consuming, and it's just the cleanup part. So, in some ways, we had been in that half-step for several years, and they're now getting to the point where we can finally remove it. JOËL: I think in kind of true strangler fig pattern, you would probably move all of your users off of that feature flag so that the people that have it active are zero, at which point it is effectively dead code, and then you can remove it. STEPHANIE: Yeah, that's a great point. And we had considered doing that first, but the thing that we had kind of come away with was that removing all of those customers from that feature flag would probably require a script or, you know, updating the production data. And that seemed a bit riskier actually to us because it wasn't as reversible as a code change. JOËL: I think you bring up a really interesting point, which is that production data changes, in general, are just scarier than code changes. At least for me, it feels like it's fairly easy generally to revert a code change. Whereas if I've messed up the production database, [laughs] that's going to be unpleasant few days. STEPHANIE: What's interesting is that this feature flag is not really supported by a nice user interface for managing it. And so, we inevitably had to do a more developer-focused solution to remove these customers from being able to access this feature. And so, the two options, you know, that we had available were to do it through data, like I mentioned, or do it through that code change. And again, I think we evaluated both options. But what's kind of nice about doing it with the code change is that when we eventually get to delete those feature flag records, it will be really nice and easy. JOËL: That's really exciting. One thing that's different about kind of more mature projects is that we often get to do some kind of change management, unlike a greenfield app where you just get to, oh, let's introduce this new thing, cool. Oftentimes, on a more mature project, before you introduce the new thing, you have to figure out, like, what is the migration path towards that? Is that a kind of work that you enjoy? STEPHANIE: I think this was definitely an exercise in thinking about how to break this down into steps. So, yeah, that change management process you mentioned, I, like, did find a lot of satisfaction in trying to break it up, you know, especially because I was also thinking that you know, maybe I am not able to see the complete, like, cleanup and removal, and, like, where can someone pick up after me? In some ways, I feel like I was kind of stepping into that migration, you know, six years [laughs] in the making from beta to the paid product. But I think I will feel really satisfied if I'm able to see this thing through and get to celebrate the success of saying, hey, like, I removed...at this point, it's a few hundred lines of code. [laughs] And also, you know, with the added business value of encouraging more customers to pay for the product. But I think I also I'm maybe figuring out how to accept like, okay, like, how could I, like, step away from this in the middle and be able to feel good that I've left it in a place that someone else could see through? JOËL: So you mentioned you're taking this over from somebody else, and this has been kind of six years in the making. I'm curious, is the person who introduced this feature flag six years ago are they even still at the company? STEPHANIE: No, they are not, which I think is pretty typical, you know, it's, like, really common for someone who had all that context about how it came to be. In fact, I actually didn't even realize that the feature flag was the original beta version of the product because that's not what it's called. [laughs] And it was when I was first onboarding onto this project, and I was like, "Hey, like, what is this? Like, why is this still here?" Knowing that the canonical, you know, version that customers were using was the paid version. And the team was like, "Oh, yeah, like, that's this whole thing that we've been meaning to remove for a long time." So it's really interesting to see the lifecycle, like, as to some of this code a little bit. And sometimes, it can be really frustrating, but this has felt a little more like an archaeology dig a little bit. JOËL: That sounds like a really interesting project to be on. STEPHANIE: Yeah. What about you, Joël, what's new in your world? JOËL: So, on my project, I've been having to do a lot of small database migrations. So I've got a bunch of these little features to do that all involve doing database migrations. They're not building on each other. So I'm just doing them all, like, in different feature branches, and pushing them all up to GitHub to get reviewed, kind of working on them in parallel. And the problem that happens is that when you switch from one branch where you've run a migration to another and then run migrations again, some local database state persists between the branch switch, which means that when you run the migrations, then this app uses a structure.sql. And the structure.sql has a bunch of extra junk from other branches you've been on that you don't want as part of your diff. And beyond, like, two or three branches, this becomes an absolute mess. STEPHANIE: Oh, I have been there. [laughs] It's always really frustrating when I switch branches and then try to do my development and then realize that I have had my leftover database changes. And then having to go back and then always forgetting what order of operations to do to reverse the migration and then having to re-migrate. I know that pain very well. JOËL: Something I've been doing for this project is when I switch branches, making sure that my structure SQL is checked out to the latest version from the main branch. So I have a clean structure SQL then I drop my local database, recreate an empty one, and run a rake db:schema:load. And that will load that structure file as it is on the main branch into the database schema. That does not have any of the migrations on this branch run, so, at that point, I can run a rake db:migrate. And I will get exactly what's on main plus what gets generated on this branch and nothing else. And so, that's been a way that I've been able to kind of switch between branches and run database operations without getting any cross-contamination. STEPHANIE: Cross-contamination. I like that term. Have you automated this at all, or are you doing this manually? JOËL: Entirely manually. I could probably script some of this. Right now...so it's three steps, right? Drop, create, schema load. I just have them in one command because you can chain Unix commands with a double ampersand. So that's what I'm doing right now. I want to say there's a db:reset task, but I think that it uses migrate rather than schema load. And I don't want to actually run migrations. STEPHANIE: Yeah, that would take longer. That's funny. I do love the up arrow key [laughs] in your terminal for, you know, going back to the thing you're running over and over again. I also appreciate the couple extra seconds that you're spending in waiting for your database to recreate. Like, you're paying that cost upfront rather than down the line when you are in the middle of doing [laughs] what you're trying to do and realize, oh no, my database is not in the state that I want it to be for this branch. JOËL: Or I'm dealing with some awful git conflict when trying to merge some of these branches. Or, you know, somebody comments on my PR and says, "Why are you touching the orders table? This change has nothing to do with orders." I'm like, "Oh, sorry, that actually came out of a different thing that I did." So, yep, keeping those diffs small. STEPHANIE: Nice. Well, I'm glad that you found a way to manage it. JOËL: So you mentioned the up arrow key and how that's really nice in the terminal. Something that I've been relying on a lot recently is reverse history search, CTRL+R in the terminal. That allows me to, instead of, like, going one by one in order of the history, filter for something that matches the thing that I've written. So, in this case, I'll hit CTRL+R, type, you know, Rails DB or whatever, then immediately it shows me, oh, did you want this long command? Hit enter, and I'm done. Even if I've done, you know, 20 git commands between then and the last time I ran it. STEPHANIE: Yeah, that's a great tip. So, a few weeks ago, we received a listener question from John, and he was responding to an episode where I'd asked about what the grammatical term is for verbs that are also nouns. He told us about the phrase, a verbal noun, for which there's a specific term called gerund, which is basically, in English, the words ending in ING. So, the gerund version of bike would be biking. And he pointed out a really interesting relationship that gerunds have to data modeling, where you can use a gerund to model something that you might describe as a verb, especially as a user interaction, but can be turned into a noun to form a resource that you might want to introduce CRUD operations for in your application. So one example that he was telling us about is the idea of maybe confirming a reservation. And, you know, we think of that as an action, but there is also a noun form of that, which is a confirmation. And so, confirmation could be a new resource, right? It could even be backed at the database level. And now you have a simpler way of representing the idea of confirming a reservation that is more about the confirmation as the resource itself rather than some kind of append them to a reservation itself. JOËL: That's really cool. We get to have a crossover between grammar terms and programming, and being able to connect those two is always a fun day for me. STEPHANIE: Yeah, I actually find it quite difficult, I think, to come up with noun forms of verbs on my own. Like, I just don't really think about resources that way. I'm so used to thinking about them in a more tangible way, I suppose. And it's really kind of cool that, you know, in the English language, we have turned these abstract ideas, these actions into, like, an object form. JOËL: And this is particularly useful when we're trying to design RESTful either APIs or even just resources for a Rails app that's server-rendered so that instead of trying to create all these, like, extra actions on our controller that are verbs, we might decide to instead create new resources in the system, new nouns that people can do the standard 7 to. STEPHANIE: Yes. I like that better than introducing custom controller actions or routes that deviate from RESTful conventions because, you know, I probably have seen a slash confirm reservation [laughs] URL. And, you know, this is, I think, an interesting way of avoiding having too many of those deviating endpoints. JOËL: Yeah, I found that while Rails does have support for those, just all the built-in things play much more nicely if you're restricting yourself to the classic seven. And I think, in general, it's easier to model and think about things in a Rails app when you have a lot of noun resources rather than one giant controller with a bunch of kind of verb actions that you can do to it. In the more formal jargon, I think we might refer to that as RESTful style versus RPC style, a Remote Procedure Call. STEPHANIE: Could you tell me more about Remote Procedure Calls and what that means? JOËL: The general idea is that it's almost like doing a method call on an object somewhere. And so, you would say, hey, I've got an account, and I want to call the confirm method on it because I know that maybe underlying this is an ActiveRecord account model. And the API or the web UI is just a really thin layer over those objects. And so, more or less, whatever your methods on your object are, can be accessed through the API. So the two kind of mirror each other. STEPHANIE: Got it. That's interesting because I can see how someone might want to do that, especially if, you know, the account is the domain object they're using at the, you know, persistence layer, and maybe they're not quite able to see an abstraction for something else. And so, they kind of want to try to fit that into their API design. JOËL: So I have a perhaps controversial opinion, which is that the resources in your Rails application, so your controllers, shouldn't map one-to-one with your database tables, your models. STEPHANIE: So, are you saying that you are more likely to have more abstractions or various resources than what you might have at the database level? JOËL: Well, you know what? Maybe more, but I would say, in general, different. And I think because both layers, the controller layer, and the model layer, are playing with very different sets of constraints. So when I'm designing database tables, I'm thinking in terms of normalization. And so, maybe I would take one big concept and split it up into smaller concepts, smaller tables because I need this data to be normalized so that there's no ambiguity when I'm making queries. So maybe something that's one resource at the controller layer might actually be multiple tables at the database layer. But the inverse could also be true, right? You might have, in the example that John gave, you know, an account that has a single table in the database with just a Boolean field confirmed yes or no. And maybe there's just a generic account resource. But then, separately, there's also a confirmation resource. And so, now we've got more resources at the controller layer than at the database layer. So I think it can go either way, but they're just not tightly coupled to each other. STEPHANIE: Yeah, that makes sense. I think another way that I've seen this manifest is when, like you said, like, maybe multiple database tables need to be updated by, you know, a request to this endpoint. And now we get into [chuckles] what some people may call services or that territory of basically something. And what's interesting is that a lot of the service classes are named as verbs, right? So order, creator. And, like, whatever order of operations that needs to happen on multiple database objects that happens as a result of a user placing an order. But the idea that those are frequently named as verbs was kind of interesting to me and a bit of a connection to our new gerund tip. JOËL: That's really interesting. I had not made that connection before. Because I think my first instinct would be to avoid a service object there and instead use something closer to a form object that takes the same idea and represents it as a noun, potentially with the same name as the resource. So maybe leaning really heavily into that idea of the verbal noun, not just in describing the controller or the route but then also maybe the object backing it, even if it's not connecting directly to a database table. STEPHANIE: Interesting. So, in this case, would the form object be mapped closer to your controller resource? JOËL: Potentially, yes. So maybe I do have some kind of, like, object that represents a confirmation and makes it nicer to render the confirmation form on the edit page or the new page. In this case, you know, it's probably just one checkbox, so maybe it's not worth creating an object. But if there were multiple fields, then yes, maybe it's nice to create an in-memory object that has the same name as the resource. Similar maybe for a resource that represents multiple underlying database tables. It can be nice to have kind of one object that represents all of them, almost like a facade, I guess. STEPHANIE: Yeah, that's really interesting. I like that idea of a facade, or it's, like, something at a higher level representing hopefully, like, some kind of meaning of all of these database objects together. JOËL: I want to give a shout-out to talk from a former thoughtboter, Derek Prior—actually, former Bike Shed host—from RailsConf 2017 called In Relentless Pursuit of REST, where he digs into a lot of these concepts, particularly how to model resources in your Rails app that don't necessarily map one to one with a database table, and why that can be a good thing. Have you seen that talk? STEPHANIE: I haven't, but I love the title of it. It's a great pun. It's very evocative, I think because I'm really curious about this idea of a relentless pursuit. Because I think another way to react to that could be to be done with REST entirely and maybe go with something like GraphQL. JOËL: So instead of a relentless pursuit, it's a relentless...what's the opposite of pursuing? Fleeing? STEPHANIE: Fleeing? [laughs] I like how we arrived there at the same time. Yes. So now I'm thinking of I had mentioned a little bit ago on the show we had our spicy takes Lightning Talks on our Boost Team. And a fellow thoughtboter, Chris White, he had given a talk about Why REST Is Not the Best and for -- JOËL: Also, a great title. STEPHANIE: Yes, also, a great title. JOËL: I love the rhyming there. STEPHANIE: Yeah. And his reaction to the idea of trying to conform user interactions that don't quite map to a noun or an obvious resource was to potentially introduce GraphQL, where you have one endpoint that can service really anything that you can think of, I suppose. But, in his example, he was making the argument that human interactions are not database resources, right? And maybe if you're not able to find that abstraction as a noun or object, with GraphQL, you can encapsulate those ideas as closer to actions, but in the GraphQL world, like, I think they're called mutations. But it is, I think, a whole world of, like, deciding what you want to be changed on the server side that is a little less constrained to having to come up with the right abstraction. JOËL: I feel like GraphQL kind of takes that, like, complete opposite philosophy in that instead of saying, hey, let's have, like, this decoupling between the API layer and the database, GraphQL almost says, "No, let's lean into that." And yeah, you want to traverse the graph of, like, tables under the hood? Absolutely. You get to know the tables. You get to know how they're related to each other. I guess, in theory, you could build a middle layer, and that's the graph that gets traversed rather than the graph of the tables. In practice, I think most people build it so that the API layer more or less has access directly to tables. Has that been your experience? STEPHANIE: That's really interesting that you brought that up. I haven't worked with GraphQL in a while, but I was reading up on it before we started recording because I was kind of curious about how it might play with what we're talking about now. But the idea that it's graphed based, to me, was like, oh, like, that naturally, it could look very much like, you know, an entity graph of your relational database. But the more I was reading about the GraphQL schema and different types, I realized that it could actually look quite different. And because it is a little bit closer to your UI layer, like, maybe you are building an abstraction that is more for serving that as that middle layer between your front end and your back end. JOËL: That's really interesting that you mentioned that because I feel like the sort of traditional way that APIs are built is that they are built by the back-end team. And oftentimes, they will reflect the database schema. But you kind of mentioned with GraphQL here, sometimes it's the opposite that happens. Instead of being driven kind of from the back towards the front, it might be driven from the front towards the back where the UI team is building something that says, hey, we need these objects. We need these connections. Can you expose them to us? And then they get access to them. What has been your experience when you've been working with front ends that are backed by a GraphQL API? STEPHANIE: I think I've tended to see a GraphQL API when you do have a pretty rich client-side application with a lot of user interactions that then need to, you know, go and fetch some data. And you, like, really, you know, obviously don't want a page reload, right? So it's really interesting, actually, that you pointed out that it's, like, perhaps the front end or the UI driving the API. Because, on one hand, the flexibility is really nice. And there's a lot more freedom even in maybe, like, what the product can do or how it would look. On the other hand, what I've kind of also seen is that eventually, maybe we do just want an API that we can talk to separate from, you know, any kind of UI. And, at that point, we have to go and build a separate thing [laughs] for the same data. JOËL: So we've been talking about structuring APIs and, like, boundaries and things like that. I think my personal favorite feature of GraphQL is not the graph part but the fact that it comes with a built-in schema. And that plays really nicely with some typed technologies. Particularly, I've used Elm with some of the GraphQL libraries there, and that experience is just really nice. Where it will tell you if your front-end code is not compatible with the current API schema, and it will generate some things based off the schema. So you have this really nice feedback cycle where somebody makes a change to the API, or you want to make a change to the code, and it will tell you immediately is your front end compatible with the current state of the back end? Which is a classic problem with developing front-end code. STEPHANIE: First of all, I think it's very funny that you admitted to not preferring the graph part of GraphQL as a graph enthusiast yourself. [laughs] But I think I'm in agreement with you because, like, normally, I'm looking at it in its schema format. And that makes a lot of sense to me. But what you said was really interesting because, in some ways, we're now kind of going back to the idea of maybe boundaries blurring because the types that you are creating for GraphQL are kind of then servicing both your front end and your back end. Do you think that's accurate? JOËL: Ooh. That is an important distinction. I think you can. And I want to say that in some TypeScript implementations, you do use the types on both sides. In Elm, typically, you would not unless there's something really primitive, like a string or something like that. STEPHANIE: Okay, how does that work? JOËL: So you have some conversion layer that happens. STEPHANIE: Got it. JOËL: Honestly, I think that's my preference, and not just at the front end versus API layer but kind of all throughout. So the shape of an object in the database should not be the same shape as the object in the business logic that runs on the back end, which should not be the same shape as the object in transport, so JSON or whatever, which is also not the same shape as the object in your front-end code. Those might be similar, but each of these layers has different responsibilities, different things it's trying to optimize for. Your code should be built, in my opinion, in a way that allows all four of those layers to diverge in their interpretation of not only what maybe common entities are, so maybe a user looks slightly different at each of these layers, but maybe even what the entities are to start with. And that maybe in the database what, we don't have a full user, we've got a profile and an account, and those get merged somehow. And eventually, when it gets to the front end, all we care about is the concept of a user because that's what we need in that context. STEPHANIE: Yeah, that's really interesting because now it almost sounds like separate systems, which they kind of are, and then finding a way to make them work also as one bigger [laughs] system. I would love to ask, though, what that conversion looks like to you. Or, like, how have you implemented that? Or, like, what kind of pattern would you use for that? JOËL: So I'm going to give a shout-out to the article that I always give a shout-out to: Parse, Don't Validate. In general, yeah, you do a transformation, and potentially it can fail. Let's say I'm pulling data from a GraphQL API into an Elm app. Elm has some built-in libraries for doing those transformations and will tell you at compile time if you're incorrectly transforming the data that comes from the shape that we expect from the schema. But just because the schema comes in as, like, a flat object with certain fields or maybe it's a deeply nested chain of objects in GraphQL, it doesn't mean that it has to be that way in your Elm app. So that transformation step, you get to sort of make it whatever you want. So my general approach is, at each layer, forget what other people are sending you and just design the entities that you would like to. I've heard the term wish-driven development, which I really like. So just, you know, if you could have, like, to make your life easy, what would the entities look like? And then kind of work backwards from there to make that sort of perfect world a reality for you and make it play nicely with other systems. And, to me, that's true at every layer of the application. STEPHANIE: Interesting. So I'm also imagining that the transformation kind of has to happen both ways, right? Like, the server needs a way to transform data from the front end or some, you know, whatever, third party. But that's also true of the front end because what you're kind of saying is that these will be different. [laughs] JOËL: Right. And, in many ways, it has to be because JSON is a very limited format. But some of the fancier things that you might have access to either on the back end or on the front end might be challenging to represent natively in JSON. And a classic one would be what Elm calls a custom type. You know, they're also called tagged unions, discriminated unions, algebraic data types. These things go by a bajillion names, and it's confusing. But they're really kind of awkward and hard, almost impossible to represent in straight-up JSON because JSON is a very limited kind of transportation format. So you have to almost, like, have a rehydration step on one side and a kind of packing down step on the other when you're reading or writing from a JSON API. STEPHANIE: Have you ever heard of or played that Wikipedia game Getting to Philosophy? JOËL: I've done, I think, variations on it, the idea that you have a start and an end article, and then you have to either get through in the fewest amount of clicks, or it might be a timed thing, whoever can get to the target article first. Is that what you're referring to? STEPHANIE: Yeah. So, in this case, I'm thinking, how many clicks through Wikipedia to get to the Wiki article about philosophy? And that's how I'm thinking about how we end up getting to [laughs] talking about types and parsing, and graphs even [laughs] on the show. JOËL: It's all connected, almost as if it forms a graph of knowledge. STEPHANIE: Learning that's another common topic on the show. [laughs] I think it's great. It's a lot of interesting lenses to view, like, the same things and just digging further and further deeper into them to always, like, come away with a little more perspective. JOËL: So, in the vein of wish-driven development, if you're starting a brand-new front-end UI, what is your sort of dream approach for working with an API? STEPHANIE: Wish-driven development is very visceral to me because I often think about when I'm working with legacy code and what my wishes and dreams were for the, you know, the stack or the technology or whatever. But, at that point, I don't really have the power to change it. You know, it's like I have what I have. And that's different from being in the driver's seat of a greenfield application where you're not just wishing. You're just deciding for yourself. You get to choose. At the end of the day, though, I think, you know, you're likely starting from a simple application. And you haven't gotten to the point where you have, like, a lot of features that you have to figure out how to support and, like, complexity to manage. And, you know, you don't even know if you're going to get there. So I would probably start with REST. JOËL: So we started this episode from a very back-end perspective where we're talking about Rails, and routes, and controllers. And we kind of ended it talking from a very front-end perspective. We also contrasted kind of a more RESTful approach, versus GraphQL, versus more kind of old-school RPC-style routing. And now, I'm almost starting to wonder if there's some kind of correlation between whether someone primarily works from the back end and maybe likes, let's say, REST versus maybe somebody on the front end maybe preferring GraphQL. So I'd be happy for any of our listeners who have strong opinions preferring GraphQL, or REST, or something else; message us at hosts@bikeshed.fm and let us know. And, if you do, please let us know if you're primarily a front-end or a back-end developer because I think it would be really fun to see any connections there. STEPHANIE: Absolutely. On that note, shall we wrap up? JOËL: Let's wrap up. STEPHANIE: 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: Byeeeeeee!!!!!! 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.
Chris Toomey is back! (For an episode.) He talks about what he's been up to since handing off the reins to Joël. He's been playing around with something at Sagewell that he enjoys. At the core of it? Serializers. Primalize gem (https://github.com/jgaskins/primalize) Derek's talk on code review (https://www.youtube.com/watch?v=PJjmw9TRB7s) Inertia.js (https://inertiajs.com/) Phantom types (https://thoughtbot.com/blog/modeling-currency-in-elm-using-phantom-types) io-ts (https://gcanti.github.io/io-ts/) dry-rb (https://dry-rb.org/) parse don't validate (https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/) value objects (http://wiki.c2.com/?ValueObject) broader perspective on parsing (https://thoughtbot.com/blog/a-broader-take-on-parsing) Enumerable#tally (https://medium.com/@baweaver/ruby-2-7-enumerable-tally-a706a5fb11ea) RubyConf mini (https://www.rubyconfmini.com/) where.missing (https://boringrails.com/tips/activerecord-where-missing-associations) 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 today, I'm joined by a very special guest, former host Chris Toomey. CHRIS: Hi, Joël. Thanks for having me. JOËL: And together, we're here to share a little bit of what we've learned along the way. So, Chris, what's new in your world? CHRIS: Being on this podcast is new in my world, or everything old is new again, or something along those lines. But, yeah, thank you so much for having me back. It's a pleasure. Although it's very odd, it feels somehow so different and yet very familiar. But yeah, more generally, what's new in my world? I think this was probably in development as I was winding down my time as a host here on The Bike Shed, but I don't know that I ever got a chance to talk about it. There has been a fun sort of deep-in-the-weeds technical thing that we've been playing around with at Sagewell that I've really enjoyed. So at the core of it, we have serializers. So we take some data structures in our Ruby on Rails code base, and we need to serialize them to JSON to send them to the front end. In our case, we're using Inertia, so it's not quite a JSON API, but it's fine to think about it in that way for the context of this discussion. And what we were finding is our front end has TypeScript. So we're writing Svelte, which is using TypeScript. And so we're stating or asserting that the types like, hey, we're going to get this data in from the back end, and it's going to have this shape to it. And we found that it was really hard to keep those in sync to keep, like, what does the user mean on the front end? What's the data that we're going to get? It's going to have a full name, which is a string, except sometimes that might be null. So how do we make sure that those are keeping up to date? And then we had a growing number of serializers on the back end and determining which serializer we were actually using, and it was just...it was a mess, to put it lightly. And so we had explored a couple of different options around it, and eventually, we found a library called Primalize. So Primalize is a Ruby library. It is for writing JSON serializers. But what's really interesting about it is it has a typing layer. It's like a type system sort of thing at play. So when you define a serializer in Primalize, instead of just saying, here are the fields; there is an ID, a name, et cetera, you say, there is an ID, and it is a string. There is a name, and it is a string, or an optional string, which is the even more interesting bit. You can say array. You can say object. You can say an enum of a couple of different values. And so we looked at that, and we said, ooh, this is very interesting. Astute listeners will know that this is probably useless in a Ruby system, which doesn't have types or a compilation step or anything like that. But what's really cool about this is when you use a Primalize serializer, as you're serializing an object, if there is ever a type mismatch, so the observed type at runtime and the authored type if those ever mismatch, then you can have some sort of notification happen. So in our case, we configured it to send a warning to Sentry to say, "Hey, you said the types were this, but we're actually seeing this other thing." Most often, it will be like an Optional, a null sneaking through, a nil sneaking through on the Ruby side. But what was really interesting is as we were squinting at this, we're like, huh, so now we're going to write all this type information. What if we could somehow get that type information down to the front end? So I had a long weekend, one weekend, and I went away, and I wrote a bunch of code that took all of those serializers, ran through them, and generated the associated TypeScript interfaces. And so now we have a build step that will essentially run that and assert that we're getting the same thing in CI as we have committed to the codebase. But now we have the generated serializer types on the front end that match to the used serializer on the back end, as well as the observed run-time types. So it's a combination of a true compilation step type system on the front end and a run-time type system on the back end, which has been very, very interesting. JOËL: I have a lot of thoughts here. CHRIS: I figured you would. [laughs] JOËL: But the first thing that came to mind is, as a consultant, there's a scenario with especially smaller startups that generally concerns me, and that is the CTO goes away for a weekend and writes a lot of code... CHRIS: [laughs] JOËL: And brings in a new system on Monday, which is exactly what you're describing here. How do you feel about the fact that you've done that? CHRIS: I wasn't ready to go this deep this early on in this episode. JOËL: [laughs] CHRIS: But honestly, that is a fantastic question. It's a thing that I have been truly not struggling with but really thinking about. We're going to go on a slight aside here, but I am finding it really difficult to engage with the actual day-to-day coding work that we're doing and to still stay close to the codebase and not be in the way. There's a pattern that I've seen happen a number of times now where I pick up a piece of work that is, you know, one of the tickets at the top of the backlog. I start to work on it. I get pulled into a meeting, then another meeting, then three more meetings. And suddenly, it's three days later. I haven't completed this piece of work that was defined to be the next most important piece of work. And suddenly, I'm blocking the team. JOËL: Hmmm. CHRIS: So I actually made a rule that I'm not allowed to own critical path work, which feels weird because it's like, I want to be engaged with that work. So the counterpoint to that is I'm now trying to schedule pairing sessions with each of the developers on the team once a week. And in that time, I can work on that sort of stuff with them, and they'll then own it and run with it. So it makes sure that I'm not blocking on those sorts of things, but I'm still connected to the core work that we're doing. But the other thing that you're describing of the CTO goes away for the weekend and then comes back with a new harebrained scheme; I'm very sensitive to that, having worked on; frankly, I think the same project. I can think of a project that you and I worked on where we experienced this. JOËL: I think we're thinking of the same project. CHRIS: So yes. Like, I'm scarred by that and, frankly, a handful of experiences of that nature. So we actually, I think, have a really healthy system in place at Sagewell for capturing, documenting, prioritizing this sort of other work, this developer-centric work. So this is the feature and bug work that gets prioritized and one list over here that is owned by our product manager. Separately, the dev team gets to say, here are the pain points. Here's the stuff that keeps breaking. Here are the things that I wish was better. Here is the observability hard-to-understand bits. And so we have a couple of different systems at play and recurring meetings and sort of unique ceremonies around that, and so this work was very much a fallout of that. It was actually a recurring topic that we kept trying a couple of different stabs at, and we never quite landed it. And then I showed up this one Monday morning, and I was like, "I found a thing; what do we think?" And then, critically, from there, I made sure I paired with other folks on the team as we pushed on the implementation. And then, actually, I mentioned Primalize, the library that we're using. We have now since deprecated Primalize within the app because we kept just adding to it so much that eventually, we're like, at this point, should we own this stuff? So we ended up rewriting the core bits of Primalize to better fit our use cases. And now we've actually removed Primalize, wonderful library. I highly recommend it to anyone who has that particular use case but then the additional type generation for the front end. Plus, we have some custom types within our app, Money being the most interesting one. We decided to model Money as our first-class consideration rather than just letting JavaScript have the sole idea of a number. But yes, in a very long-winded way, yes, I'm very sensitive to the thing you described. And I hope, in this case, I did not fall prey to the CTO goes away for the weekend and made a thing. JOËL: I think what I'm hearing is the key difference here is that you got buy-in from the team around this idea before you went out and implemented it. So you're not off doing your own things disconnected from the team and then imposing it from on high. The team already agreed this is the thing we want to do, and then you just did it for them. CHRIS: Largely, yes. Although I will say there are times that each developer on the team, myself included, have sort of gone away, come back with something, and said, "Hey, here's a WIP PR exploring an area." And there was actually...I'm forgetting what the context was, but there was one that happened recently that I introduced. I was like; I had to do this. And the team talked me out of it, and I ended up closing that PR. Someone else actually made a different PR that was an alternative implementation. I was like, no, that's better; we should absolutely do that. And I think that's really healthy. That's a hard thing to maintain but making sure that everyone feels like they've got a strong voice and that we're considering all of the different ways in which we might consider the work. Most critically, you know, how does this impact users at the end of the day? That's always the primary consideration. How do we make sure we build a robust, maintainable, observable system, all those sorts of things? And primarily, this work should go in that other direction, but I also don't want to stifle that creative spark of I got this thing in my head, and I had to explore it. Like, we shouldn't then need to never mind, throw away the work, put it into a ticket. Like, for as long as we can, that more organic, intuitive process if we can retain that, I like that. Critically, with the ability for everyone to tell me, "No, this is a bad idea. Stop it. What are you doing?" And that has happened recently. I mean, they were kinder about it, but they did talk me out of a bad idea. So here we are. JOËL: So you showed up on Monday morning, not with telling everyone, "Hey, I merged this thing over the weekend." You're showing up with a work-in-progress PR. CHRIS: Yes, definitely. I mean, everything goes through a PR, and everything has discussion and conversation around it. That's a strong, strong like Derek Prior's wonderful talk Building a Culture of Code Review. I forget the exact name of it. But it's one of my favorite talks in talking about the utility of code review as a way to share ideas and all of those wonderful things. So everything goes through code review, and particularly anything that is of that more exploratory architectural space. Often we'll say any one review from anyone on the team is sufficient to merge most things but something like that, I would want to say, "Hey, can everybody take a look at this? And if anyone has any reservations, then let's talk about it more." But if I or anyone else on the team for this sort of work gets everybody approving it, then cool, we're good to go. But yeah, code review critical, critical part of the process. JOËL: I'm curious about Primalize, the gem that you mentioned. It sounds like it's some kind of validation layer between some Ruby data structure and your serializers. CHRIS: It is the serializer, but in the process of serializing, it does run-time type validation, essentially. So as it's accessing, you know, you say first name. You have a user object. You pass it in, and you say, "Serializer, there's a first name, and it's a string." It will call the first name method on that user object. And then, it will check that it has the expected type, and if it doesn't, then, in our case, it sends to Sentry. We have configured it...it's actually interesting. In development and test mode, it will raise for a type mismatch, and in production mode, it will alert Sentry so you can configure that differently. But that ends up being really nice because these type mismatches end up being very loud early on. And it's surprisingly easy to maintain and ends up telling us a lot of truths about our system because, really, what we're doing is connecting data from many different systems and flowing it in and out. And all of the inputs and outputs from our system feel very meaningful to lock down in this way. But yeah, it's been an adventure. JOËL: It seems to me there could almost be two sets of types here, the inputs coming into Primalize from your Ruby data structures and then the outputs that are the actual serialized values. And so you might expect, let's say, an integer on the Ruby side, but maybe at the serialization level, you're serializing it to a string. Do you have that sort of conversion step as part of your serializers sometimes, or is the idea that everything's already the right type on the Ruby side, and then we just, like, to JSON it at the end? CHRIS: Yep. Primalize, I think, probably works a little closer to what you're describing. They have the idea of coercions. So within Primalize, there is the concept of a timestamp; that is one of the types that is available. But a timestamp is sort of the union of a date, a time, or I think they might let through a string; I'm not sure if there is as well. But frankly, for us, that was more ambiguity than we wanted or more blurring across the lines. And in the implementation that we've now built, date and time are distinct. And critically, a string is not a valid date or time; it is a string, that's another thing. And so there's a bunch of plumbing within the way you define the serializers. There are override methods so that you can locally within the serializer say, like, oh, we need to coerce from the shape of data into this other shape of data, even little like in-line proc, so we can do it quickly. But the idea is that the data, once it has been passed to the serializer, should be up the right shape. And so when we get to the type assertion part of the library, we expect that things are in the asserted type and will warn if not. We get surprisingly few warnings, which is interesting now. This whole process has made us pay a little more intention, and it's been less arduous simultaneously than I would have expected because like this is kind of a lot of work that I'm describing. And yet it ends up being very natural when you're the developer in context, like, oh, I've been reading these docs for days. I know the shape of this JSON that I'm working with inside and out, and now I'll just write it down in the serializer. It's very easy to do in that moment, and then it captures it and enforces it in such a useful way. As an aside, as I've been looking at this, I'm like, this is just GraphQL, but inside out, I'm pretty sure. But that is a choice that we have made. We didn't want to adopt the whole GraphQL thing. But just for anyone out there who is listening and is thinking, isn't this just GraphQL but inside out? Kind of. Yes. JOËL: I think my favorite part of GraphQL is the schema, which is not really the selling point for GraphQL, you know, like the idea that you can traverse the graph and get any subset of data that you want and all that. I think I would be more than happy with a REST API that has some kind of schema built around it. And someone told me that maybe what I really just want is SOAP, and I don't know how to feel about that comment. CHRIS: You just got to have some XML, and some WSDLs, and other fun things. I've heard people say good things about SOAP. SOAP seems like a fine idea. If anything, I think a critical part of this is we don't have a JSON API. We have a very tightly coupled front end and back end, and a singular front end, frankly. And so that I think naturally...that makes the thing that I'm describing here a much more comfortable fit. If we had multiple different downstream clients that we're trying to consume from the same back end, then I think a GraphQL API or some other structured JSON schema, whatever it is type of API, and associated documentation and typing layer would be probably a better fit. But as I've said many a time on this here, Bike Shed, Inertia is one of my favorite libraries or frameworks (They're probably more of a framework.) one of my favorite technological approaches that I have ever found. And particularly in buildings Sagewell, it has allowed us to move so rapidly the idea that changes are, you know, one fell swoop changes everything within the codebase. We don't have to think about syncing deploys for the back end and the front end and how to coordinate across them. Our app is so much easier to understand by virtue of that architecture that Inertia implies. JOËL: So, if I understand correctly, you don't serialize to JSON as part of the serializers. You're serializing directly to JavaScript. CHRIS: We do serialize to JSON. At the end of the day, Inertia takes care of this on both the Rails side and the client side. There is a JSON API. Like, if you look at the network inspector, you will see XHR requests happening. But critically, we're not doing that. We're not the ones in charge of it. We're not hitting a specific endpoint. It feels as an application coder much closer to a traditional Rails app. It just happens to be that we're writing our view layer. Instead of an ERB, we're writing them in Svelte files. But otherwise, it feels almost identical to a normal traditional Rails app with controllers and the normal routing and all that kind of stuff. JOËL: One thing that's really interesting about JSON as an interchange format is that it is very restrictive. The primitives it has are even narrower than, say, the primitives that Ruby has. So you'd mentioned sending a date through. There is no JSON date. You have to serialize it to some other type, potentially an integer, potentially a string that has a format that the other side knows how it's going to interpret. And I feel like it's those sorts of richer types when we need to pass them through JSON that serialization and deserialization or parsing on the other end become really interesting. CHRIS: Yeah, I definitely agree with that. It was a struggling point for a while until we found this new approach that we're doing with the serializers in the type system. But so far, the only thing that we've done this with is Money. But on the front end, a while ago, we introduced a specific TypeScript type. So it's a phantom type, and I believe I'm getting this correct. It's a phantom type called Cents, C-E-N-T-S. So it represents...I'm going to say an integer. I know that JavaScript doesn't have integers, but logically, it represents an integer amount of cents. And critically, it is not a number, like, the lowercase number in the type system. We cannot add them together. We can't -- JOËL: I thought you were going to say, NaN. CHRIS: [laughs] It is not a number. I saw a n/a for not applicable somewhere in the application the other day. I was like, oh my God, we have a NaN? It happened? But it wasn't, it was just n/a, and I was fine. But yeah, so we have this idea of Cents within the application. We have a money input, which is a special input designed exactly for this. So to a user, it is formatted to look like you're entering dollars and cents. But under the hood, we are bidirectionally converting that to the integer amount of cents that we need. And we strictly, within the type system, those are cents. And you can't do math on Cents unless you use a special set of helper functions. You cannot generate Cents on the fly unless you use a special set of helper functions, the constructor functions. So we've been really restrictive about that, which was kind of annoying because a lot of the data coming from the server is just, you know, numbers. But now, with this type system that we've introduced on the Ruby side, we can assert and enforce that these are money.new on the Ruby side, so using the Money gem. And they come down to the front end as capital C Cents in the type system on the TypeScript side. So we're able to actually bind that together and then enforce proper usage sort of on both sides. The next step that we plan to do after that is dates and times. And those are actually almost weirder because they end up...we just have to sort of say what they are, and they will be ISO 8601 date and time strings, respectively. But we'll have functions that know this is a date string; that's a thing. It is, again, a phantom type implemented within our TypeScript type system. But we will have custom functions that deal with that and really constrain...lock ourselves down to only working with them correctly. And critically, saying that is the only date and time format that we work with; there is no other. We don't have arbitrary dates. Is this a JSON date or something else? I don't know; there are too many date syntaxes. JOËL: I like the idea of what you're doing in that it sounds like you're very much narrowing that sort of window of where in the stack the data exists in the sort of unstructured, free-floating primitives that could be misinterpreted. And so, at this point, it's almost narrowed to the point where it can't be touched by any user or developer-written code because you've pushed the boundaries on the Rails side down and then on the JavaScript side up to the point where the translation here you define translations on one side or, I guess, a parser on one side and a serializer on the other. And they guarantee that everything is good up until that point. CHRIS: Yep, with the added fun of the runtime reflection on the Ruby side. So it's an interesting thing. Like, TypeScript actually has similar things. You can say what the type is all day long, and your code will consistently conform to that asserted type. But at the end of the day, if your JSON API gets in some different data...unless you're using a library like io-ts, is one that I've looked at, which actually does parsing and returns a result object of did we parse to the thing that you wanted or did we get an error in that data structure? So we could get to that level on the client side as well. We haven't done that yet largely because we've essentially pushed that concern up to the Ruby layer. So where we're authoring the data, because we own that, we're going to do it at that level. There are a bunch of benefits of defining it there and then sort of reflecting it down. But yeah, TypeScript, you can absolutely lie to yourself, whereas Elm, a language that I know you love dearly, you cannot lie to yourself in Elm. You've got to tell the truth. It's the only option. You've got to prove it. Whereas in TypeScript, you can just kind of suggest, and TypeScript will be like, all right, cool, I'll make sure you stay honest on that, but I'm not going to make you prove it, which is an interesting sort of set of related trade-offs there. But I think we found a very comfortable resting spot for right now. Although now, we're starting to look at the edges of the Ruby system where data is coming in. So we have lots of webhooks and other external partners that we're integrating with, and they're sending us data. And that data is of varying shapes. Some will send us a payload with the word amount, and it refers to an integer amount of cents because, of course, it does. Some will send us the word amount in their payload, and it will be a floating amount of dollars. And I get a little sad on those days. But critically, our job is to make sure all of those are the same and that we never pass dollars as cents or cents as dollars because that's where things go sad. That is job number one at Sagewell in the engineering team is never get the decimal place wrong in money. JOËL: That would be a pretty terrible mistake to make. CHRIS: It would. I mean, it happens. In fintech, that problem comes up a lot. And again, the fact that...I'm honestly surprised to see situations out there where we're getting in floating point dollars. That is a surprise to me because I thought we had all agreed sort of as a community that it was integer cents but especially in a language that has integers. JavaScript, it's kind of making it up the whole time. But Ruby has integers. JSON, I guess, doesn't have integers, so I'm sort of mixing concerns here, but you get the idea. JOËL: Despite Ruby not having a static type system, I've found that generally, when I'm integrating with a third-party API, I get to the point where I want something that approximates like Elm's JSON decoders or io-ts or something like that. Because JSON is just a big blob of data that could be of any shape, and I don't really trust it because it's third-party data, and you should not trust third parties. And I find that I end up maybe cobbling something together commonly with like a bunch of usage of hash.fetch, things like that. But I feel like Ruby doesn't have a great approach to parsing and composing these validators for external data. CHRIS: Ruby as a language certainly doesn't, and the ecosystem, I would say, is rather limited in terms of the options here. We have looked a bit at the dry-rb stack of gems, so dry-validation and dry-schema, in particular, both offer potentially useful aspects. We've actually done a little bit of spiking internally around that sort of thing of, like, let's parse this incoming data instead of just coercing to hash and saying that it's got probably the shape that we want. And then similarly, I will fetch all day instead of digging because I want to be quite loud when we get it wrong. But we're already using dry-monads. So we have the idea of result types within the system. We can either succeed or fail at certain operations. And I think it's just a little further down the stack. But probably something that we will implement soon is at those external boundaries where data is coming in doing some form of parsing and validation to make sure that it conforms to unknown data structure. And then, within the app, we can do things more cleanly. That also would allow us to, like, let's push the idea that this is floating point dollars all the way out to the edge. And the minute it hits our system, we convert it into a money.new, which means that cents are properly handled. It's the same type of money or dollar, same type of currency handling as everywhere else in the app. And so pushing that to the very edges of our application is a very interesting idea. And so that could happen in the library or sort of a parsing client, I guess, is probably the best way to think about it. So I'm excited to do that at some point. JOËL: Have you read the article, Parse, Don't Validate? CHRIS: I actually posted that in some code review the other day to one of the developers on the team, and they replied, "You're just going to quietly drop one of my favorite articles of all time in code review?" [laughs] So yes, I've read it; I love it. It's a wonderful idea, definitely something that I'm intrigued by. And sort of bringing dry-monads into Ruby, on the one hand, feels like a forced fit and yet has also been one of the other, I think strongest sort of architectural decisions that we've made within the application. There's so much imperative work that we ended up having to do. Send this off to this external API, then tell this other one, then tell this other one. Put the whole thing in a transaction so that our local data properly handles it. And having dry-monads do notation, in particular, to allow us to make that manageable but fail in all the ways it needs to fail, very expressive in its failure modes, that's been great. And then parse, don't validate we don't quite do it yet. But that's one of the dreams of, like, our codebase really should do that thing. We believe in that. So let's get there soon. JOËL: And the core idea behind parse, don't validate is that instead of just having some data that you don't trust, running a check on it and passing that blob of now checked but still untrusted data down to the next person who might also want to check it. Generally, you want to pass it through some sort of filter that will, one, validate that it's correct but then actually typically convert it into some other trusted shape. In Ruby, that might be something like taking an amorphous blob of JSON and turning it into some kind of value object or something like that. And then anybody downstream that receives, let's say, money object can trust that they're dealing with a well-formed money value as opposed to an arbitrary blob of JSON, which hopefully somebody else has validated, but who knows? So I'm going to validate it again. CHRIS: You can tell that I've been out of the podcasting game for a while because I just started responding to yes; I love that blog post without describing the core premise of it. So kudos to you, Joël; you are a fantastic podcast host over there. I will say one of the things you just described is an interesting...it's been a bit of a struggle for us. We keep sort of talking through what's the architecture. How do we want to build this application? What do we care about? What are the things that really matter within this codebase, and then what is all the other stuff? And we've been good at determining the things that really matter, thinking collectively as a group, and I think coming up with some novel, useful, elegant...I'm saying too many positive adjectives for what we're doing. But I've been very happy with sort of the thing that we decide. And then there's the long-tail work of actually propagating that change throughout the rest of the application. We're, like, okay, here's how it works. Every incoming webhook, we now parse and yield a value object. That sentence that you just said a minute ago is exactly what I want. That's like a bunch of work. It's particularly a bunch of work to convert an existing codebase. It's easy to say, okay, from here forward, any new webhooks, payloads that are coming in, we're going to do in this way. But we have a lot of things in our app now that exist in this half-converted way. There was a brief period where we had three different serializer technologies at play. Just this week, I did the work of killing off the middle ground one, the Primalized-based thing, and we now have only our new hotness and then the very old. We were using Blueprinter as the serializer as the initial sort of stub. And so that still exists within the codebase in some places. But trying to figure out how to prioritize that work, the finishing out those maintenance-type conversions is a tricky one. It's never the priority. But it is really nice to have consistency in a codebase. So it's...yeah, do you have any thoughts on that? JOËL: I think going back to the article and what the meaning of parsing is, I used to always think of parsing as taking strings and turning them into something else, and I think this really broadened my perspective on the idea of parsing. And now, I think of it more as converting from a broader type to a narrower type with failures. So, for example, you could go from a string to an integer, and not all strings are valid integers. So you're narrowing the type. And if you have the string hello world, it will fail, and it will give you an error of some type. But you can have multiple layers of that. So maybe you have a string that you parse into an integer, but then, later on, you might want to parse that integer into something else that requires an integer in a range. Let's say it's a percentage. So you have a value object that is a percentage, but it's encoded in the JSON as a string. So that first pass, you parse it from a string into an integer, and then you parse that integer into a percentage object. But if it's outside the range of valid percentage numbers, then maybe you get an error there as well. So it's a thing that can happen at multiple layers. And I've now really connected it with the primitive obsession smell in code. So oftentimes, when you decide, wait, I don't want a primitive here; I want a richer type, commonly, there's going to be a parsing step that should exist to go from that primitive into the richer type. CHRIS: I like that. That was a classic Joël wildly concise summary of a deeply complex technical topic right there. JOËL: It's like I'm going to connect some ideas from functional programming and a classic object-oriented code smell and, yeah, just kind of mash it all together with a popular article. CHRIS: If only you had a diagram. Podcast is not the best medium for diagrams, but I think you could do it. You could speak one out loud, and everyone would be able to see it in their mind's eye. JOËL: So I will tell you what my diagram is for this because I've actually created it already. I imagine this as a sort of like pyramid with different layers that keep getting smaller and smaller. So the size of type is sort of the width of a layer. And so your strings are a very wide layer. Then on top of that, you have a narrower layer that might be, you know, it could be an integer, or you could even if you're parsing JSON, you first start with a string, then you parse that into a Ruby hash, not all strings are valid hashes. So that's going to be narrower. Then you might extract some values out of that hash. But if the keys aren't right, that might also fail. You're trying to pull the user out of it. And so each layer it gets a richer type, but that richer type, by virtue of being richer, is narrower. And as you're trying to move up that pyramid at every step, there is a possibility for a failure. CHRIS: Have you written a blog post about this with said diagram in it? And is that why you have that so readily at hand? [laughs] JOËL: Yes, that is the case. CHRIS: Okay. Yeah, that made sense to me. [laughs] JOËL: We'll make sure to link to it in the show notes. CHRIS: Now you have to link to Joël blog posts, whereas I used to have to link to them [chuckles] in almost every episode of The Bike Shed that I recorded. JOËL: Another thing I've been thinking about in terms of this parsing is that parsing and serializing are, in a sense, almost opposites of each other. Typically, when you're parsing, you're going from a broad type to a narrow one. And when you're serializing, you're going from a narrow type to a broader one. So you might go from a user into a hash into a string. So you're sort of going down that pyramid rather than going up. CHRIS: It is an interesting observation and one that immediately my brain is like, okay, cool. So can we reuse our serializers but just run them in reverse or? And then I try and talk myself out of that because that's a classic don't repeat yourself sort of failure mode of, like, actually, it's fine. You can repeat a little bit. So long as you can repeat and constrain, that's a fine version. But yeah, feels true, though, at the core. JOËL: I think, in some ways, if you want a single source of truth, what you want is a schema, and then you can derive serializers and parsers from that schema. CHRIS: It's interesting because you used the word derive. That has been an interesting evolution at Sagewell. The engineering team seems to be very collected around the idea of explicitness, almost the Zen of Python; explicit is better than implicit. And we are willing to write a lot of words down a lot of times and be happy with that. I think we actually made the explicit choice at one point that we will not implement an automatic camel case conversion in our serializer, even though we could; this is a knowable piece of code. But what we want is the grepability from the front end to the back end to say, like, where's this data coming from? And being able to say, like, it is this data, which is from this serializer, which comes from this object method, and being able to trace that very literally and very explicitly in the code, even though that is definitely the sort of thing that we could derive or automatically infer or have Ruby do that translation for us. And our codebase is more verbose and a little noisier. But I think overall, I've been very happy with it, and I think the team has been very happy. But it is an interesting one because I've seen plenty of teams where it is the exact opposite. Any repeated characters must be destroyed. We must write code to write the code for us. And so it's fun to be working with a team where we seem to be aligned around an approach on that front. JOËL: That example that you gave is really interesting because I feel like a common thing that happens in a serialization layer is also a form of normalization. And so, for example, you might downcase all strings as part of the serialization, definitely, like dates always get written in ISO 8601 format whenever that happens. And so, regardless of how you might have it stored on the Ruby side, by the time it gets to the JSON, it's always in a standard format. And it sounds like you're not necessarily doing that with capitalization. CHRIS: I think the distinction would be the keys and the values, so we are definitely doing normalization on the values side. So ISO 8601 date and time strings, respectively that, is the direction that we plan to go for the value. But then for the key that's associated with that, what is the name for this data, those we're choosing to be explicit and somewhat repetitive, or not even necessarily repetitive, but the idea of, like, it's first_name on the Ruby side, and it's first capital N name camel case, or it's...I forget the name. It's not quite camel case; it's a different one but lower camel, maybe. But whatever JavaScript uses, we try to bias towards that when we're going to the front end. It does get a little tricky coming back into the Ruby side. So our controllers have a bunch of places where they need to know about what I think is called lower camel case, and so we're not perfect there. But that critical distinction between sort of the names for things, and the values for things, transformations, and normalizations on the values, I'm good with that. But we've chosen to go with a much more explicit version for the names of things or the keys in JSON objects specifically. JOËL: One thing that can be interesting if you have a normalization phase in your serializer is that that can mean that your serializer and parsers are not necessarily symmetric. So you might accept malformed data into your parser and parse it correctly. But then you can't guarantee that the data that gets serialized out is going to identically match the data that got parsed in. CHRIS: Yeah, that is interesting. I'm not quite sure of the ramifications, although I feel like there are some. It almost feels like formatting Prettier and things like that where they need to hold on to whitespace in some cases and throw out in others. I'm thinking about how ASTs work. And, I don't know, there's interesting stuff, but, again, not sure of the ramifications. But actually, to flip the tables just a little bit, and that's an aggressive terminology, but we're going to roll with it. To flip the script, let's go with that, Joël; what's been up in your world? You've been hosting this wonderful show. I've listened in to a number of episodes. You're doing a fantastic job. I want to hear a little bit more of what's new in your world, Joël. JOËL: So I've been working on a project that has a lot of flaky tests, and we're trying to figure out the source of that flakiness. It's easy to just dive into, oh, I saw a flaky Test. Let me try to fix it. But we have so much flakiness that I want to go about it a little bit more systematically. And so my first step has actually been gathering data. So I've actually been able to make API requests to our CI server. And the way we figure out flakiness is looking at the commit hash that a particular test suite run has executed on. And if there's more than one CI build for a given commit hash, we know that's probably some kind of flakiness. It could be a legitimate failure that somebody assumed was flakiness, and so they just re-run CI. But the symptom that we are trying to address is the fact that we have a very high level of people re-verifying their code. And so to do that or to figure out some stats, I made a request to the API grouped by commit hash and then was able to get the stats of how many re-verifications there are and even the distribution. The classic way that you would do that is in Ruby; you would use the GroupBy function from enumerable. And then, you would transform values instead of having, like, say; each commit hash then points to all the builds, an array of builds that match that commit hash. You would then thumb those. So now you have commit hashes that point to counts of how many builds there were for that commit hash. Newer versions of Ruby introduced the tally method, which I love, which allows you to basically do all of that in one step. One thing that I found really interesting, though, is that that will then give me a hash of commit hashes that point to the number of builds that are there. If I want to get the distribution for the whole project over the course of, say, the last week, and I want to say, "How many times do people run only one CI run versus running twice in the same commit versus running three times, or four times, or five or six times?" I want to see that distribution of how many times people are rerunning their build. You're effectively doing that tally process twice. So once you have a list of all the builds, you group by hash. You count, and so you end up with that. You have the Ruby hash of commit SHAs pointing to number of times the build was run on that. And then, you again group by the number of builds for each commit SHA. And so now what you have is you'll have something like one, and then that points to an array of SHA one, SHA two, SHA three, SHA four like all the builds. And then you tally that again, or you transform values, or however, you end up doing it. And what you end up with is saying for running only once, I now have 200 builds that ran only once. For running twice in the same commit SHA, there are 15. For running three times, there are two. For running four times, there is one. And now I've got my distribution broken down by how many times it was run. It took me a while to work through all of that. But now the shortcut in my head is going to be you double tally to get distribution. CHRIS: As an aside, the whole everything you're talking about is interesting and getting to that distribution. I feel like I've tried to solve that problem on data recently and struggled with it. But particularly tally, I just want to spend a minute because tally is such a fantastic addition to the Ruby standard library. I used to have in sort of like loose muscle memory transform value is grouped by ampersand itself, transform values count, sort, reverse to H. That whole string of nonsense gets replaced by tally, and, oof, what a beautiful example of Ruby, and enumerable, and all of the wonder that you can encapsulate there. JOËL: Enumerable is one of the best parts of Ruby. I love it so much. It was one of the first things that just blew my mind about Ruby when I started. I came from a PHP, C++ background and was used to writing for loops for everything and not the nice for each loops that a lot of languages have these days. You're writing like a legit for or while loop, and you're managing the indexes yourself. And there's so much room for things to go wrong. And being introduced to each blew my mind. And I was like, this is so beautiful. I'm not dealing with indexes. I'm not dealing with the raw implementation of the array. I can just say do a thing for each element. This is amazing. And that is when I truly fell in love with Ruby. CHRIS: I want to say I came from Python, most recently before Ruby. And Python has pretty nice list comprehensions and, in fact, in some ways, features that enumerable doesn't have. But, still, coming to Ruby, I was like, oh, this enumerable; this is cool. This is something. And it's only gotten better. It still keeps growing, and the idea of custom enumerables. And yeah, there's some real neat stuff in there. JOËL: I'm going to be speaking at RubyConf Mini this fall in November, and my talk is all about Enumerators and ranges in enumerable and ways you can use those to make the APIs of the objects that you create delightful for other people to use. CHRIS: That sounds like a classic Joël talk right there that I will be happy to listen to when it comes out. A very quick related, a semi-related aside, so, tally, beautiful addition to the Ruby language. On the Rails side, there was one that I used recently, which is where.missing. Have you seen where.missing? JOËL: I have not heard of this. CHRIS: So where.missing is fantastic. Let's assume you've got two related objects, so you've got like a has many blah, so like a user has many posts. I think you can...if I'm remembering it correctly, it's User.where.missing(:posts). So it's where dot missing and then parentheses the symbol posts. And under the hood, Rails will do the whole LEFT OUTER JOIN where the count is null, et cetera. It turns into this wildly complex SQL query or understandably complex, but there's a lot going on there. And yet it compresses down so elegantly into this nice, little ActiveRecord bit. So where.missing is my new favorite addition into the Rails landscape to complement tally on the Ruby side, which I think tally is Ruby 2.7, I want to say. So it's been around for a while. And where.missing might be a Ruby 7 feature. It might be a six-something, but still, wonderful features, ever-evolving these tool sets that we use. JOËL: One of the really nice things about enumerable and family is the fact that they build on a very small amount of primitives, and so as long as you basically understand blocks, you can use enumerable and anything in there. It's not special syntax that you have to memorize. It's just regular functions and blocks. Well, Chris, thank you so much for coming back for a visit. It's been a pleasure. And it's always good to have you share the cool things that you're doing at Sagewell. CHRIS: Well, thank you so much, Joël. It's been an absolute pleasure getting to come back to this whole Bike Shed. And, again, just to add a note here, you're doing a really fantastic job with the show. It's been interesting transitioning back into listener mode for the show. Weirdly, I wasn't listening when I was a host. But now I've regained the ability to listen to The Bike Shed and really enjoy the episodes that you've been doing and the wonderful spectrum of guests that you've had on and variety of topics. So, yeah, thank you for hosting this whole Bike Shed. It's been great. JOËL: And with that, 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 hosts@bikeshed.fm via email. Thank you so much for listening to The Bike Shed, and we'll see you next week. Byeeeeeeeeeee!!!!!!!! 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.
It's Steph and Chris' last show. Steph found a game, and if you've been following the journey, all of the Test::Unit test files are now live in RSpec. JWTs really grind Chris' gears. They wrap up with things they've learned, takeaways they've had, and their proudest podcasting moments. They also thank all the folks who've helped make The Bike Shed happen. This episode is brought to you by Airbrake (https://airbrake.io/?utm_campaign=Q3_2022%3A%20Bike%20Shed%20Podcast%20Ad&utm_source=Bike%20Shed&utm_medium=website). Visit Frictionless error monitoring and performance insight for your app stack. Microservices (https://www.youtube.com/watch?v=y8OnoxKotPQ) Transcript: CHRIS: One more round of golden roads, our golden. So here we go. STEPH: Oh, one more round of golden roads. Okay, maybe that's going to get to me today. [laughs] CHRIS: [singing] Golden roads take me home to the place. STEPH: [singing] I belong. CHRIS: Yeah, there you go. Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Chris Toomey. STEPH: And I'm Steph Viccari. CHRIS: And together we're here to share a bit of what we've learned along the way, at least one more time. So with that [chuckles] as an intro, Steph, what would you say is new in your world? STEPH: Hey, Chris. Well, today is the big day. It is the day that you and I are recording our final Bike Shed episode, which we have all the feels about, and we will definitely dive into. But to ignore some of that for now, I have another small fun update I can provide about a new game that I found. So one of the things that's new in my world is I started playing a new board game with Tim; it's called Ticket to Ride. Have you heard of that? CHRIS: I have. I don't know if I've played it. I feel like it's a particularly popular one now. But I don't know if I've ever had the pleasure. STEPH: It's a very cute game, so we have the smaller version of it. For anyone that's not familiar, it's essentially a map. And then there's a bunch of spots where you can build trains and connect them, and then you get tickets. So your goal is that you're going to connect one location to another location. And then you get points and yada yada, but it's so much fun and especially the two-player version. It's like this perfect 20, maybe 30-minute game. I'll be honest; I'm not really a board game person. I always enjoy it. Once I get into it, then I'm like, this is great. I don't know why I was resistant to this. But every time someone's like, "Do you want to play a board game?" I'm like, "Not really." [laughs] I first have to get into it. But I have really enjoyed Ticket to Ride. That's been a really fun game to play. And it's been a nice way to, like, even during the day, we'll break for lunch and squeeze in a game. CHRIS: Well, I love good two-player games. They're hard to find. But when you find a good one, and it's got that easy pickup and play...I believe I'm going to now purchase this. And thank you for the tip. STEPH: Yeah, this is definitely one of those where it's easy to pick up, and then you can get the expanded board. So there's a two-player version, but then yeah, you can get one that's a map of the U.S. or a map of Europe. And I think it accommodates up to five players as the maximum, so not a huge group but definitely more than two. On a slightly more technical note, I have something that I'm very excited to share. It is a journey that you have been on with me, that everybody listening has been on this journey with me. And I'm very excited. I see you nodding your head, so I'm guessing that you're going to know where I'm headed with this. But I'm very excited to announce that all of the Test::Unit test files now live in RSpec. So that is a big win. I'm very, very excited for that to be a previous state of life and not an ongoing state of life. Because I have certainly developed too much niche knowledge around migrating these tests, and that became apparent to me when I was pairing with another developer that works with the client because they had offered...they had some time. They're like, "Hey, do you want help migrating a test file?" And I was like, "Sure." I was like, "But this is wonky enough, like, we should pair and work on this together because I just know some ins and outs. And I don't want you to have to learn a lot of the hard lessons that I've learned." And the test that we happened to pick up was very gnarly. It had a lot of mystery guests. And we spent, I think it was a good two hours. And we only migrated one of the tests, so not even a full file but one of the tests. And at the end of it, I was like, I know way too much about some of the oddities and quirkiness of this. And we got through it, but we decided that wasn't a good use of their time for them to go at this alone. So that's why I'm extra excited and relieved because I didn't want this task to carry on to someone else. So, hooray, we did it. CHRIS: Hooray. Just in time. You're Indiana Jones grabbing your hat right as you roll out and off to [laughs] be away from the project for a bit. So you stuck the landing. Well done, Steph. STEPH: Thank you. Thank you. So that's some great news. And then also, everything else in life is pretty much focused around getting ready for maternity leave. That's about to happen soon, and I am so ready. I have thoroughly enjoyed a lot of the things that I'm doing, [laughs] but goodness, being pregnant is hard. And I am very much ready for that leave. So also, a lot of the things that I'm doing right now are very focused on making sure everything's transitioned and communicated and that I just feel really good about that day of departure. That covers all the newness in my world other than the big thing that we're just not talking about yet. How about you? What's new in your world? CHRIS: Well, continuing to skirt the bigger topic that we will certainly get to in the episode, what is new in my world? I'm actually quite excited workwise right now. We have a much larger body of work that finally we got the clarity. All the pieces fell into place, and now we're sort of everybody rowing in the same direction. There's interesting, I think, really impactful code that we're writing for Sagewell right now. So that's really fantastic. We've got the whole team back together on the engineering side. And so we're, I think, in the strongest and most interesting point that I have experienced thus far. So that's all really fantastic. On a slight technical deep dive, you know what really grinds my gears? It's JWTs. JSON Web Tokens and I have never gotten along. It's never been a match made in heaven. And we have a webhook that comes from Plaid. Plaid is a vendor for connecting bank accounts and whatnot. And they have webhooks like many people do. So they can inform us when things change, lovely feature of how we build web apps these days. But often, there's a signature that says, "This is definitively from us, and you can trust us." And usually, it's some calculated signature, HMAC, or something like that. For some reason, Plaid's uses JWTs, and more than that, they use JWKs. So there's JWT which is the signature. That JWT itself is signed with a JWK. You have to fetch the JWK from their server based on the key ID in the header of the JWT. But how do you know if you can trust the JWT before you've gotten the JWK? All of this broke in a recent upgrade. We went from Heroku-20 to Heroku-22 to the new platform with Heroku, which bumped us to OpenSSL 3.0, and it turns out JWT doesn't work with it. And so that's sad. It's a no. It's going to be a no. It turns out the way that OpenSSL 3.0 works is incompatible with some of the code paths in JWT. And so I was like, wait, we just can't do this? And it's low-level cryptographic primitive stuff that I'm not comfortable messing around with. I'm not going to hop in there and roll up my sleeves. And even just getting to the point that I understood what was broken about this took like an hour and a half just to sort of like, wait, which is okay...so the JWT signs and encodes. And this will be a theme that we come back to later, but I think web development should be simpler. I think we should strive for simplicity. And this is a perfect example where I'm guessing Plaid uses JWTs and that approach to communicating security things often, but I've not seen it used much for signing webhooks. And, oof, it led to a complicated day. And it's unfixable now as far as I can tell. There is a commit on the JWT Ruby repo as of five days ago, but it doesn't build in our system. And it's not released. And it's just a mess. So yeah, engineering is complicated. I'm both wildly excited about what we're doing at Sagewell, and then today was this local minimum of like, oh, JWTs again. Again, we find ourselves battling. And you won today, but hopefully not for too long. STEPH: Oof, how did this manifest that you first noticed? So is it because a webhook suddenly stopped working, and that was like the error that rose up, and that's what helped you dive into it? CHRIS: Yeah, we have a little bit of code in the controller for where Plaid events come in. We calculate and verify the signature of the webhook to make sure that it's valid, and we reject it otherwise. And we alert ourselves via Sentry, and then we also have a Datadog scan that can show what's the status code of the response. Because these are incoming HTTP payloads or requests, and so we can see there were 200 up until this magical day when suddenly everything changed. And that was when we switched Heroku stacks. And then we can see it also in Sentry. So we're able to look at it, and we're like, why are none of the Plaid webhooks able to verify the signature anymore? That seems weird. And so then Datadog confirmed that it consistently was broken from this point in time. And then we were able to track that back. It was also pretty easy to guess because the error was "pkeys are immutable in OpenSSL 3.0," and that was the data. And I was like, oh, cool, that sounds fun. Let me go figure out what that means. STEPH: [laughs] Well, it's a nice use of Datadog. I remember in the past you were talking about adding it. And I was excited because I've never been at that point where a team has just introduced it; either a team doesn't have it, and they wish they had more insights, or they have it and don't use it. And nobody ever checks the board. So that's a nice anecdote for Datadog helping you out. Yeah, I'm not envious of your situation, friend. CHRIS: I do love the cup half full take [laughs] that you have on the overall situation, but that's nice how Datadog worked out for you. And you know what? It was. Thank you, Steph, for once again being that voice of positivity. STEPH: I appreciate that you enjoy it because there are times that when someone points it out to me that I do that, I have to be like, "I'm sorry, I'm not trying to be toxic positivity over here. [chuckles] That's just how my brain works." CHRIS: Oh, you are definitively not toxic positivity. That's a different thing. Because you ended with but also, I feel bad for you, and I'm glad that I'm not in your shoes. So you are the right level of positivity. I don't think I could have talked to you for three and a half years as co-host on a podcast if I didn't appreciate the level of positivity or the general approach that you bring to thinking about stuff. STEPH: Okay. Well, to borrow a phrase from Matt Sumner, who has been a guest on the show, cool, cool, cool, cool. I'm glad my positivity has been well calibrated. And I was about to say I'm interested to hear how this turns out for the team. [laughs] But we're in an awkward spot where I mean, you and I, we can still totally chat. But listeners won't get to hear the rest of that particular saga. I mean, you can share. I mean, you do you. I'm setting all sorts of boundaries for you right now. Okay. And now I'm just rambling, and I'm getting weird with it. Because the truth is that, you know, we won't be back. And this is our final episode together. So I think let's just go ahead and rip off the Band-Aid. Let's dive into it. Let's talk about it. Given that it's our last episode that we are recording, we thought of a couple of things that we'd like to talk about. You brought up a great idea that I'm excited to dive into. Do you want to lead us in? CHRIS: Sure. Well, if we go back all the way to Episode 172, that is the first episode that you came on as a guest. I actually continue to really love the title of that episode, which is What I Believe About Software. And it both captured that conversation really well, but also, more generally, it's actually become the tagline of the show when we do our little introduction. What do we believe about building great software? Et cetera. And I think that's been the throughline of the conversations that we've had is what remains true. What are the themes? Not necessarily the specific technologies, although we certainly talk about that. But what do we believe about building great software? And so today, I thought it would be fun for us to talk about what do we still believe about building great software? It's roughly three and a half years or so that we've been doing this. What's still true? STEPH: Oh, well, I have the first unequivocal one, the thing that I still believe about building great software, and that's you should hire thoughtbot. That's definitely the way to go. We'll help you get it done, not that I'm biased in any way. CHRIS: No. I'd say collectively between us; there's zero bias with regard to thoughtbot or any other web development shop out there. But thoughtbot is the best. STEPH: All right, perfect. So we've got the first one, the clutch one of hire thoughtbot. And then I also really like this topic. And I still think back to that first episode that I recorded with you and how much fun that was and how that really got me to start thinking about this. Because it was something that, at the time, I didn't really reflect on a lot in terms of what does it take to build great software? I was often just doing the day-to-day actions but then not really going high-level think about it. So I'm excited this is one of the topics that we're revisiting. So for the next one, this one is, I don't know, maybe it's a little cutesy, but I was trying to think of an alliteration that I enjoyed. And so this one is be an assumption assassin. So what assumptions are you making? And then how can you validate or disprove them? And that is something that I find myself doing constantly. And it always yields better work, better questions, better software, better code, better code reviews. And that's my first one is be an assumption assassin and identify what assumptions you have. And I had a really good example come up today while I was having a conversation with Joël about something that I was looking to merge. But I was a little hesitant about it because there are some oddities that I won't dig in too deeply. But essentially, there's a test that I migrated that highlights an existing concern in the code. And I was like, should I go ahead and merge this test that documents it, or should I wait to fix that concern and address it? And he brought up a good point. And he's like, "Well, we're assuming it's a bug and an issue, but it may not actually be depending on how the software is being used." And so then he was encouraging me to reevaluate that assumption that I had where I'm like, oh, this is definitely a problem to, like, I don't know, is it a problem? Let's ask somebody. CHRIS: First off, I love that as a theme, as one of the things that you still believe about software. Second, I believe you correctly said that you were looking for an alliteration, but my brain heard acronym. STEPH: [laughs] CHRIS: And so then I was like, B-A-A-A. Is it BAAA? What are you going for there? Oh, you just wanted a bunch of As. Okay, I got it now. Secondly or thirdly, I think I'm on my third now. Apparently, within Sagewell team culture, one of the things that I'm most known for is... there are two phrases: one is just to name it, and the other is to be clear. And these are the two things that I do apparently constantly so much that it's become a meme within the team. It's just like, okay, everybody's been talking. But I just want to make sure we're on the same page here. So just to be clear, or just to name it, here's what I'm seeing. But I agree; I think taking those things...what are the implicit bits? What are the assumptions? And making them more explicit. Our job as developers is just to yell at computers all the time and make them try and do human stuff. And there's so much room for lossy conversions at every point in that conversation chain. And so yeah, being very clear, getting rid of assumptions, love it. It's all great stuff. Actually, in a very related note, the first on my list is that code is for humans to read. This is one of the things that I believe most deeply and most impacts the way that I write software. Any given piece of functionality that we want to author in our code feels like 10, 20, 50, frankly, almost infinite different versions of the code that would produce nearly identical functionality. So at the end of the day, the actual symbols and strings of text that we bring together to write the code is all about other humans, other people on your team, you five months from now, you a week from now, frankly, or me. I'm going to say me, me a week from now. I want to do future me and everyone else on the team a solid and spend that extra 10% of okay, I have something that works now, but let me try and push it around and try and massage it into a shape that is a little more representative of how we're actually thinking about the code, how we talk about it as an organization. Is that the word that we use to describe that domain concept? Maybe we could change that just a little bit. Can I push more of this into the private API? What actually needs to be known here? And I think that's where I'm happiest is in those moments because that's where all of the parts of the job come together, the bit where I trick a computer into doing what I want and simultaneously making it so that that code is revisitable, clear, expressive, all of those things. So yeah, code is for humans. And that's true across every language, and framework, and domain that I have worked in. And I've only believed it more and more so over time. So yeah, that's mine. STEPH: Yeah, I love that one. That's one of the things that comes to mind when people talk about disliking code reviews. And I can imagine there are a number of reasons that people may have had a poor experience with a code review process. But at the end of the day, if you're not getting that feedback or validation from fellow humans, then how do you know that you've been successful, that you've written something that other people can follow up on? Which goes back to the assumptions in terms of like, you're assuming that you have written something that your future self or that other people are going to be able to read and maintain down the road. So yeah, I love that one. One of the other things that I still hold really true to building great software is prioritize early and often. So always be checking in to understand with your users, with your tech concerns, with data that you may have, new insights, and then just confirm that yes, you and the team are constantly working on the thing that has been prioritized and that is the most important. And also, be ready to let go. That can be really hard. I have definitely had those moments in my career where I've spent two weeks working really hard on something. And then we've realized that the thing that we were pursuing isn't that valuable, or it's something that users don't need or actually want. And so it was better to let go of it than to pursue it and ship it anyways. So that's one of my other mantras that I have adopted now is prioritize, prioritize, prioritize. CHRIS: Unsurprisingly, I agree wholeheartedly with all of that. We're still searching for that thing, that core thing that we disagree on other than Pop-Tarts and IPAs. But I don't know that today is the episode that we're actually going to find that. But yeah, prioritizing is such a critical activity. And it is this interesting collaboration point. It gets different groups together. It's this trade-off. It's this balance. And it's a way to focus on and make explicit the choices that we're making. And we're always making choices. We're always making trade-offs. And so being more explicit, being more connected and collaborative around those I believe in so, so, so much. So love that that was something on your list. Let's see, next up on my list is reduce complexity, just sort of as an adage, just always be reducing complexity. It is amazing to me in my time, particularly as a consultant, but even now, this is something that I hold very true is just it's so easy to grow a system in anticipation of future complexity or imagine that the performance concerns that we're going to run into will be so large that we must switch from Postgres and a nice, simple atomic database into a sharded, clustered Kafka queue adventure. And there are absolutely cases that make sense for that sort of thing. But at a minimum, I beg of you, anyone starting a new system, don't start with microservices. Don't start with an event queue-based system. These are wildly complex versions of what often can be done with so much simpler of an application. And this scales through to everything. What's the complexity of an API? Do we need caching in that API layer? Or can we just be a little bit inefficient for a little while and avoid the complexity and the overhead of caching? Turns out caching is a tricky thing to get right, just as an aside. And so the idea of like, oh, let's just sprinkle in a little bit of caching. It'll be easy, and then we'll get better performance, like, yeah, but did you get it right? Or did you introduce a subtle bug into your program that's going to be really hard to debug later? Because do you cache in development? Well, maybe, I'm not sure, could be. So over time, this is something that I've sort of always felt, but I've only ratcheted it up. It's only something that I've come to believe in more and to hold more firmly to. I think earlier in my career, it was something that I felt, but I would more easily be swayed by aspirational ideas of the staggering amounts of traffic that we would be getting soon or the nine different ways that the data model will expand. And so, we should code the current version in anticipation of that. And I have become somewhat the old man on his lawn yelling at the clouds like, "Nah, we don't need it yet. We can grow to that." And there's a certain category of things that are useful to try and get out in front of and don't introduce additional complexity, but they're a tiny, tiny list. And so, for most things, my stance is what's the simplest thing that we can get away with right now, that still provides a meaningful experience to our users, that doesn't compromise on security or robustness or correctness but just solves the problem we have right now? And over and over and over again, that has served me incredibly well. So yeah, keep that complexity at bay. STEPH: That is one that I've definitely struggled with. And frankly, it works in my favor, that idea of keeping things simple. Because I'm terrible when it comes to predicting the future or trying to build things in a way that I just don't have enough information to really drive the architecture or the application that I'm building. So anytime I'm trying to then stretch and reach for the future in those ways unless I really have a concrete understanding of I am building for these particular scenarios, it's really hard to do. So I very much like keeping it simple and not optimizing before you need to. And it reminds me of I think it's Mark Twain, who has a quote, "Worrying is like paying a debt that you don't owe." And that's something that comes to mind for me when also writing code and building features and software is that I tend to be someone who will worry about stuff. And I'm like, oh, is this going to be easy to extend? Is it going to be what it needs to be six months from now if we need to add more features to this and build on top of it? And I have to remind myself it's like, well, let's just wait. Let's wait till we get there and we know more. One of my other ideas that couples nicely with the one that you just shared in regards to keeping things simple and then waiting for those needs to arise is that mistakes are going to happen. They are a part of the process. As we are learning and growing and we're stretching our skills and trying things out, things are going to go wrong. We're going to introduce bugs. And to take those opportunities, that's when we start to use that feedback to then improve things like observability, like capturing logs, and how we handle error reporting or having a plan for emergencies. So maybe that's the part of worrying that can pay off is thinking through, all right, if something does break, or if something gets shipped that shouldn't, then what is our plan in how we handle that? How do we roll back? Or how do we get things back to a stable build? CHRIS: It's funny. I was actually visiting with a friend this past weekend, and we were chatting more generally about life things but the idea of worrying and anticipation and trying to prepare for every bad outcome. And there's the adage of an ounce of prevention is worth a pound of cure. But increasingly, both in life, depending on the context, and in code, I've found that I've shifted to the opposite of it's impossible to stop everything. There are going to be bugs that are going to get out there. There are going to be places where we code things incorrectly. And I would rather...I still want to try as hard as I can to get things right, to be clear. I'm not giving up on trying. But I'm all the more focused on how do we know and how do we recover when those things happen? So it's interesting that you just described exactly that, which, again, is a very human life conversation, and yet it applies to the code. STEPH: I love that rephrasing of it. Instead of the mistakes are going to happen, it's, like, how do we know, and how do we recover? I think that's perfect. I've also found that by answering the how do we know and how do we recover, that really helps you build trust with clients as well. Because again, things are going to happen, things are going to break. And the more prepared you are for that and then the better plan that you have, and then they can watch how you execute that plan, and it's going to establish a lot of deep trust with other engineers and also the team that you're working with, that you have been thoughtful and that you have ideas on how are we going to address this? Instead of waiting for that moment to happen. That's going to happen too. You're going to make decisions in the heat of the moment. But I have found that to be a really useful way to establish yourself with a team in terms of I care about this team and these processes and this application. So how do we handle the bad times, not just the good times? I do want to circle back because you alluded to the fact that you and I, we've tried to find things that we disagree on. And so far, Pop-Tarts and beer have been the two things that we disagree on. But I do have a question for you that maybe I will disagree with you on. But I need to know some more about it first. You have alluded to there's the Brussels snack, (Oh, I'm going to get this wrong.) Brussels sprout snack hour or working lunch, something combination of those words. [laughs] And it's the working lunch that has stuck out to me, and I've wanted to ask you about it. So here I am. I'm asking you about it. What's a working lunch? What's the Brussels snack happy hour, snackariffic working lunch look like? CHRIS: This is fantastic. I love that you waited until the last episode that this was rolling around in the back of your head. And you're like, are you making the team work through lunch? And now, on this final episode, we get to address the controversy that has been brewing in the back of your head. Spoiler alert, no, this is just ridiculous nomenclature. These are two meetings that we have that are more like, let's get the dev team together and talk about stuff that's in our platform sort of developer experience. Or stuff in observability often is talked about in this context because it doesn't quite impact users, but it's how we think about the work. And so there are two different meetings that alternate every other week. So every Friday afternoon, we do this, but it's one of two meetings depending on the day. So there's a crispy Brussels snack hour that was the first one that was named, which was named purely for nonsense reasons because we don't have anything else that's named nonsensically in our organization. And so I was like, oh when we name this meeting, we should make it nonsense because we don't have any other...We don't have, you know, an SOA microservices fleet with Barbie doll and Galactus and all of the other wonderful names. Those are references to the greatest video ever about microservices; if you've not seen it, that will be in the show notes. It's required reading. But anyway, we don't have that. And so we thought, let's be funny with the name of this. So the crispy Brussels snack hour is one, and the crispy Brussels we wanted something that was...the first one is a planning meeting. The second is like, let's actually sort of ensemble program. Let's get the four of us together, and we'll work on some of the stuff that we're talking about here but as a group. And so I wanted the idea of we're working, and so I was like, oh, this will be the crispy Brussels work lunch. But it's purely a name. It's the same time slot. It's 3:00 o'clock on a Friday afternoon. [laughs] So it is not at all us working through lunch. I don't think we should work through lunch. I'm concerned that you thought that for a while, and you were just like, I'm a little worried, but I'm not going to bring it up. But I'm glad we got to cover this before we wrapped up this whole Bike Shed co-hosting adventure together. STEPH: I feel relieved and also a little robbed of an opportunity for us to have something that we disagree on because I thought this might be a thing. [laughs] CHRIS: We can continue searching for that thing. But maybe it's okay that we agreed on most stuff for the run [laughs] of this fun, little show that we did together. STEPH: Yeah, that's gone on quite a time. We've got like three years together that we have managed to really only find two, I mean, very important of course, two things. But yeah, it's been pretty limited to those two areas. And each time that you'd mentioned the work lunch, I was like, huh, I need to ask about that because I have feelings about it. But then, you always would dive into very interesting stories of things that came out of it, and I quickly forgot about it. So this feels good. This feels like very good important closure. I'm glad that this finally surfaced. But circling back, since I took us on a detour for a little bit, what are some other things that you still hold deeply about building great software? CHRIS: I've really got one last thing on the list. It's interesting, there's not a ton technically in this list, which I think represents broadly how I feel about software, and I think how you feel about software. It's like, it's actually mostly about how the people interact at the end of the day. And you can program in any language or framework, and you can get the job done. We certainly have our preferences and things that we enjoy. But the last one really rounds us out, which is think about the users. I always want to be anchoring the conversations that we're having, the approach that we're taking to building the software in what do the users think? Who are our users? What do we know about them? What do they care about? How are they using this technology? How is it impacting their lives? We've talked a number of times about potentially actually watching the sales demo as an engineering team, trying to understand what's the messaging that we're putting out into the world for this piece of software that we're building? Or write along with customer support and understand what are the pain points that people are hitting? And really, like, real humans, what are they experiencing? Potentially with a name attached. And that just changes the way that you think about the software. There's also even the lower-level version of it. As we're building classes or modules, what are the public facets of that, and what are the private API? What's the stuff that we're hiding away? And what's the shape that we are exposing to the outside world for varying definitions of outside? And how can we just bring in a little bit of empathy to try and think about, again, in the case of like the API for a class, it's probably you on the other side of it, but it's future you in a slightly different mindset with a little bit less information and context on the current problem that you're working on. And so, how can we make things easier for ourselves in the code, for our users at the end of the day? How can we deliver real value that is not mired in the minutiae of technical complexity and whatnot but really is trying to help people live better lives? That's a little too fancy as I say it out loud. But it is kind of the core of what I believe, so I'm not going to take it back. STEPH: I love how you've expanded users where more traditionally, it's people that are then using the software. But then you've expanded it to include developers because that is something that is often on my mind and something that I just agree with wholeheartedly in terms of when you're writing software; as you mentioned before, software is for people. And so we want to include others. And it does improve people's lives. People show up to work every day, and if you've been thoughtful if your past you has been thoughtful, it's either going to give you your future self a better day, or it's going to give other people a better day. So I think that's a very fair statement, improving lives by being thoughtful in regards to focusing on the users, people consuming software, and working in the codebase. CHRIS: I know we've talked about this before, but I was having a conversation with one of the developers on the team at Sagewell just last week, and they were mentioning how they really loved working on admin features. And I was like, oh, that's interesting. Let's talk more about that. And it was really it's that same thing that I think you and I have discussed of like there's that immediacy. There's that connection. These are actually colleagues, but you can build software to make their day better. You can understand in detail what the pain points are. What's the workflow that as you watch it, you're like, oh, I could put a button up in the corner of the screen that would automate almost all of this and your day would be that much faster? Oh, let me do that. That's exciting. And so I love that as another variation of it, like, yeah, there's for other developers. There's also for the admin team or other users in the organization of the software. There are so many different versions of users, but I think I think we build a better thing if we think about them more. STEPH: I have definitely worked with teams where I can tell that certain people are demoralized, and it comes down to they feel frustrated and often disconnected from the people that they are building for. And so then you really feel isolated. I'm pushing code around, but I don't really see the benefit or the purpose of it. And I think that's very hard for developers who typically want to build something that's going to be useful and not feel like it's just going to be thrown away. So connecting your team to those users, I certainly understand. Getting to build something for your colleagues and then they get to say how much they like it is an incredible, rewarding experience. You also touched on something that I really appreciate, where you highlighted that a lot of the technical decisions that we make are important, but they're not at the center of the things that we believe when it comes to building great software. And that's something that I will often reflect on. Like, as we were thinking through these particular ideas that we still hold true today, how mine are more people and process-focused and rarely deep in the technical weeds. And there are times that I think, well, shouldn't there be something that's more technical, something that's very concrete? Yes, you should build your code this way or build your application or use a specific technology. But after all the projects and teams that I've been a part of, that's just usually not the most important part. And so I appreciate that you highlighted that because sometimes I have to remind myself that, yes, those things can be challenging, but it's often with people and process. That's where the heart of great software lies. CHRIS: That's a fantastic phrase, I think, that really encapsulates all of the conversations that we're having here. 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 you cut your debugging time in half. So why do developers love Airbrake? Well, 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 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 enables 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 and includes 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. So head on over to airbrake.io/try/bikeshed to create your FREE developer account today! CHRIS: Actually shifting gears a little bit, so we've just talked about what we still believe about building great software. I'm intrigued. We've been chatting for a number of years here on this microphone, these microphones. We have separate ones because we're in different states. But I'm interested; what have we changed our minds about? What have you changed your mind about, Steph? I got a couple of ideas, but I'm intrigued to hear yours. STEPH: Nothing. I've never been wrong. I've stuck to everything that I've ever thought. CHRIS: That must be boring. STEPH: [laughs] Yeah, that's totally not true there. There are definitely things that I've changed my mind about. One of the things that I've changed my mind about is that people who know the most will ask the fewest questions. That's something that I used to consider the trademark of someone who is a more experienced senior developer in terms of you really know what you're doing. And so you typically don't ask for help or need help very often. And so, I'm going way back in terms of things that I have changed my mind about. But I have definitely changed my mind where people who know the most are actually the ones that do a really great job of constantly asking questions and asking for feedback. And I think that is still a misconception that people still carry forward. The idea that if you're asking a lot of questions or asking for help that you are not as skilled in your work, and I view it as quite the opposite, that you are very good at what you do and that you know precisely the value of your time. And then also reaching out to others for help, and then also just getting validation on things that you may have concerns around. So that's one I've changed my mind on is that I think the more experienced you are, the more questions you tend to ask. CHRIS: Oh, I love that one. It's a behavior that I know...I think we've talked about this before. But as consultants, we try and model it just the like; it's totally fine to ask questions. And because we often come in with less context, it makes sense for us to be asking questions, but I will definitely intentionally lean into it in those contexts to be like, everybody keeps throwing around this acronym. I don't actually know what that is. Let me raise my hand. And my favorite moment is when people disagree on what the acronym or what the particular word or what the particular project is. Like, I ask the question, and people are like, "Oh, it's this," and someone across the room is like, "Wait, that's what it means? I thought it was this totally other thing." I'm like, cool, glad that we sorted that out. Glad that we got that one up in the air. But I actually remember many, many, many years ago, at this point, there was a video series of...PeepCode was the company, and there was the Play by Play series. And so there were particular prominent developers, particularly in the Ruby community. And they would come and sort of be interviewed and pair program. And it was amazing getting to watch these big names that you had heard of, like Yehuda Katz is the one that stands out in my mind. He was one of the authors of merb, which was a framework that was merged with Rails, I want to say around the 3.0 time. And just an absolute, very big name in this world and someone that I looked up to and respected. And watching this video, they had to Google for particular API signatures and Rails methods. They were like, "Oh, how does that work? Is it link to and then you pass the name?" I forget what it was specifically. But it was just this very human normalizing moment of this person who has demonstrably done incredible work in our community and produced very complex software still needs to Google for the order of arguments to a particular method within Rails. I was like, oh, okay, that's good to know. And with complete humility in the moment, I was just like, yeah, this is normal. Like, it's impossible to hold all of that in your head. And seeing that early on shook me off the idea that that's the thing to do is just memorize everything. It's like no, no, get good at asking the questions. Get good at debugging. Get good, yeah, asking questions. It's a core skill rather than a thing that you grow out of. But I definitely shared early on I was like, not allowed to ask questions, that'll be scary. STEPH: I love that example. Because counterintuitively, to me, it demonstrates confidence when someone can say, "Oh, I don't remember how this works," or "Let me go look it up." And so I just very much appreciate when I see someone demonstrating that level of confidence of let's keep going. Let's keep making progress. I'm going to ask for help because that is totally fine, and we are in a safe space. Or I'm going to create a safe space for us to do that. One of my favorite versions of this where you shared like if you ask about an acronym and then people disagree, one of my favorite versions is to ask about a particular area of the codebase and be like, what would you say this code is doing here? What do you think users do here? Like, what is the purpose? What's the point of this? [chuckles] And then having people be able to say, "Oh, yeah, this definitively does this thing." Or people are like, "You know, I'm not sure. I don't even know if that code is getting run." That's one of my favorite outcomes of asking questions. How about you? What's something you've changed your mind about? CHRIS: I made a list of a couple of things like remote is on there. I didn't know if I'd like remote. I wanted to try it for a while. Tried it, turns out I like it a lot. It's complex. You got to manage it, whatever. But that I think everybody's talked about that a bunch. I think probably the most interesting one is deadlines. Initially, in my career, I didn't really feel anything about them. And then I experienced the badness of deadlines. Deadlines are bad. Deadlines are things that come down from on high and then you fail to hit them, and then you're sad. And maybe along the way, you're very stressed and work long hours to try and get there. But they're perhaps arbitrary. And what do they even mean? And also, we have this fixed scope, and they're just bad. And then there was a period of my time where, like, deadlines are bad. The only thing that we do is we show up, and we make the software as quickly as we can. But in reality, there are times that we need that constraint. And in fact, I have found a ton of value in deadlines when used intentionally. So we can draw a line in the sand, and we can say, at this point in time, we will have a version of the software. We have a marketing campaign that we need to align with this. So we got to have something at that point. And critically, if you're going to have a deadline, you've now fixed a point in time. You need to flex other things. And critically, I think the thing to flex is the scope. So we need to have team management. We have user accounts right now, but now we need to organize them into teams. That is like a category of functionality. It's not a singular feature. And so yeah, we can ship teams in the next quarter. What exactly that means is up in the air. And as long as we're able to have conversations essentially on a day-to-day at least weekly cadence as to what will make it in by that deadline and what won't, and we're able to have sometimes the hardest conversations but the very necessary conversations of the trade-offs that we have to make as we're building that software, then I find deadlines are absolutely fantastic tools for focusing and for actually reducing scope but in a really useful way. And getting something out there in the hands of users so that you start to get real feedback so that you start to learn, is this useful? What are the ways that people are using this? What should we lean into and do more of? What maybe should we roll back, actually? So yeah, deadlines. First, I didn't know them, then I feared them. Now I love them but only under the right circumstances. It's a double-edged sword, definitely. STEPH: I, too, have felt the terribleness of deadlines and railed against them pretty hard because I had gone through a negative experience with them but have also shifted my feelings about them where they can be incredibly useful. So I really liked that's one of the things that you've changed your mind about. It also reminds me of one of the other things...I'm going to circle back for a moment to one of the things that I believe about creating great software is to not wait for perfection, and deadlines are a really good tool that helps you not wait for perfection. Because I have also seen teams really struggle or sometimes fail because they waited until there was something perfect to present, and then you realize that you've built the wrong thing. So I do want to transition and talk a bit about the show because it's our last episode, and we should talk about it, and the fun adventures that we've had and some of the things that we've learned or things that we're feeling in the moment. So given that it's been a wonderful three years for me, it's been four years for you since you've been a host on the show. How are you feeling? CHRIS: I'm feeling a bunch of different things sort of all at once. I am definitely going to miss this immensely. Particularly, I loved when I started, and I got to interview a bunch of thoughtboters and other people from the community. But frankly, three-plus years of getting to chat with you has been just such a delight. There's been an ease to it. We kind of just show up and talk about what we're doing. And yet there are these themes that have run through it. And it has definitely helped me hone and shape my thinking and my ability to communicate about what I'm thinking. I've learned that you have a literal superpower to remember the last thing that you were talking about. Listeners, you may not know this, but we are not quite the put-together folks that we may sound like in these recordings. We have a wonderful editor, Mandy Moore, who makes us sound so much better than we are. But we'll often pause and stop and then discuss what we want to talk about next. And Steph always knows the exact phrase that she or I left off on. And it has been so valuable to the team. But really, it's been just such a pleasure getting to have these conversations. It's also been something that has just gently been in the back of my mind at all times. And so, I'm observing the work in any given week as I'm doing it. It's almost like meditation in a certain way, whereas I'm working on something, like, oh, this is actually really cool. I want to take a note about this and talk about it on The Bike Shed with Steph. And having this outlet, having this platform to be able to have those conversations and knowing that there are people out there is fantastic, although it's very weird because really, every one of these recordings is just you and I on a video call. And so there is an audience, I'm pretty sure. I think people listen to the show; I don't know, occasionally they write in, so it seems like they do. But at the end of the day, this really just feels like a conversation with a friend, and that has been so valuable to have. And yeah, I'm definitely going to miss that. It's been a wonderful run, you know, four years is a long time. It's about as long as I've done most things in my career. And so I'm very happy with what we have done here. And there's a trite saying that isn't...yeah, whatever; I'll just say it, which is, "Don't be sad that it's over. Be glad that it happened." And I guess I'm still going to be sad that it's over. But I am so glad that I got the opportunity to do this, that you joined in this adventure and that we got to chat each week. It's been really delightful. STEPH: I really liked how you refer to this as being a meditative state. And that is something that I have certainly picked up from you and thoroughly enjoyed that I have this space that I get to show up and bring these ideas and topics and then get to talk them out with you. And that has been such a nice way to either end the week or start a week. I mean, it doesn't matter. Anytime that we record, it's this very nice moment of the week where we get to come together and talk through some of the difficulties and share our stories. And that's been one of my favorite moments is because you and I get to show up and share everything that's going on. But then when someone writes into the show or if they send a tweet or something and they share their story or their version of something that happened, or if they said that we made them laugh, that was one of my favorite accomplishments is the idea that something that we have done was silly enough or fun enough that it has brought them joy and made them laugh. So I, too, I'm very, very much going to miss this. It has been a wonderful adventure. And I thank you for encouraging me to come on this adventure because I was quite nervous in the beginning. And this has definitely been an aspect of my life that started out as something that was very challenging and stretching my limits, and now it has become this very fun aspect and something that I get to show up and do and then get to share with everyone. And I do feel very proud of it, very much in part to Thom Obarski, who was our initial producer and helped us have that safe space to chat about things. And now Mandy, who keeps the show running smoothly and helps us sound our best week to week. So it's been a wonderful adventure. This is going to be hard to let go. And I think it's going to hit me most. Like, this was one of those things as we're talking about it, it's, like, I'll see you next week. This will be fine. But I think it's going to hit me when there's something that I want to talk about where I'm like, oh, this would be great to talk about, and I'll add it to The Bike Shed Trello board. And I'll be like, oh yeah, that's not a thing anymore, at least not quite in the same way that it was. CHRIS: So what I'm taking away from this is that you're immediately going to delete my phone number the minute we hang up this call and stop recording. [laughs] STEPH: Oh yeah. I preemptively deleted. So that's already done. Friendship is over at this point. CHRIS: That's smart. Yeah, because you might forget otherwise in the heat of the moment as we're wrapping this whole thing up. STEPH: [laughs] CHRIS: But actually, on that note, in a slightly more serious vein, again, there's this weird aspect where the audience is out there. But we're very sort of disconnected, particularly at the moment in time where we're recording. But it has been so wonderful getting various notes from listeners, listener questions, but also just commentary and the occasional thanks and focusing; oh, you pointed me in the direction, or you helped me think through a complicated piece of work or process a problem that we were having. And so that has been just so, so rewarding. And one of the facets of this that has been so interesting to me is being able to connect to people and basically put out there what we believe about software, and for the folks that resonate with it and be able to have that connection instantly. And meeting people, and they're like, "Oh, I've listened to The Bike Shed. I like all these things." I'm like, oh, cool, we get to skip way further into the conversation because I've already said a bunch, and you say you like that thing. So, cool, we're halfway through our introductory chat. And I know that we agree about a bunch of things, and that's really wonderful. And frankly, I'm going to miss that immensely. So for anyone out there who's found something valuable in this, who's enjoyed listening week to week, or perhaps even back to Upcase for things, I would love to hear from you. I'd love to connect to folks. Send me an email, Twitter. I'm on all the places. I'm Chris Toomey in various spots or ctoomey.com on the internet. Chris Toomey on GitHub. I'm findable, I think. Chris Toomey developer will probably get you there. But I would really love to hear from folks, to connect to folks, you know, someday down the road; I think I'll be hiring again. And that'll be fun. I would love to work with some of the folks that have listened to this show or meet you at a conference, or if I happen to be traveling to a city or you're traveling to Boston. Really for me, so much of what this show is about is connecting with people around how we think about building great software. And so, I would love to continue that forward into the future. So yeah, say hi, if you're interested. STEPH: I agree. That's been one of the most fun aspects of being co-host of the show. And I'll be honest, you are welcome to contact me, but I am going to be off-grid for probably six months. [laughs] So just know that there will be a bit of a delay before you hear back from me. But I would definitely love to hear from you. I also want to say a very heartfelt thanks to a couple of people, just folks that have made this journey incredible and have made it so much fun. One, in particular, is everyone at thoughtbot for their continuous stream of knowledge. I mean, frankly, my software opinions wouldn't be half as interesting if it wasn't for everyone at thoughtbot constantly sharing their knowledge and being a source of inspiration. So I deeply appreciate everyone that has contributed to topics and ideas and just constantly churning out blog posts because those are phenomenal. And I also want to give a shout-out to my husband, Tim, because he has listened to The Bike Shed for many years and even helped out with a number of show notes when that was something that you and I used to do before Mandy made our life so much easier and took that over for us. And has intervened a number of times when Utah mid-recording would decide it's time to play. So I want to give a very special thank you to him because he has been a very big supporter of the show and frankly helped me manage through a lot of the recordings for when I had an 80-pound dog that was demanding my attention. CHRIS: I think continuing on the note of thanks; similarly, I'm so grateful to thoughtbot as an organization for everything that is represented in my career. It's a decade-plus that I have been following and then listening to the podcasts and then joining the organization, and then getting so many wonderful opportunities to learn about this thing called web development. And then, even after I left the organization, I was able to stay on here on The Bike Shed and hang out and still chat with you, Steph, which has been really wonderful. So thank you, thoughtbot, so much. Thank you to Joël Quenneville, who will be the continuing host of the show. This show is not going anywhere. And, Steph, you and I aren't really going anywhere, but we won't be around anymore. But we are leaving it in the very, very capable hands of Joël, and I'm super excited to hear the direction that he takes it and Joel's incredibly thoughtful and nuanced approach to thinking about programming and communicating. So I think that will be really wonderful. And lastly, I definitely want to thank Derek Prior and Sage Griffin, the two original hosts of this show, who really produced something wonderful, and for many years, I think it was about four years that they hosted together. I was an avid listener despite actually working at the company the whole time and really loved the thing that they produced and was so grateful that they entrusted me with continuing it forward. And hopefully myself and then with the help of you along the way, we've...I think we've done an okay job, but now it is time to pass the torch or the green lantern. That's the adage I've been going with. Gotta pass the lantern, pass the mantle on to the next one. So, Joël, it's going to be in your hands now. STEPH: Yeah, I'm so looking forward to future episodes with Joël Quenneville. They are going to be fabulous. So I've been thinking in terms of this being our finale episode and then a fun ending for it, so there's a thing called the 21-gun salute, which is the military honor that's performed by firing cannons or artillery. Not to be confused with the three-volley salute, which I definitely confused earlier that is reserved and used at funerals, which this is not. So using the 21-gun salute, I was like, hmm, it is The Bike Shed, and we have this cute ring ring that goes. So I think for our finale, we should have a 21-bell salute as we exit the shed and right off into the sunset. CHRIS: I love it. I couldn't imagine a more perfect send-off. So with that, what do you think? Should we wrap up? STEPH: Yes, but I have one more silly thing to add. I've thought of a new software idiom that I'm excited about. And so, this may be my final send-off into glory that I'd like to share with you. And I think that we should make like a shard and split. CHRIS: [laughs] I so appreciate that in this moment, this final moment that we have together, you choose to go with a punny joke. It is so on brand for the show. It is absolutely perfect. And I think with that note, shall we wrap up? STEPH: Let's wrap up. CHRIS: The show notes for this episode can be found at bikeshed.fm. STEPH: This show is produced and edited by Mandy Moore. CHRIS: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or even a review on 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 hosts@bikeshed.fm via email. CHRIS: Thanks so much for listening to The Bike Shed, and we'll see you next week. ALL: Byeeeeeeeeee!!!!!!!! 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.
Steph has a baby update and thoughts on movies, plus a question for Chris related to migrating Test Unit tests to RSpec. Chris watched a video from Google I/O where Chrome devs talked about a new feature called Page Transitions. He's also been working with a tool called Customer.io, an omnichannel communication whiz-bang adventure! Page transitions Overview (https://youtu.be/JCJUPJ_zDQ4) Using yield_self for composable ActiveRecord relations (https://thoughtbot.com/blog/using-yieldself-for-composable-activerecord-relations) A Case for Query Objects in Rails (https://thoughtbot.com/blog/a-case-for-query-objects-in-rails) Customer.io (https://customer.io/) Turning the database inside-out with Apache Samza | Confluent (https://www.confluent.io/blog/turning-the-database-inside-out-with-apache-samza/) Datomic (https://www.datomic.com/) About CRDTs • Conflict-free Replicated Data Types (https://crdt.tech/) Apache Kafka (https://kafka.apache.org/) Resilient Management | A book for new managers in tech (https://resilient-management.com/) Mixpanel: Product Analytics for Mobile, Web, & More (https://mixpanel.com/) Become a Sponsor (https://thoughtbot.com/sponsorship) of The Bike Shed! Transcript: CHRIS: Golden roads are golden. Okay, everybody's got golden roads. You have golden roads, yes? That is what we're -- STEPH: Oh, I have golden roads, yes. [laughter] CHRIS: You might should inform that you've got golden roads, yeah. STEPH: Yeah, I don't know if I say might should as much but might could. I have been called out for that one a lot; I might could do that. CHRIS: [laughs] STEPH: That one just feels so natural to me than normal. Anytime someone calls it out, I'm like, yeah, what about it? [laughter] CHRIS: Do you want to fight? STEPH: Yeah, are we going to fight? CHRIS: I might could fight you. STEPH: I might could. I might should. [laughter] CHRIS: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Chris Toomey. STEPH: And I'm Steph Viccari. CHRIS: And together, we're here to share a bit of what we've learned along the way. So, Steph, what's new in your world? STEPH: Hey, Chris. I have a couple of fun updates. I have a baby Viccari update, so little baby weighs about two pounds now, two pounds. I'm 25 weeks along. So not that I actually know the exact weight, I'm using all those apps that estimate based on how far along you are, so around two pounds, which is novel. Oh, and then the other thing I'm excited to tell you about...I'm not sure how I should feel that I just got more excited about this other thing. I'm very excited about baby Viccari. But the other thing is there's a new Jurassic Park movie coming out, and I'm very excited. I think it's June 10th is when it comes out. And given how much we have sung that theme song to each other and make references to what a clever girl, I needed to share that with you. Maybe you already know, maybe you're already in the loop, but if you don't, it's coming. CHRIS: Yeah, the internet likes to yell things like that. Have you watched all of the most recent ones? There are like two, and I think this will be the third in the revisiting or whatever, the Jurassic World version or something like that. But have you watched the others? STEPH: I haven't seen all of them. So I've, of course, seen the first one. I saw the one that Chris Pratt was in, and now he's in the latest one. But I think I've missed...maybe there's like two in the middle there. I have not watched those. CHRIS: There are three in the original trilogy, and then there are three now in the new trilogy, which now it's ending, and they got everybody. STEPH: Oh, I'm behind. CHRIS: They got people from the first one, and they got the people from the second trilogy. They just got everybody, and that's exciting. You know, it's that thing where they tap into nostalgia, and they take advantage of us via it. But I'm fine. I'm here for it. STEPH: I'm here for it, especially for Jurassic Park. But then there's also a new Top Gun movie coming out, which, I'll be honest, I'm totally going to watch. But I really didn't remember the first one. I don't know that I've really ever watched the first Top Gun. So Tim, my partner, and I watched that recently, and it's such a bad movie. I'm going to say it; [laughs] it's a bad movie. CHRIS: I mean, I don't want to disagree, but the volleyball scene, come on, come on, the volleyball scene. [laughter] STEPH: I mean, I totally had a good time watching the movie. But the one part that I finally kept complaining about is because every time they showed the lead female character, I can't think of her character name or the actress's name, but they kept playing that song, Take My Breath Away. And I was like, can we just get past the song? [laughs] Because if you go back and watch that movie, I swear they play it like six different times. It was a lot. It was too much. So I moved it into bad movie category but bad movie totally worth watching, whatever category that is. CHRIS: Now I kind of want to revisit it. I feel like the drinking game writes itself. But at a minimum, anytime Take My Breath Away plays, yeah. Well, all right, good to know. [laughs] STEPH: Well, if you do that, let me know how many shots or beers you drink because I think it will be a fair amount. I think it will be more than five. CHRIS: Yeah, it involves a delicate calibration to get that right. I don't think it's the sort of thing you just freehand. It writes itself but also, you want someone who's tried it before you so that you're not like, oh no, it's every time they show a jet. That was too many. You can't drink that much while watching this movie. STEPH: Yeah, that would be death by Top Gun. CHRIS: But not the normal way, the different, indirect death by Top Gun. STEPH: I don't know what the normal way is. [laughs] CHRIS: Like getting shot down by a Top Gun pilot. [laughter] STEPH: Yeah, that makes sense. [laughs] CHRIS: You know, the dogfighting in the plane. STEPH: The actual, yeah, going to war away. Just sitting on your couch and you drink too much poison away, yeah, that one. All right, that got weird. Moving on, [laughs] there's a new Jurassic Park movie. We're going to land on that note. And in the more technical world, I've got a couple of things on my mind. One of them is I have a question for you. I'm very excited to run this by you because I could use a friend in helping me decide what to do. So I am still on that journey where I am migrating Test::Unit test over to RSpec. And as I'm going through, it's going pretty well, but it's a little complicated because some of the Test::Unit tests have different setup than, say, the RSpec do. They might run different scripts beforehand where they're loading data. That's perhaps a different topic, but that's happening. And so that has changed a few things. But then overall, I've just been really just porting everything over, like, hey, if it exists in the Test::Unit, let's just bring it to RSpec, and then I'm going to change these asserts to expects and really not make any changes from there. But as I'm doing that, I'm seeing areas that I want to improve and things that I want to clear up, even if it's just extracting a variable name. Or, as I'm moving some of these over in Test::Unit, it's not clear to me exactly what the test is about. Like, it looks more like a method name in the way that the test is being described, but the actual behavior isn't clear to me as if I were writing this in RSpec, I think it would have more of a clear description. Maybe that's not specific to the actual testing framework. That might just be how these tests are set up. But I'm at that point where I'm questioning should I keep going in terms of where I am just copying everything over from Test::Unit and then moving it over to RSpec? Because ultimately, that is the goal, to migrate over. Or should I also include some time to then go back and clean up and try to add some clarity, maybe extract some variable names, see if I can reduce some lets, maybe even reduce some of the test helpers that I'm bringing over? How much cleanup should be involved, zero, lots? I don't know. I don't know what that...[laughs] I'm sure there's a middle ground in there somewhere. But I'm having trouble discerning for myself what's the right amount because this feels like one of those areas where if I don't do any cleanup, I'm not coming back to it, like, that's just the truth. So it's either now, or I have no idea when and maybe never. CHRIS: I'll be honest, the first thing that came to mind in this most recent time that you mentioned this is, did we consider just deleting these tests entirely? Is that on...like, there are very few of them, right? Like, are they even providing enough value? So that was question one, which let me pause to see what your thoughts there were. [chuckles] STEPH: I don't know if we specifically talked about that on the mic, but yes, that has been considered. And the team that owns those tests has said, "No, please don't delete them. We do get value from them." So we can port them over to RSpec, but we don't have time to port them over to RSpec. So we just need to keep letting them go on. But yet, not porting them conflicts with my goal of then trying to speed up CI. And so it'd be nice to collapse these Test::Unit tests over to RSpec because then that would bring our CI build down by several meaningful minutes. And also, it would reduce some of the complexity in the CI setup. CHRIS: Gotcha. Okay, so now, having set that aside, I always ask the first question of like, can you just put Derek Prior's phone number on the webpage and call it an app? Is that the MVP of this app? No? Okay, all right, we have to build more. But yeah, I think to answer it and in a general way of trying to answer a broader set of questions here... I think this falls into a category of like if you find yourself having to move around some code, if that code is just comfortably running and the main thing you need to do is just to get it ported over to RSpec, I would probably do as little other work as possible. With the one consideration that if you find yourself needing to deeply load up the context of these tests like actually understand them in order to do the porting, then I would probably take advantage of that context because it's hard to get your head into a given piece of code, test or otherwise. And so if you're in there and you're like, well, now that I'm here, I can definitely see that we could rearrange some stuff and just definitively make it better, if you get to that place, I would consider it. But if this ends up being mostly a pretty rote transformation like you said, asserts become expects, and lets get switched around, you know, that sort of stuff, if it's a very mechanical process of getting done, I would probably say very minimal. But again, if there is that, like, you know what? I had to understand the test in order to port them anyway, so while I'm here, let me take advantage of that, that's probably the thing that I would consider. But if not that, then I would say even though it's messy and whatnot and your inclination would be to clean it, I would say leave it roughly as is. That's my guess or how I would approach it. STEPH: Yeah, I love that. I love how you pointed out, like, did you build up the context? Because you're right, in a lot of these test cases, I'm not, or I'm trying really hard to not build up context. I'm trying very hard to just move them over and, if I have to, mainly to find test descriptions. That's the main area I'm struggling to...how can I more explicitly state what this test does so the next person reading this will have more comprehension than I do? But otherwise, I'm trying hard to not have any real context around it. And that's such a good point because that's often...when someone else is in the middle of something, and they're deciding whether to include that cleanup or refactor or improvement, one of my suggestions is like, hey, we've got the context now. Let's go with it. But if you've built up very little context, then that's not a really good catalyst or reason to then dig in deeper and apply that cleanup. That's super helpful. Thank you. That will help reinforce what I'm going to do, which is exactly let's migrate RSpec and not worry about cleanup, which feels terrible; I'm just going to say that into the world. But it also feels like the right thing to do. CHRIS: Well, I'm happy to have helped. And I share the like, and it feels terrible. I want to do the right thing, but sometimes you got to pick a battle sort of thing. STEPH: Cool. Well, that's a huge help to me. What's going on in your world? CHRIS: What's going on in my world? I watched a great video the other day from the Google I/O. I think it's an event; I'm not actually sure, conference or something like that. But it was some Google Chrome developers talking about a new feature that's coming to the platform called Page Transitions. And I've kept an eye on this for a while, but it seems like it's more real. Like, I think they put out an RFC or an initial sort of set of ideas a while back. And the web community was like, "Oh, that's not going to work out so well." So they went back to the drawing board, revisited. I've actually implemented in Chrome Canary a version of the API. And then, in the video that I watched, which we'll include a show notes link to, they demoed the functionality of the Page Transitions API and showed what you can do. And it's super cool. It allows for the sort of animations that you see in a lot of native mobile apps where you're looking at a ListView, you click on one of the items, and it grows to fill the whole screen. And now you're on the detail screen for that item that you were looking at. But there was this very continuous animated transition that allows you to keep context in your head and all of those sorts of nice things. And this just really helps to bridge that gap between, like, the web often lags behind the native mobile platforms in terms of the experiences that we can build. So it was really interesting to see what they've been able to pull off. The demo is a pretty short video, but it shows a couple of different variations of what you can build with it. And I was like, yeah, these look like cool native app transitions, really nifty. One thing that's very interesting is the actual implementation of this. So it's like you have one version of the page, and then you transition to a new version of the page, and in doing so, you want to animate between them. And the way that they do it is they have the first version of the page. They take a screenshot of it like the browser engine takes a screenshot of it. And then they put that picture on top of the actual browser page. Then they do the same thing with the next version of the page that they're going to transition to. And then they crossfade, like, change the opacity and size and whatnot between the two different images, and then you're there. And in the back of my mind, I'm like, I'm sorry, what now? You did which? I'm like, is this the genius solution that actually makes this work and is performant? And I wonder if there are trade-offs. Like, do you lose interactivity between those because you've got some images that are just on the screen? And what is that like? But as they were going through it, I was just like, wait, I'm sorry, you did what? This is either the best idea I've ever heard, or I'm not so sure about this. STEPH: That's fascinating. You had me with the first part in terms of they take a screenshot of the page that you're leaving. I'm like, yeah, that's a great idea. And then talking about taking a picture of the other page because then you have to load it but not show it to the user that it's loaded. And then take a picture of it, and then show them the picture of the loaded page. But then actually, like you said, then crossfade and then bring in the real functionality. I am...what am I? [laughter] CHRIS: What am I actually? STEPH: [laughs] What am I? I'm shocked. I'm surprised that that is so performant. Because yeah, I also wouldn't have thought of that, or I would have immediately have thought like, there's no way that's going to be performant enough. But that's fascinating. CHRIS: For me, performance seems more manageable, but it's the like, what are you trading off for that? Because that sounds like a hack. That sounds like the sort of thing I would recommend if we need to get an MVP out next week. And I'm like, what if we just tried this? Listen, it's got some trade-offs. So I'm really interested to see are those trade-offs present? Because it's the browser engine. It's, you know, the low-level platform that's actually managing this. And there are some nice hooks that allow you to control it. And at a CSS level, you can manage it and use keyframe animations to control the transition more directly. There's a JavaScript API to instrument the sequencing of things. And so it's giving you the right primitives and the right hooks. And the fact that the implementation happens to use pictures or screenshots, to use a slightly different word, it's like, okey dokey, that's what we're doing. Sounds fun. So I'm super interested because the functionality is deeply, deeply interesting to me. Svelte actually has a version of this, the crossfade utility, but you have to still really think about how do you sequence between the two pages and how do you do the connective tissue there? And then Svelte will manage it for you if you do all the right stuff. But the wiring up is somewhat complicated. So having this in the browser engine is really interesting to me. But yeah, pictures. STEPH: This is one of those ideas where I can't decide if this was someone who is very new to the team and new to the idea and was like, "Have we considered screenshots? Have we considered pictures?" Or if this is like the uber senior person on the team that was like, "Yeah, this will totally work with screenshots." I can't decide where in that range this idea falls, which I think makes me love it even more. Because it's very straightforward of like, hey, what if we just tried this? And it's working, so cool, cool, cool. CHRIS: There's a fantastic meme that's been making the rounds where it's a bell curve, and it's like, early in your career, middle of your career, late in your career. And so early in your career, you're like, everything in one file, all lines of code that's just where they go. And then in the middle of your career, you're like, no, no, no, we need different concerns, and files, and organizational structures. And then end of your career...and this was coming up in reference to the TypeScript team seems to have just thrown everything into one file. And it's the thing that they've migrated to over time. And so they have this many, many line file that is basically the TypeScript engine all in one file. And so it was a joke of like, they definitely know what they're doing with programming. They're not just starting last week sort of thing. And so it's this funny arc that certain things can go through. So I think that's an excellent summary there [laughs] of like, I think it was folks who have thought about this really hard. But I kind of hope it was someone who was just like, "I'm new here. But have we thought about pictures? What about pictures?" I also am a little worried that I just deeply misunderstood [laughs] the representation but glossed over it in the video, and I'm like, that sounds interesting. So hopefully, I'm not just wildly off base here. [laughs] But nonetheless, the functionality looks very interesting. STEPH: That would be a hilarious tweet. You know, I've been waiting for that moment where I've said something that I understood into the mic and someone on Twitter just being like, well, good try, but... [laughs] CHRIS: We had a couple of minutes where we tried to figure out what the opposite of ranting was, and we came up with pranting and made up a word instead of going with praising or raving. No, that's what it is, raving. [laughs] STEPH: No, raving. I will never forget now, raving. [laughs] CHRIS: So, I mean, we've done this before. STEPH: That's true. Although they were nice, I don't think they tweeted. I think they sent in an email. They were like, "Hey, friends." [laughter] CHRIS: Actually, we got a handful of emails on that. [laughter] STEPH: Did you know the English language? CHRIS: Thank you, kind Bikeshed audience, for not shaming us in public. I mean, feel free if you feel like it. [laughs] But one other thing that came up in this video, though, is the speaker was describing single-page apps are very common, and you want to have animated transitions and this and that. And I was like, single-page app, okay, fine. I don't like the terminology but whatever. I would like us to call it the client-side app or client-side routing or something else. But the fact that it's a single page is just a technical consideration that no user would call it that. Users are like; I go to the web app. I like that it has URLs. Those seem different to me. Anyway, this is my hill. I'm going to die on it. But then the speaker in the video, in contrast to single-page app referenced multi-page app, and I was like, oh, come on, come on. I get it. Like, yes, there are just balls of JavaScript that you can download on the internet and have a dynamic graphics editor. But I think almost all good things on the web should have URLs, and that's what I would call the multiple pages. But again, that's just me griping about some stuff. And to name it, I don't think I'm just griping for griping sake. Like, again, I like to think about things from the user perspective, and the URL being so important. And having worked with plenty of apps that are implemented in JavaScript and don't take the URL or the idea that we can have different routable resources seriously and everything is just one URL, that's a failure mode in my mind. We missed an opportunity here. So I think I'm saying a useful thing here and not just complaining on the internet. But with that, I will stop complaining on the internet and send it back over to you. What else is new in your world, Steph? STEPH: I do remember the first time that you griped about it, and you were griping about URLs. And there was a part of me that was like, what is he talking about? [laughter] And then over time, I was like, oh, I get it now as I started actually working more in that world. But it took me a little bit to really appreciate that gripe and where you're coming from. And I agree; I think you're coming from a reasonable place, not that I'm biased at all as your co-host, but you know. CHRIS: I really like the honest summary that you're giving of, like, honestly, the first time you said this, I let you go for a while, but I did not know what you were talking about. [laughs] And I was like, okay, good data point. I'm going to store that one away and think about it a bunch. But that's fine. I'm glad you're now hanging out with me still. [laughter] STEPH: Don't do that. Don't think about it a bunch. [laughs] Let's see, oh, something else that's going on in my world. I had a really fun pairing session with another thoughtboter where we were digging into query objects and essentially extracting some logic out of an ActiveRecord model and then giving that behavior its own space and elevated namespace in a query object. And one of the questions or one of the things that came up that we needed to incorporate was optional filters. So say if you are searching for a pizza place that's nearby and you provide a city, but you don't provide what's the optional zip code, then we want to make sure that we don't apply the zip code in the where clause because then you would return all the pizza places that have a nil zip code, and that's just not what you want. So we need to respect the fact that not all the filters need to be applied. And there are a couple of ways to go about it. And it was a fun journey to see the different ways that we could structure it. So one of the really good starting points is captured in a blog post by Derek Prior, which we'll include a link to in the show notes, and it's using yield_self for composable ActiveRecord relations. But essentially, it starts out with an example where it shows that you're assigning a value to then the result of an if statement. So it's like, hey, if the zip code is present, then let's filter by zip code; if not, then just give us back the original relation. And then you can just keep building on it from there. And then there's a really nice implementation that Derek built on that then uses yield_self to pass the relation through, which then provides a really nice readability for as you are then stepping through each filter and which one should and shouldn't be applied. And now there's another blog post, and this one's written by Thiago Silva, A Case for Query Objects in Rails. And this one highlighted an approach that I haven't used before. And I initially had some mixed feelings about it. But this approach uses the extending method, which is a method that's on ActiveRecord query methods. And it's used to extend the scope with additional methods. You can either do this by providing the name of a module or by providing a block. It's only going to apply to that instance or to that specific scope when you're using it. So it's not going to be like you're running an include or something like that where all instances are going to now have access to these methods. So by using that method, extending, then you can create a module that says, "Hey, I want to create this by zip code filter that will then check if we have a zip code, let's apply it, if not, return the relation. And it also creates a really pretty chaining experience of like, here's my original class name. Let's extend with these specific scopes, and then we can say by zip code, by pizza topping, whatever else it is that we're looking to filter by. And I was initially...I saw the extending, and it made me nervous because I was like, oh, what all does this apply to? And is it going to impact anything outside of this class? But the more I've looked at it, the more I really like it. So I think you've seen this blog post too. And I'm curious, what are your thoughts about this? CHRIS: I did see this blog post come through. I follow that thoughtbot blog real close because it turns out some of the best writing on the internet is on there. But I saw this...also, as an aside, I like that we've got two Derek Prior references in one episode. Let's see if we can go for three before the end. But one thing that did stand out to me in it is I have historically avoided scopes using scope like ActiveRecord macro thing. It's a class method, but like, it's magic. It does magic. And a while ago, class methods and scopes became roughly equivalent, not exactly equivalent, but close enough. And for me, I want to use methods because I know stuff about methods. I know about default arguments. And I know about all of these different subtleties because they're just methods at the end of the day, whereas scopes are special; they have certain behavior. And I've never really known of the behavior beyond the fact that they get implemented in a different way. And so I was never really sold on them. And they're different enough from methods, and I know methods well. So I'm like, let's use the normal stuff where we can. The one thing that's really interesting, though, is the returning nil that was mentioned in this blog post. If you return nil in a scope, it will handle that for you. Whereas all of my query objects have a like, well, if this thing applies, then scope dot or relation dot where blah, blah, blah, else return relation unchanged. And the fact that that natively exists within scope is interesting enough to make me reconsider my stance on scopes versus class methods. I think I'm still doing class method. But it is an interesting consideration that I was unaware of before. STEPH: Yeah, it's an interesting point. I hadn't really considered as much whether I'm defining a class-level method versus a scope in this particular case. And I also didn't realize that scopes handle that nil case for you. That was one of the other things that I learned by reading through this blog post. I was like, oh, that is a nicety. Like, that is something that I get for free. So I agree. I think this is one of those things that I like enough that I'd really like to try it out more and then see how it goes and start to incorporate it into my process. Because this feels like one of those common areas of where I get to it, and I'm like, how do I do this again? And yield_self was just complicated enough in terms of then using the fancy method method to then be able to call the method that I want that I was like, I don't remember how to do this. I had to look it up each time. But including this module with extending and then being able to use scopes that way feels like something that would be intuitive for me that then I could just pick up and run with each time. CHRIS: If it helps, you can use then instead of yield_self because we did upgrade our Ruby a while back to have that change. But I don't think that actually solves the thing that you're describing. I'd have liked the ampersand method and then simple method name magic incantation that is part of the thing that Derek wrote up. I do use it when I write query objects, but I have to think about it or look it up each time and be like, how do I do that? All right, that's how I do that. STEPH: Yeah, that's one of the things that I really appreciate is how often folks will go back and update blog posts, or they will add an addition to them to say, "Hey, there's something new that came out that then is still relevant to this topic." So then you can read through it and see the latest and the greatest. It's a really nice touch to a number of our blog posts. But yeah, that's what was on my mind regarding query objects. What else is going on in your world? CHRIS: I have this growing feeling that I don't quite know what to do with. I think I've talked about it across some of our conversations in the world of observability. But broadly, I'm starting to like...I feel like my brain has shifted, and I now see the world slightly differently, and I can't go back. But I also don't know how to stick the landing and complete this transition in my brain. So it's basically everything's an event stream; this feels true. That's life. The arrow of time goes in one direction as far as I understand it. And I'm now starting to see it manifest in the code that we're writing. Like, we have code to log things, and we have places where we want to log more intentionally. Then occasionally, we send stuff off to Sentry. And Sentry tells us when there are errors, that's great. But in a lot of places, we have both. Like, we will warn about something happening, and we'll send that to the logs because we want to have that in the logs, which is basically the whole history of what's happened. But we also have it in Sentry, but Sentry's version is just this expanded version that has a bunch more data about the user, and things, and the browser that they were in. But they're two variations on the same event. And then similarly, analytics is this, like, third leg of well, this thing happened, and we want to know about it in the context. And what's been really interesting is we're working with a tool called Customer.io, which is an omnichannel communication whiz-bang adventure. For us, it does email, SMS, and push notifications. And it's integrated into our segment pipeline, so events flow in, events and users essentially. So we have those two different primitives within it. And then within it, we can say like, when a user does X, then send them an email with this copy. As an aside, Customer.io is a fantastic platform. I'm super-duper impressed. We went through a search for a tool like it, and we ended up on a lot of sales demos with folks that were like, hey, so yeah, starting point is $25,000 per year. And, you know, we can talk about it, but it's only going to go up from there when we talk about it, just to be clear. And it's a year minimum contract, and you're going to love it. And we're like, you do have impressive platforms, but okay, that's a bunch. And then, we found Customer.io, and it's month-by-month pricing. And it had a surprisingly complete feature set. So overall, I've been super impressed with Customer.io and everything that they've afforded. But now that I'm seeing it, I kind of want to move everything into that world where like, Customer.io allows non-engineer team members to interact with that event stream and then make things happen. And that's what we're doing all the time. But I'm at that point where I think I see the thing that I want, but I have no idea how to get there. And it might not even be tractable either. There's the wonderful Turning the Database Inside Out talk, which describes how everything is an event stream. And what if we actually were to structure things that way and do materialized views on top of it? And the actual UI that you're looking at is a materialized view on top of the database, which is a materialized view on top of that event stream. So I'm mostly in this, like, I want to figure this out. I want to start to unify all this stuff. And analytics pipes to one tool that gets a version of this event stream, and our logs are just another, and our error system is another variation on it. But they're all sort of sampling from that one event stream. But I have no idea how to do that. And then when you have a database, then you're like, well, that's also just a static representation of a point in time, which is the opposite of an event stream. So what do you do there? So there are folks out there that are doing good thinking on this. So I'm going to keep my ear to the ground and try and see what's everybody thinking on this front? But I can't shake the feeling that there's something here that I'm missing that I want to stitch together. STEPH: I'm intrigued on how to take this further because everything you're saying resonates in terms of having these event streams that you're working with. But yet, I can't mentally replace that with the existing model that I have in my mind of where there are still certain ideas of records or things that exist in the world. And I want to encapsulate the data and store that in the database. And maybe I look it up based on when it happens; maybe I don't. Maybe I'm looking it up by something completely different. So yeah, I'm also intrigued by your thoughts, but I'm also not sure where to take it. Who are some of the folks that are doing some of the thinking in this area that you're interested in, or where might you look next? CHRIS: There's the Kafka world of we have an event log, and then we're processing on top of that, and we're building stream processing engines as the core. They seem to be closest to the Turning the Database Inside Out talk that I was thinking or that I mentioned earlier. There's also the idea of CRDTs, which are Conflict-free Replicated Data Types, which are really interesting. I see them used particularly in real-time application. So it's this other tool, but they are basically event logs. And then you can communicate them well and have two different people working on something collaboratively. And these event logs then have a natural way to come together and produce a common version of the document on either end. That's at least my loose understanding of it, but it seems like a variation on this theme. So I've been looking at that a little bit. But again, I can't see how to map that to like, but I know how to make a Rails app with a Postgres database. And I think I'm reasonably capable at it, or at least I've been able to produce things that are useful to humans using it. And so it feels like there is this pretty large gap. Because what makes sense in my head is if you follow this all the way, it fundamentally re-architects everything. And so that's A, scary, and B; I have no idea how to get there, but I am intrigued. Like, I feel like there's something there. There's also Datomic is the other thing that comes to mind, which is a database engine in the Clojure world that stores the versions of things over time; that idea of the user is active. It's like, well, yeah, but when were they not? That's an event. That transition is an event that Postgres does not maintain at this point. And so, all I know is that the user is active. Maybe I store a timestamp because I'm thinking proactively about this. But Datomic is like no, no, fundamentally, as a primitive in this database; that's how we organize and think about stuff. And I know I've talked about Datomic on here before. So I've circled around these ideas before. And I'm pretty sure I'm just going to spend a couple of minutes circling and then stop because I have no idea how to connect the dots. [laughs] But I want to figure this out. STEPH: I have not worked with Kafka. But one of the main benefits I understand with Kafka is that by storing everything as a stream, you're never going to lose like a message. So if you are sending a message to another system and then that message gets lost in transit, you don't actually know if it got acknowledged or what happened with it, and replaying is really hard. Where do you pick up again? While using something like Kafka, you know exactly what you sent last, and then you're not going to have that uncertainty as to what messages went through and which ones didn't. And then the ability to replay is so important. I'm curious, as you continue to explore this, do you have a particular pain point in mind that you'd like to see improve? Or is it more just like, this seems like a really cool, novel idea; how can I incorporate more of this into my world? CHRIS: I think it's the latter. But I think the thing that I keep feeling is we keep going back and re-instrumenting versions of this. We're adding more logging or more analytics events over the wire or other things. But then, as I send these analytics events over the wire, we have Mixpanel downstream as an analytics visualization and workflow tool or Customer.io. At this point, those applications, I think, have a richer understanding of our users than our core Rails app. And something about that feels wrong to me. We're also streaming everything that goes through segment to S3 because I had a realization of this a while back. I'm like, that event stream is very interesting. I don't want to lose it. I'm going to put it somewhere that I get to keep it. So even if we move off of either Mixpanel or Customer.io or any of those other platforms, we still have our data. That's our data, and we're going to hold on to it. But interestingly, Customer.io, when it sends a message, will push an event back into segments. So it's like doubly connected to segment, which is managing this sort of event bus of data. And so Mixpanel then gets an even richer set there, and the Rails app is like, I'm cool. I'm still hanging out, and I'm doing stuff; it's fine. But the fact that the Rails app is fundamentally less aware of the things that have happened is really interesting to me. And I am not running into issues with it, but I do feel odd about it. STEPH: That touched on a theme that is interesting to me, the idea that I hadn't really considered it in those terms. But yeah, our application provides the tool in which people can interact with. But then we outsource the behavior analysis of our users and understanding what that flow is and what they're going through. I hadn't really thought about those concrete terms and where someone else owns the behavior of our users, but yet we own all the interaction points. And then we really need both to then make decisions about features and things that we're building next. But that also feels like building a whole new product, that behavior analysis portion of it, so it's interesting. My consulting brain is going wild at the moment between like, yeah, it would be great to own that. But that the other time if there's this other service that has already built that product and they're doing it super well, then let's keep letting them manage that portion of our business until we really need to bring it in-house. Because then we need to incorporate it more into our application itself so then we can surface things to the user. That's the part where then I get really interested, or that's the pain point that I could see is if we wanted more of that behavior analysis, that then we want to surface that in the app, then always having to go to a third-party would start to feel tedious or could feel more brittle. CHRIS: Yeah, I'm definitely 100% on not rebuilding Mixpanel in our app and being okay with the fact that we're sending that. Again, the thing that I did to make myself feel better about this is stream the data to S3 so that I have a version of it. And if we want to rebuild the data warehouse down the road to build some sort of machine learning data pipeline thing, we've got some raw data to work with. But I'm noticing lots of places where we're transforming a side effect, a behavior that we have in the system to dispatching an event. And so right now, we have a bunch of stuff that we pipe over to Slack to inform our admin team, hey, this thing happened. You should probably intervene. But I'm looking at that, and we're doing it directly because we can control the message in Slack a little bit better. But I had this thought in the back of my mind; it's like, could we just send that as an event, and then some downstream tool can configure messages and alerts into Slack? Because then the admin team could actually instrument this themselves. And they could be like; we are no longer interested in this event. Users seem fine on that front. But we do care about this new event. And all we need to do as the engineering team is properly instrument all of that event stream tapping. Every event just needs to get piped over. And then lots of powerful tools downstream from that that can allow different consumers of that data to do things, and broadly, that dispatch events, consume them on the other side, do fun stuff. That's the story. That's the dream. But I don't know; I can't connect all the dots. It's probably going to take me a couple of weeks to connect all these dots, or maybe years, or maybe my entire career, something like that. But, I don't know, I'm going to keep trying. STEPH: This feels like a fun startup narrative, though, where you start by building the thing that people can interact with. As more people start to interact with it, how do we start giving more of our team the ability to then manage the product that then all of these users are interacting with? And then that's the part that you start optimizing for. So there are always different interesting bits when you talk about the different stages of Sagewell, and like, what's the thing you're optimizing for? And I'm sure it's still heavily users. But now there's also this addition of we are also optimizing for our team to now manage the product. CHRIS: Yes, you're 100%. You're spot on there. We have definitely joked internally about spinning out a small company to build this analytics alerting tool [laughs] but obviously not going to do that because that's a distraction. And it is interesting, like, we want to build for the users the best thing that we can and where the admin team fits within that. To me, they're very much customers of engineering. Our job is to build the thing for the users but also, to be honest, we have to build a thing for the admins to support the users and exactly where that falls. Like, you and I have talked a handful of times about maybe the admin isn't as polished in design as other things. But it's definitely tested because that's a critical part of how this application works. Maybe not directly for a user but one step removed for a user, so it matters. Absolutely we're writing tests to cover that behavior. And so yeah, those trade-offs are always interesting to me and exploring that space. But 100%: our admin team are core customers of the work that we're doing in engineering. And we try and stay very close and very friendly with them. STEPH: Yeah, I really appreciate how you're framing that. And I very much agree and believe with you that our admin users are incredibly important. CHRIS: Well, thank you. Yeah, we're trying over here. But yeah, I think I can wrap up that segment of me rambling about ideas that are half-formed in my mind but hopefully are directionally important. Anyway, what else is up with you? STEPH: So, not that long ago, I asked you a question around how the heck to manage themes that I have going on. So we've talked about lots of fun productivity things around managing to-dos, and emails, and all that stuff. And my latest one is thinking about, like, I have a theme that I want to focus on, maybe it's this week, maybe it's for a couple of months. And how do I capture that and surface it to myself and see that I'm making progress on that? And I don't have an answer to that. But I do have a theme that I wanted to share. And the one that I'm currently focused on is building up management skills and team lead skills. That is something that I'm focused on at the moment and partially because I was inspired to read the book Resilient Management written by Lara Hogan. And so I think that is what has really set the idea. But as I picked up the book, I was like, this is a really great book, and I'd really like to share some of this. And then so that grew into like, well, let's just go ahead and make this a theme where I'm learning this, and I'm sharing this with everyone else. So along that note, I figured I would share that here. So we use Basecamp at thoughtbot. And so, I've been sharing some Basecamp posts around what I'm learning in each chapter. But to bring some of that knowledge here as well, some of the cool stuff that I have learned so far...and this is just still very early on in the book. There are a couple of different topics that Laura covers in the first chapter, and one of them is humans' core needs at work. And then there's also the concept of meeting your team, some really good questions that you can ask during your first one-on-one to get to know the person that then you're going to be managing. The part that really resonated with me and something that I would like to then coach myself to try is helping the team get to know you. So as a manager, not only are you going out of your way to really get to know that person, but how are you then helping them get to know you as well? Because then that's really going to help set that relationship in regards of they know what kind of things that you're optimizing for. Maybe you're optimizing for a deadline, or for business goals, or maybe it's for transparency, or maybe it would be helpful to communicate to someone that you're managing to say, "Hey, I'm trying some new management techniques. Let me know how this goes." [chuckles] So there's a healthier relationship of not only are you learning them, but they're also learning you. So some of the questions that Laura includes as examples as something that you can share with your team is what do you optimize for in your role? So is it that you're optimizing for specific financial goals or building up teammates? Or maybe it's collaboration, so you're really looking for opportunities for people to pair together. What do you want your teammates to lean on you for? I really liked that question. Like, what are some of the areas that bring you joy or something that you feel really skilled in that then you want people to come to you for? Because that's something that before I was a manager...but it's just as you are growing as a developer, that's such a great question of like, what do you want to be known for? What do you want to be that thing that when people think of, they're like, oh, you should go see Chris about this, or you should go see Steph about this? And two other good questions include what are your work styles and preferences? And what management skills are you currently working on learning or improving? So I really like this concept of how can I share more of myself? And the great thing about this book that I'm learning too is while it is geared towards people that are managers, I think it's so wonderful for people who are non-managers or aspiring managers to read this as well because then it can help you manage whoever's managing you. So then that way, you can have some upward management. So we had recent conversations around when you are new to a team and getting used to a manager, or maybe if you're a junior, you have to take a lot of self-advocacy into your role to make sure things are going well. And I think this book does a really good job for people that are looking to not only manage others but also manage themselves and manage upward. So that's some of the journeys from the first chapter. I'll keep you posted on the other chapters as I'm learning more. And yeah, if anybody hasn't read this book or if you're interested, I highly recommend it. I'll make sure to include a link in the show notes. CHRIS: That was just the first chapter? STEPH: Yeah, that was just the first chapter. CHRIS: My goodness. STEPH: And I shortened it drastically. [laughs] CHRIS: Okay. All right, off to the races. But I think the summary that you gave there, particularly these are true when you're managing folks but also to manage yourself and to manage up, like, this is relevant to everyone in some capacity in some shape or form. And so that feels very true. STEPH: I will include one more fun aspect from the book, and that's circling back to the humans' core needs at work. And she references Paloma Medina, a coach, and trainer who came up with this acronym. The acronym is BICEPS, and it stands for belonging, improvement, choice, equality, predictability, and significance. And then details how each of those are important to us in our work and how when one of those feels threatened, then that can lead to some problems at work or just even in our personal life. But the fun example that she gave was not when there's a huge restructuring of the organization and things like that are going on as being the most concerning in terms of how many of these needs are going to be threatened or become vulnerable. But changing where someone sits at work can actually hit all of these, and it can threaten each of these needs. And it made me think, oh, cool, plus-one for being remote because we can sit wherever we want. [laughs] But that was a really fun example of how someone's needs at work, I mean, just moving their desk, which resonates, too, because I've heard that from other people. Some of the friends that I have that work in more of a People Ops role talk about when they had to shift people around how that caused so much grief. And they were just shocked that it caused so much grief. And this explains why that can be such a big deal. So that was a fun example to read through. CHRIS: I'm now having flashbacks to times where I was like, oh, I love my spot in the office. I love the people I'm sitting with. And then there was that day, and I had to move. Yeah, no, those were days. This is true. STEPH: It triggered all the core BICEPS, all the things that you need to work. It threatened all of them. Or it could have improved them; who knows? CHRIS: There were definitely those as well, yeah. Although I think it's harder to know that it's going to be great on the way in, so it's mostly negative. I think it has that weird bias because you're like, this was a thing, I knew it. I at least understood it. And then you're in a new space, and you're like, I don't know, is this going to be terrible or great? I mean, hopefully, it's only great because you work with great people, and it's a great office. [laughs] But, like, the unknown, you're moving into the unknown, and so I think it has an inherent at least questioning bias to it. STEPH: Agreed. 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 on 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 hosts@bikeshed.fm via email. CHRIS: Thanks so much for listening to The Bike Shed, and we'll see you next week. ALL: 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.
Derek Prior is a fantasy author best known for the Annals of the Nameless Dwarf series. Listen as he shares his author's journey and what it took for him to become successful. You can find him at www.dpprior.com
The big "Three Oh Oh!" What a milestone for this podcast! Aside from celebrating that the show has made it this far, Chris gives some followup on some Inertia.js issues he had been having, and talks about open source licenses and legality and testing against external APIs. Steph has thoughts on mozzarella sticks and what makes good ones; particularly the cheese to bread ratio... They then, together, answer a listener question re: knowledge silos: Jan asked, "Our team (3 pairs) is currently working on two different projects due to that fact we are creating information silos. Now we are looking into ways how we can minimize those information silos. Do you have any ideas how we could achieve this?" With switching pairs they are unsure about it as it can be difficult for new pairs to get up to speed. inertia-rails thread safety (https://github.com/inertiajs/inertia-rails/pull/70) Rails Cache-Control no-store fix (https://github.com/rails/rails/pull/40324) Transcript: STEPH: I have no shame. CHRIS: That's important in this industry. STEPH: [laughs] Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Steph Viccari. CHRIS: And I'm Chris Toomey. STEPH: And together, we're here to share a bit of what we learned along the way. Hey, Chris. So today's an exciting day. It's a rather momentous day, at least in my world, because today is our 300th episode. CHRIS: 300? That is incredible. STEPH: That's an incredible amount of episodes. And it made me pause and reflect on how many episodes I have been a part of. And I've realized it's over 100. I think it's around 104 or something like that, and I can't believe it. Time flies when you're behind the mic. CHRIS: Time does fly, yeah. So yeah, fully a third of these you've been involved in. I don't know what the number is. And I'm just so grateful to Derek Prior and Sage Griffin, who started this whole process. And then to Thom Obarski, who was the producer for so long, and Mandy Moore, who recently joined us and has been doing a wonderful job of carrying that forward and to you, Steph, because this has just been such a joy to work on. Yeah, it's just a joy to be on the show and to get to chat with you each week and share some things. And frankly, learn from folks writing in questions and sharing pointers with us, and it really is such a delight. And yeah, 300 is pretty momentous. STEPH: The listener questions and feedback have undoubtedly been a highlight for me. That is one of the areas that I love the most. I love the questions. I also love when people provide helpful answers to us, and then they help us out in return and also, all the incredible guests that we've had on the show. It has been phenomenal. I'm also very thankful to have been part of this journey and appreciate everyone that has got us here today. I wonder what the fourth iteration of The Bike Shed looks like. I consider this the third iteration because the first iteration was Sage Griffin and Derek Prior. The second iteration was where you took over The Bike Shed, and then you were hosting a number of incredible guests on the show. And then the third iteration is the iteration that we're living, so I wonder what the fourth will look like. CHRIS: Oh, that is an interesting question. Hopefully, you and I get to hang out for a good bit longer. But at some point, much like the Green Lantern, this will get passed on, and someone else will take up the mantle and tell some stories. But, yeah, hopefully, that's not too soon because I certainly enjoy hanging out with you. STEPH: Oh, I agree. I certainly enjoy this, and I'm in no rush to leave The Bike Shed. But I think it's just fun thinking about the next people that will carry this journey forward. CHRIS: And determine the color of The Shed. STEPH: And determine. I mean, that is their right. As host and co-host, they get to determine the color of The Shed. CHRIS: 300 episodes in, and we still haven't figured it out. So I guess we got to keep trying. STEPH: Oh, I have. I already know what color it is. CHRIS: Is it yellow? STEPH: It's yellow. CHRIS: Yeah. Okay. [laughs] STEPH: I like how we said yellow at the same time, you know. [laughs] CHRIS: I do, although I feel like it's wrong to have a color in mind, or at least I want to dig in and talk about it for a while just to be in keeping with the show, but... STEPH: One must first argue before deciding and then argue again. But to not continue bikeshedding on The Bike Shed, what's new in your world? CHRIS: My week has been good. Actually, I have two quick updates on various Inertia things that I've shared in previous weeks. So we can include a show notes link for the two different episodes where I talked about these respective things. But there was one weird issue that I ran into with Inertia where it could start clicking a button that would delete, was behaving weirdly and occasionally, intermittently; some of the responses would end up as a full HTML page response as opposed to the expected Inertia response. And there's a bunch of subtlety around this. I actually reported it as an issue to the Inertia team. And they very kindly pointed me to the HTTP semantics at place. So it's the difference between a 302 redirect and a 303 redirect. And so, in their code, they were correctly doing a 303. They were standards-compliant; everything was great. But for some reason, it was still misbehaving sort of randomly, and I could never pin it down. I ended up working around it and opting out of Inertia behavior for those endpoints. But my assumption was that something in my Rails Middleware Stack was behaving weirdly and occasionally overriding Inertia Rails' setting of the status. So Inertia Rails was saying, "303," which is a special version of redirect, and something else in the Rails Middleware Stack, was saying, "302, it will be fine." Turns out, in retrospect, the Inertia Rails team has discovered that this was, in fact, a threading bug on their side. So it's not Inertia's fault. Inertia as a core concept and as a protocol was definitely doing the right thing. And the Inertia-Rails Middleware was attempting to do the right thing. But threads and concurrency got in the way, which I'll be honest, I don't deeply understand those concepts. So I was just like, oh okay, that sounds like a thing that could go wrong occasionally, which is exactly how I experienced it. But now they've made an update to the project, so that should be resolved in a deep way. But goes to show you threading and concurrency are really tricky to chase down. STEPH: I appreciate that you're coming back to give us the conclusion to that issue because I remember talking about it, and you were still going off on a journey and finding out what's wrong, so that's super interesting. And yeah, threads and concurrency those are super easy, like cache invalidation and naming, that's right up there. CHRIS: It's actually kind of funny. One of the issue threads where I wrote about it, someone followed up and asked if I'd come to any solution. And I said, "Oh, I've gone kind of this weird way, and I'm doing these things." But I shared a code sample, and I said, "Just to be clear, this is 100% about something Rails is doing and not Inertia, which remains a stellar project." And then, very shortly after that, someone from the Inertia-Rails team was like, "Ah, actually, I think it was us. Sorry about that, but we fixed it now." And I was like, "I still love you guys. This is great. You're doing a great job. [chuckles] You continue to push the envelope in a wonderful way." But it was a funny interaction where I was like, never shall I let the name be dragged through the mud. Whoops. Okay. Never mind. STEPH: You're an excellent hype man for Inertia. CHRIS: I try, I really try. I believe in it to my core. And actually, there's another one that this one's not really related to Inertia at all, although I've seen it discussed within the context of Inertia. And again, I think the Inertia team has done a really great job of responding and pointing to here are the HTTP semantics, and adhering to the standards, and the way that things should work. But this one has to do with the back button. When you're doing sequential forms or really any sort of form type thing, the browser will just pull from its back/forward cache, which is a local cache of the HTML of the page as it just had it. And I had come to the understanding that this was not something that I could workaround. This was not something that I could control. I had tried every combination of headers, at least I thought I had, in Rails to try and control this from the server-side because ideally, the server is the one who knows about when data is changing and things of that nature. The server should be able to inform the browser, "Hey, don't cache or store this page in any way, always revalidate it." It turns out there was a bug in Rails that was improperly normalizing the Cache-Control header and always removing the no-store Cache-Control value. So there are like five different or a handful of possible values that can be set for that header for the Cache-Control header. And Rails has a bunch of internal logic that says, "Okay, if you've set this, then I'll put these two, but not that one." And they're just trying to manage it and do nice things on our behalf. But unfortunately, they were being a little overzealous in that normalization effort. And so they were dropping an important value, which is no-store. So now there's a PR opened in Rails, or I think it's actually been merged in at this point that will fix that and allow you to set that particular header value, which then should get the behavior of "Hey, browser, if I hit the back button, please go ask the server. Don't trust your local cache, “which is exactly what I want. STEPH: Interesting. Wow. So that's two very helpful resolutions to some of those strange issues you were running into before. CHRIS: Yeah, definitely. And actually, for that issue, in particular, it was a very kind Bike Shed listener; Alexei Vasiliev wrote in and shared some initial thoughts, pointed me in the direction of some things. In that case, I actually was like, "I don't think that's the case. I tried it." And he was like, "No, no, no, pretty sure." And he was definitely correct in this case and was very kind and gave me an example of code reproduction and all of those nice things. So I was able to chase this down and then eventually find the issue in Rails, which had been opened like eight days before. So I think for me, I just happened to run into a weird period of time where Rails was subtly broken around this behavior. And therefore, I determined that the world was broken when, in fact, it was just a tiny slice of Rails' history. But yes, thank you so much, Alexei, for writing in and pointing me in the right direction on that. STEPH: The dream came true. We talk about some of our troubles and our strifes, and people respond and help us out. CHRIS: That is the dream. But yeah, so those are some quick updates, not really about me, although tangentially, I got to go along for these rides, and it was fun. But what else is up in your world? STEPH: Let's see. Well, I also have a small update that I can share. It's circling back to the conversation that we had talking about extracting an untrustworthy service to a new location. And at that time, I don't remember exactly the process I laid out. But at that time, it's the idea that it is a bit untrustworthy, but we have some security in how this process works, and it is ideal that we move it to this other location. So let's just go ahead and move it wholesale, bugs and all to the new location. And then there, we will start to refine, and we'll start to improve the service. Well, the update is that we have realized that the untrustworthy service is untrustworthy enough that I'm actually working on improving it in its current place just to a certain extent that then it feels like we can move it to another location. There have been enough issues with it that it has taken my focus to continue patching those bugs and making sure everything is working appropriately. But now I'm in the space of where I'm like, goodness, I thought I knew this thing and now I'm realizing I don't. And so, I'm looking for ways to inform myself and the team when something isn't working when we think it is. So to provide a bit of context, this service is sending a bunch of messages to other systems, and most of the time, that is working, but there are times that it's not. And when it's not working, it's silent about the fact that those messages aren't being sent, and it's very important that we send those messages. So what's been on my mind is looking for a way to then elevate myself and the team to say, "Hey, these are the number of messages that are being sent on average." And then suddenly, let's say it dropped by 50%, or maybe we typically send 98% successful messages, and we have a 2% failure rate, but suddenly we have a 50% failure rate, but looking for those metrics that I can capture and then alert the team if something is going wrong. And one of the suggestions that was bubbled up by Chad Pytel, who's a developer, he's also founder and COO of thoughtbot, and we're working on the same project together. And he had highlighted that a previous project that he worked on used AWS specifically to leverage the idea of tracking how many successful messages are being sent, or perhaps in their particular project, it was focused on how many orders were being processed. That was important to know. And in our case, we could do a similar metric where we look to see are we still sending messages? Has the number dropped significantly lately so then we can be notified, and then we can escalate that to PagerDuty? So then we notify the team that something's going on. I don't know the specific mechanics of how I'm going to implement that yet. So I will report back, but I'm excited to have something that's going to alert me for when things aren't working the way I expect versus waiting for then someone that's a customer to notice it and then get back to us. It's very in line with a number of the topics that you've brought to the show recently, talking about how we can measure more of the user's experience and be notified sooner versus waiting for a user to bump into an error and then they reach out and notify us. CHRIS: I'm super interested to hear where you get with that because that's definitely an area that I've poked at but not dug into particularly deeply. I know there are a number of projects like StatsD is one of them. I think there are others in that space, but that's where you're sending metrics just out to some service, and then you can aggregate and graph. I've also done similar things with Papertrail; I want to say, where you can do a very specific search in the logs, and then within that, you can aggregate and graph and show things over time. So you can do a very simplified version of what you're describing to sort of visualize a rate of something over time. And then I think they might have some thresholding alerts. But also, that's one of those super hard things to do because it turns out like Monday morning, a lot of emails get sent and then Friday afternoon, fewer, and then on the weekend, none. And so, there's going to be an inherent sort of fluctuation to the data. And so then what is normal? What does the baseline look like? And then how do you do anomalies around that? Because inherently, there's going to be noise in the data. And so is it a 10% band around the normal? And I'm just saying a lot of words now that I barely know the meaning of. But it's one of those things where it's like, oh yeah, just let me know if it's behaving abnormally. There's so much in that one little sentence. And it's one of the like; I love the fractal complexity of this space where every part of that sentence that I just said is like, oh, that's way more complicated than it sounds when you just say that word. So very interested to hear where you get with this. And this is also something that I'll probably be pushing on in my work in the near term. So maybe we can even compare notes, but as of now, I just have, I think, buzzword-level knowledge of it. STEPH: Well, I love that phrasing fractal complexity because yes, that was also where my brain got hung up in starting to think about this process and like, well, what's normal? I don't actually know what normal looks like because I haven't been tracking this until now. So do I go back a week and say, "Okay, let's compare our average sent rate to in the past week and try to define normal in that timeframe?" And I think the answer, for now, is to do the smallest thing but also has the biggest impact, and that's to notify the team if messages just stop. That feels like the first, small step to take, and then we can fine-tune. Do we want to know if suddenly successful messages are being marked as a failure? We have an increase in failed messages versus successful messages. But I think the first iteration is just to know or to confirm that we are sending messages and send us an alert if suddenly we're not sending messages for...ooh, I just realized there's a complexity in that statement too. It's like, how long are we not sending messages for? Is it for an hour? Is it for a day? CHRIS: I was going to ask. [laughter] STEPH: I just caught myself there. Yeah. I don't have an answer to that right now. I have to think about it, but there's an answer there. I will have to choose an answer. CHRIS: You sure will. And then you'll probably have to tweak it over time. It's also one of those topics where false negatives and false positives are really easy to fall into where the system's alerting too often. And so people then start to ignore the alerts versus it's too cautious before it will send out an alert and, therefore, you're missing things and so finding that optimum level. It also might be different days of the week. Aah. [chuckles] STEPH: Yeah, I think that's very true. It will be different for different days of the week. So I have a lot more to think about in regards to how we're going to report on this. But that still feels very much like something I want in the world because right now, it's a lot of spelunking and production consoles to find out what's going on with the data and making sure that it's going through. And that feels like the least favorable option as to the world that I want to live in. Oh, on a completely unrelated topic, I saw an article that I'm very excited to read. And it's not related to technology at all, but it looks like a very delightful article that someone wrote and titled My 14-Hour Search for the End of TGI Friday's Endless Appetizers. And I haven't read it in-depth yet, but I just read the first bit, and it seems like it's going to be delightful. But I thought of you because we've had previous outtakes around mozzarella sticks. And you were very excited when you thought thoughtbot had mozzarella sticks, the actual fried kind versus just the healthier cheese stick kind. So this seems like a thing that you'd enjoy. CHRIS: I feel like it may have even ended up in an episode, and we talked about mozzarella wedges and the ratio of surface area to volume. STEPH: Yes. CHRIS: I don't know if that made it into an episode or not, but we have definitely you and I discussed mozzarella sticks before. And I'm definitely intrigued by this article. I will add it to Instapaper immediately and then probably never read it again because Instapaper is where I put things to forget them. But maybe someday I'll sit down with a coffee and read things. STEPH: I've heard you mention Instapaper before, and I've looked into it. And I don't know why, but it just hasn't stuck for me. So I always throw anything that I want to explore or something that is also critical for me to do. I use Todoist. I don't know if you're familiar with that app, but that's my go-to. CHRIS: Well, I'm familiar with Todoist. I take a slight line between my to-do list, which I want to be as, I don't know, clean and tidy and only the things that I have to do versus for me, Instapaper is a list of when I get around to it when I've got those ten free minutes, which apparently don't exist in the world. But when I have them, this is the list of things that I can read. But I think I've heard this from a number of people of having a more integrated system that all the stuff's in the same place. I keep my to-dos in Trello, also as an aside, and I'm not super happy with that. How do you like Todoist? Is it bringing you joy? STEPH: I really like Todoist. I find it is simple enough an interface that I'm not spending a lot of time customizing it or messing around with it. I can just go there and log the things that I want. I can create individual projects and spaces as well. So if I want to separate my personal to-do list from my work to-do list or if I have a project, that's a really nice feature as well. I think my only small complaint is if I'm writing a date or if I'm writing tomorrow, Todoist will try to do the smart thing and say, "Oh, I'm going to add a due date for you since you mentioned a date." And I'm like, no, no, no, I don't want a due date. I just want to mention the specific date because somehow it's relevant. And undoing that is sometimes a little tricky. But otherwise, I have found Todoist very helpful. I'm a big fan. Also, you and I are slightly different creatures in terms of how neat and tidy we keep our spaces. I think how we both manage our email inbox is a really good indicator of this where you are more organized than I am when it comes to emails. And so, our to-do list might be similar. I'd be interested to see if Todoist fits your needs or if it doesn't offer enough structure. CHRIS: I almost certainly could make it work. And it's one of those things where I've actually settled on Trello, which is a very loose tool. And so I've been able to shape it sort of to what I want, but it doesn't really have that many true productivity-type features. It's just a loose board where I can drag around cards and move them through. And that's worked fine, or I've been able to talk myself into not trying to be as neat and tidy and intentional with my to-do list, which I think has been good overall. I've looked at Todoist in the past. And the thing that gives me pause sort of related to what you were talking about with the date things, but I get the idea, or I get the sense that Todoist really, from a fundamental philosophical approach, really wants things to have dates and to have priorities, and my thinking is not quite that. Like, there is a priority, but it's relative. So it's the order of things in a list, but it's not this is a one, and this is a two, and that's another two. I find that logic of like there are different tiers of importance doesn't really map to my world, nor do dates. Almost everything I do has no date, has no context. It's just like when I'm at the computer because that's the only place I ever am. So it's when I'm at the computer, it's all kind of important-ish. Nothing really has a date, but it should probably be done pretty soon. That sort of stuff doesn't quite map to what I see in Todoist. So I've always found a little bit of a mismatch between what I think I want and what Todoist, as far as I understand, provides. I know they added Kanban-type boards recently. So I think that might help with just visualizing workflow and being a little closer to Trello, which I'm familiar with. But I'm sort of on the search right now for another to-do list. I like what you said about being able to separate the work and personal because that's definitely a thing that I would want, although there's always the added complexity of whatever tracking tool that we're using as a team at work and which things go into my list versus that list. And do I try and synchronize them in any way? And then I do what I do, which is I start to imagine this ridiculously complex, fully integrated, bi-directional sinking nonsense system where like, never mind. Stop it. Pen and paper, Trello. I don't know; you've lost your privileges, though. This is me talking to myself. I lose my privileges much like I'm not allowed to ever try Emacs. I have had a multi-year moratorium on exploring new productivity tools, but I think maybe, just maybe, now is the time to revisit that. STEPH: If you ever disappear for a week or two, I'll know that you tried Emacs or something like that happened CHRIS: [chuckles] My beard is three times longer when I come back, and I'm like, "All right. I figured some stuff out, though." STEPH: I'm with you in regards to trying to bucket all of your to-do items as if it's a priority one, two, three. I am not good at that, and I'm always wrong. So I've also given up on that system. I would describe myself as a minimalist user. I'm using all the basic functionality. I'm not leveraging what a lot of stuff that Todoist probably can do for me. And so I have a very just flat list of things that I'd like to do. I do have a couple of projects because I do try to have that personal versus work, and maybe I have some other project that's on there as well. And then, in my mind, I try to avoid due dates unless it's really important. Although I say that if it's really important, it's going on my calendar too because I'm going to budget time for it or make sure that I don't forget it. But then each day then I go through that full list, and then I pick the things that need to be done that day or it's reasonable to get done that day, and then I kick everything to the next day. So that way, I'm always reevaluating a fresh list of what do I need to tackle? What's reasonable for today, and what can I punt on? And Josh Clayton said this to me before, and I really liked it in terms of punting on work because typically, when you're really busy, something's always going to drop. You're always going to push something to the next day. So then it's just figuring out what's going to bounce and what's going to break? So I'm always looking for what's going to break? And let's prioritize that for today to make sure it gets done. If it will bounce, then I'm going to kick it to the next day, and I can't see it until I'm going back through that full list again. CHRIS: I really like that framing around you're going to have to drop things. That's just the nature of life. There's always more to do than there is time. So will it bounce, or will it break in that? And that framing around how to decide which things get moved out. Interestingly, I just looked up because I wanted to know does Todoist support snoozing things? Which is something that I use constantly in Trello and Gmail and basically everywhere else. I'm just like, nope, future me problem, future me problem, and I just keep pushing things into the future. But critically, I want them to be hidden until that time. And it sounds like Todoist; you can set a future due date, and then it'll show up in today. But again, that's sort of conflating how I think about productivity and whatnot. Also, I found…this is a Reddit post that I'm looking at where I'm determining this. And there is the question, and then there's someone answered, but the answer is deleted. And then there's someone replying to that saying, "Wow, what a thoughtful response. Have you written this up anywhere else, like a blog post? You sound like an absolute pro." But the parent comment, which apparently was beautiful, and articulate, and well-written, has been deleted. And this is the sadness of the internet. So a really beautiful xkcd about the saddest thing you can see is you search for a question, and you find Stack Overflow from 10 years ago one person asking the question and no answers. And you've got one other person out there in the world who cares the same way you do, but you have no answers, and it's sad. But I'm just sad about the loss of information. STEPH: That's so tragic, or that's a really pro troll move. And you leave a comment, and then below, you're like, “Wow, that was amazing. That was beautiful.” And then you delete your own previous comment. So then you're just tricking people into thinking there was an answer. CHRIS: It does sound almost performative, especially the last line, "You sound like an absolute pro." So I could see that being the case. And you know what? I'm going to choose to believe that that's what it is because then I can sleep better at night. So thank you, Steph. STEPH: Happy to help. CHRIS: But I think we should probably move on to perhaps a listener question or something. But before we do that, I do want to ask if anyone out there has a to-do list that they're using and they love; I would love to hear about it. I think I'm familiar with most of them, but votes of confidence from the listeners of this show will certainly go a long way with me. Because I think you folks are all very smart people. I mean, you're listening here, so, obviously. STEPH: Yes, obviously. This very deeply intellectual show about mozzarella sticks and the ratio of cheese to fried and what's the best. CHRIS: It's an important question. STEPH: It is an important question. I have strong feelings about it. That's why we've talked about it. [chuckles] CHRIS: On this very serious show that we host. STEPH: [chuckles] Yes, we have an awesome listener question that I'm really excited to dive into. But before we do, I have a quick git thing that I'd love to share. It's a tip that Dimitry, another thoughtboter, shared with me today that I think is just really nice and something that I have not used before. And it's specific to a workflow where if you need to grab a file from another branch or from another commit, and then if you want to bring it into your current branch. And there are a couple of ways to go about it. One of them is you can do git checkout main and then pass the file presuming the file that you want is in main and then you want to bring it to your current branch. And that will copy over the file to that exact location. But if you wanted to grab a file that's on the main branch but then you want to port that file to a new location, then you can use git-show and do git show branch. So let's say you're bringing a file from main over to your current branch, so it would be git show main: and then pass to the file that you wish to copy, and then the greater than sign and the path to where you want that file to live. So you can grab that file and then stash it in a new location, and you can also do it for commits too. So if someone has pushed up a commit and you want to copy a particular file, say if you need to bring in some of their work into your branch, then you could also do git show commit, and then that colon, and then the path to the file. And then, if you wanted to move it to a new location, you can use that greater than sign and then the path to where you would like that file to live. So it's a nice combination of the git command of git show and then also shell redirection. So then, you can pipe that content from the file that you wish to copy over to the new location that you would like. And it's not something that I've reached for very often, but I find lately I've been in a mode where I'm trying harder and harder to stay within my terminal and not have to jump over to GitHub or to external UIs if I can. And so this just feels like a nice additional tool where then I can use this one more thing where I don't have to either...I guess it's small. I could check out main locally. But even with this way, I don't have to switch branches, grab something, and bring it over, or I don't have to go to GitHub and then look for something. It feels like a nice way that then I could grab that file locally and bring it over to my branch. CHRIS: That's a nice combination of tips there. Like you said, a bunch of different pieces at play, but that is definitely a super useful thing. It's one of those that I've not gotten that into muscle memory yet or even close to muscle memory. Git is complicated in terms of the interface that it provides, at least at the command line. I've been trying to make sense of it all and then trying to find what are the useful workflows that I want to build? Because you can do anything, and you can do most things in five different ways. And so finding that set that you do want to know deeply but then also getting that committed into your hands, not even into your head, is the thing that I strive for. But that particular one is one that I struggle with every single time. So especially, I think you broke that down really nicely, so it makes sense. There's a corollary in Fugitive for any Vim users out there. There's a Gread command, so it's capital G-re-a-d. And then after that, it takes some identifier, and I've never gotten the identifier right. But as you just described it, it's the same as the git show sequence. So it's a commit or a branch name, colon, and then the file path that you want. And then, in Vim, you can use % to reference the current file. So I've tried really hard to teach my brain Gread main :%, and somehow, my brain doesn't want to remember that ridiculous sequence of characters. So, only in this moment am I like, oh, it all kind of fits together. STEPH: Oh, that's nice. I am a Vim Fugitive user, but I didn't know that one. And I'm with you; I rarely remember all these off the top of my head unless I've done them like a hundred times, and it finally starts to sink in. So I always have a cheat sheet, or since we were talking about tooling earlier, I use Notion to capture tidbits for myself. So this is a place where I would probably stash in a web development folder that I have. And it's just a tip to my future self as to like, hey, remember when you were trying to do that thing, and then you had to look it up and figure it out? Well, here's how you did it, so then I can revisit it in the future. CHRIS: I thought a number of times about introducing a flashcard system to revisit these sorts of things. Gary Bernhardt, who I had on a while back now, is building a platform that does this essentially for TypeScript and regular expressions in JavaScript arrays and a bunch of different topics. But it's got built into it the idea of spaced repetition, so you review a thing and then three days later, you review it again and then seven days later, and then ten. And there's a particular sequence to it, but it helps you to really internalize that knowledge. I've never gotten to the level of going to that, but I like that idea of being purposeful and trying to commit some things to memory because having them at your hands and being able to stay, like you said, in the terminal and closer to the work and not having to break out of the context, I do find a lot of value in that. But it does take some effort to build that up. So I've never quite gotten to that flashcard system myself. STEPH: Yeah, that's interesting. I think I have mixed feelings about it because, on one hand, it is nice to commit some things to memory. And on the other hand, I'm totally cool with having a way to organize stuff so I can easily search it and find it later and not use up memory space for something that I don't use that often that then I just can't commit it. So I could definitely see it being useful. But I'm also okay with just having a nice way to search for it. But pivoting a bit and circling back to the listener question that you alluded to earlier, we have a listener question from Jen and Jen wrote in about knowledge silos across different projects. Specifically, Jen wrote in "Hello, Steph and Chris, first of all, I want to say that I love to listen to your podcast for multiple years now." That's awesome. Thank you, Jen. "I like how you both share things along your week and fill the discussion with so many useful things and findings. Our team, which consists of three pairs, is currently working on two different projects. And due to that fact, we are creating information silos. Now we are looking into ways into how we can minimize those information silos. And do you have any ideas for how we can achieve this? Some additional context, switching pairs we're unsure about as this will be difficult for the new person to get up to speed. And currently, we are thinking about having a mob review session. But of course, with those, you only get a limited overview." All right. Well, thank you, Jen, for the question. I'm excited for knowledge silos because, I'll be honest, I am guilty of this one right now. I am a bit of a knowledge silo on my current project if we're telling our truths here on the show today. CHRIS: Steph, I thought I knew you. STEPH: You know, I'm full of surprises. CHRIS: Aren't we all at various times? This really does feel like one of those core things that I associate with you, though. So it is interesting. But it's so easy to fall into this space. I think without purposeful, intentional effort, this is the natural way things will trend. It's so much easier for the person who understands a portion of a system or an entire system to take on the next piece of work for that system. And I think we can probably offer some specific advice. But to talk about it more generally, Jen, I think you've found yourself in the pretty common position of there isn't a great answer here. There's going to have to be an investment of some amount of effort; some potentially decreased productivity for a period of time in order to get out of the situation that you're in. But that's just the name of the game. So if we name it as that, and we say that, then the question becomes how much effort do we need to put towards that, and what are the different ways that we can do it? So to go through the two that you listed, mob review sessions, I think can be a great way to give an introduction to a project, but I think they'll very quickly taper off in my experience. So I think it's a great way, especially if you're going to do any more formal things after that; a mob review or even a mob overview of the system is a great way to introduce new folks into it. But then from there, I personally would think that if you are feeling pain around the knowledge silos or even if you're not, because frankly, knowledge silos can very quickly become a major problem, say if someone needs to...if someone happens to leave the company or if someone needs to take some time off, anything of that nature, this is one of those things that can be fine until it's not, and then it's not in a very serious way, and that's the wrong time to try and resolve it. So I would very much be in favor of more purposeful things. As you described, switching pairs is an interesting one. I think that's a cost you're probably going to have to pay. I am interested; the way you're talking about it, it sounds like your teams are paired up consistently, so you're working exclusively in those pairs, which frankly is a really interesting thing. I think it was the previous episode where Steph and I talked about agile and particularly 100% pairing, and that's a pretty intense idea. It also does potentially lean towards this. Now, each of those groups of people, each of those pairs is collectively aware of the same subset of the application. But now, if you were to split that up and you have six individuals that pair in varying sets across the different projects, you have this sort of Venn diagram tapestry of knowledge of the different systems and the subsets and the features. And for that reason, I actually would probably question, at least if I'm correctly interpreting it, that you have three consistent pairs; maybe you shuffle that up. Maybe that's a practice that should be unwound. And now the pair should rotate on a daily basis or something to that effect. But overall, I think this is a cost you're going to have to pay but will pay off longer term. And it's definitely worth doing in my mind. But yeah, that's some high-level thoughts. What do you think, Steph? STEPH: I agree with all of those sentiments very much. And as you're talking about the cost and investing in the team, I think that's very true and something that needs to be done. The fact that they're working in pairs is already reducing knowledge silos because you at least have another person. Because I have been part of teams where there's one person that is that knowledge silo. So at least here, we already have two people that are aware of how code works and then why code was implemented in a certain way. So then, to categorize how painful that knowledge silo is or how risky that knowledge silo is, I think there are really two ends of the spectrum. And on one side, there's that example that you alluded to a little bit ago about isolating one developer on a project for six months, and they have minimal code reviews. And then suddenly that person leaves, and that's the hardest silo to then rectify. And it will probably be a lesson that stings enough that hopefully it won't be repeated where someone gets that isolated and then others have to figure out what was going on while that person was working on something independently. And then on the other side of that spectrum is you need to take some time to explore and understand a portion of the application that you haven't worked on before, or perhaps it's you need to understand how to work with an internal API. And stuff on that side of the spectrum feels more addressable with documentation and also mob reviews. And maybe there are also demos as well because a lot of the knowledge that goes into building a product may not be specific to the code, but it's more why was this done, and why was it built, and why did we go this way? And that feels more addressable with documentation, with commit messages, with those mob review sessions, and also with demos where then you can show the high-level functionality of a feature that's being implemented. So then, even if everyone else on the team doesn't have the technical knowledge as to how it was built, they'll have more of the user context, and the product context as this is a feature that we built, and this is why it's useful to the world. I find a lot of that knowledge is what's harder to capture because then you'll find a feature and wonder who uses this and how is it in use? And that stuff is harder to backtrack. Circling back to something that Jen caught out in their question, highlighting that it takes time for someone to get up to speed. That's a really interesting one for me because it goes back to the idea of wanting to know well; what's difficult? Not specifically what is difficult, but let's define difficult and what's a reasonable level of difficultness because onboarding to any applications or onto a new section of code is always going to take some time to process and understand. But what's an acceptable timeline in which someone can onboard and be productive? There's something that I've heard from someone at thoughtbot. I don't have the exact context to quote them directly. If I find it, then I'll be sure to add it to the show notes. And they shared that another company is measuring this difficulty of onboarding by they take the person's first starting date, and then they track to see when that person has merged in 10 PRs because they are looking to see how long it took for that person to get up and running to then feel comfortable, to then make some contributions. Often, your first couple of PRs might be something that's less challenging. It might be something that's updating the README because you are going through that onboarding process. And that's a great time to then reevaluate how clear the instructions are. But by the time you get to the 10th PR, you've probably addressed something that's a bit meatier and impactful to the product. And then they use that as a metric to then calculate okay; how well are we doing? Is it a month? Is it six months until someone gets there? How complicated is the application is another way that you could look at that metric to say, "Well, if it takes people a very long time to get there, maybe it's because of the codebase versus processes." And I really like that thinking of we have knowledge silos; let's think about where it's actually hurting us. And then, if we think it's specific to the onboarding process where that part is painful, then let's break down how we can measure how difficult it is, and then look for ways to improve it but then also track that improvement. CHRIS: Well, I like that idea of trying to quantify and measure onboarding. I've heard a lot of organizations having like, "We want you to ship a PR on your first day," that's a meaningful thing. But obviously, that first one will probably be pretty small, and it's sort of getting that first one out of the way, if anything. But it's not truly representative of someone being able to comfortably work within the repo, but ten, that starts to feel like a real number. And I do like quantifying it. More generally, I'm intrigued. Metrics around developer productivity is such a difficult thing to pin down. And it can, I think, become really complicated, especially if you're looking at individuals and trying to say, "Well, you had four PRs, but you had two PRs," and comparing individuals. But I do really like the idea of more aggregate stats of on average; right now last month, we were doing 1.2 PRs per week per developer, and now we're down 2.7 PRs per week per developer, something like that, and seeing that looks like something that we might want to address. Are there fundamental things that are happening that are causing development to slow down? Are we doing bigger PRs, et cetera? And starting to look at that, but try and have a metric to keep an eye on that. So I'm super intrigued by that and then again, more specifically to the onboarding one that you were talking about there. Actually, popping up a slightly higher level, though, I think both you and I sort of jumped into this conversation as, like, yes, knowledge silos got to fix those, that's a problem. And I do feel that way. This is a topic that I feel pretty strongly about and pretty clearly about that knowledge silos are the natural state that things fall to, and it's not a good thing, and we want to avoid it. But it is important to ask the question of who is deeming this to be a problem and for what reasons? And we had a good conversation two episodes back in response to a different listener question about consulting versus building product. And I feel like, with this, we can almost go up to the consulting level of this can be a problem, but it also maybe isn't. Or, who believes it's a problem? Is it management thinking, "Oh no, when that person went on vacation, suddenly everything ground to a halt? This is a problem, and we need to resolve that." Or is it the development team themselves saying, "Hey, we feel like we're a bit siloed here, and that's a problem we're recognizing," but they don't have buy-in from management. Or worst case management saying, "This is a problem, but you get no time to resolve it." As long as everyone's in agreement of the potential benefits and aligned to this is a thing that we would want to improve, and then also aligned to there will be a cost to resolving it, that it's not free to try and unwind this siloing of knowledge, then I think everything can be great. But any mismatch at sort of any level of that either on the cost or the benefit side can be problematic. And so getting to the point where you've had a clear conversation that defines this and gets everyone to come to an idea of yes, we think it's a problem, and yes, we want to put in the effort to resolve it, then I think you can move forward and tackle any number of different approaches. But I think you have to start from that conversation. STEPH: I love asking that question of how has this manifested into a problem or a concern? Because you just highlighted a really great example where if it's only a concern because someone was on vacation and the team couldn't respond to a customer request or couldn't respond to an outage, then there are different ways to address that. So documentation may not be the best way to help out with that. That's probably a pairing session. So then someone can respond quickly to an outage versus you don't want to say, "Okay, here's a couple of pages of documentation," and then have that developer go on vacation again, and then there's an outage, and you're trying to read through those pages to figure out what's wrong. So figuring out the right approach based on the pain that's being felt feels like a really great way to go about this. Because frankly, breaking down a knowledge silo is always going to have a cost. So you want to make sure that you're being as cost-efficient as possible with your approach and then addressing the root concerns and making everybody's lives better. Because I do think there's some knowledge silo that's appropriate. And I think silo may be the wrong word, but someone who is more skilled or an expert in the area or has more context for a particular area of the application. Because applications can get so large that not everyone's going to know everything and context switching between all of those can be really challenging. So I think it's very natural that you're going to have different people that you go to around a certain feature. If there is some lofty feature around search and you know a particular person that has worked on it for a while, then you go to them, and that feels like an appropriate level of knowledge that someone has obtained. And I wouldn't classify that as a silo at that point. But then if you do get to the point where that person went on vacation and then search broke, then you can start to revisit okay, maybe this person does have too much context, and then we can offload some of that context to someone else. CHRIS: There was a phrase I used earlier of like a patchwork quilt, but I think that's not quite the right image. There's an image in my mind of little islands of color that are fully separated; that's bad. And then there's a version of more like a Venn diagram overlap where each of the colors sort of bleeds into the other ones, and I think that's good. But then the perfect overlap where it's just one big blob of brown because all the colors are the same, that's bad. And I think that's what you're highlighting is like, you don't want to go to that. You don't need the perfect overlap of everyone having a complete shared knowledge set. I'm trying to make word pictures over internet radio. So it's probably not going great, but it's something to that. Like, there is an optimization here, and I think the way to find that is by starting from what are the pain points? What are we feeling that is less than optimal? And then coming up with solutions that directly address those pain points, not generically try and target like knowledge silos bad. And retros are a perfect way to do that. So if you listen to our previous episode where we talk about the virtues of retros and other agile philosophies...This is great. I feel really good about being able to reference previous episodes. I think we've talked about good stuff in the previous episodes. STEPH: You've been on fire with this episode. I think you've referenced at least two, three episodes at this point. [chuckles] CHRIS: Yeah. Wow. Well, I mean, we're at 300 now, so we've got plenty to go back to. [laughter] STEPH: We've got plenty of content to reference. I think you and I do have an advantage here based on our experience where we have had to join a number of projects. And then we know our time with that project is very determined, and we want to make sure that we don't take any knowledge with us. So something that you and I have acquired as a skill is seeking knowledge when we first join a project and asking a lot of questions around how the application works and then understanding more about the intent of different features, and then knowing where to dive into a codebase to then make fruitful contributions. And I think there's a similar approach that can be taken when trying to break down a knowledge silo is a person who is that silo may be in a spot where they're having trouble communicating all that information and then dispersing it to others. So then us, as their teammates, can go to them and try to ask those types of questions to then help ourselves level up and then recognize areas that don't feel documented. And maybe it's adding documentation, maybe it's adding tests, or maybe it's doing a demo, maybe it's recording something about the feature and then sharing that with the team. But then you can be an advocate for that person who is in a silo position to then help them share that knowledge because they may be too far down that path where they don't recognize what they know, and other people don't. I don't know if that's directly related to being a knowledge silo but just an additional way to approach helping breaking down when you recognize that a silo does exist and looking for ways to then help that person communicate and distribute their knowledge. CHRIS: Yeah, I think you're describing a distinction between a push versus a pull. It could be incumbent upon the person who has the knowledge to try and push it out to the team. But often, they're going to be perhaps a more senior person. They've got code review to do. They've got other meetings, and planning, and things, and they just may not have the time. But is there a way that other team members can proactively pull that information from them and help them find the moments that will clarify that? So, yeah, broadly, as a team, let's rally around the desilofication of the whole adventure. STEPH: That's exactly what I was going for is that push versus pull mentality and how we can break down the silo from both sides. So thank you, Jen, for that wonderful question. I hope we gave you some helpful ideas and suggestions around addressing a silo and then also identifying the pains that you're feeling so that way you can find the most cost-effective approach. But on that note, shall we wrap up? CHRIS: Let's wrap up. STEPH: The show notes for this episode can be found at bikeshed.fm. CHRIS: This show is produced and edited by Mandy Moore. STEPH: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or a review in iTunes as it helps other people find the show. CHRIS: If you have any feedback for this or any of our other episodes, you can reach us @bikeshed on Twitter. And I'm @christoomey. STEPH: And I'm @SViccari. CHRIS: Or you can email us at hosts@bikeshed.fm. STEPH: Thanks so much for listening to The Bike Shed, and we'll see you next week. All: Byeeeeeeeee! 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._
After the last episode where database switching was discussed, a number of listeners reached out with thoughts. In particular, one listener gave a reproducible example of how to make things better. Chris talks about why he always moves errors to the left, and Steph gives a hot take where she admits that she is not a fan of hackathons and explains why. Steph and Chris also share exciting Bike Shed show news in that we now have transcripts for each episode, and tackle another listener question asking, "How do you properly implement a multi-step form in a boring Rails way?” Chris talks about his experiences with multi-step forms and gives his own hot take on refactoring: he doesn't until he feels pain! Database Switching in Dev Mode Gist (https://gist.github.com/danott/e698435bb4e1d34bc70853514ba681a7) In Relentless Pursuit of REST – Derek Prior (https://www.youtube.com/watch?v=HctYHe-YjnE) Transcript: CHRIS: Happy Friday or whatever day it happens to be in your future situation. STEPH: Happy day. [chuckles] CHRIS: Happy day or night. I'm sorry, I'm done. [laughter] STEPH: Shut up. [laughs] Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Steph Viccari. CHRIS: And I'm Chris Toomey. STEPH: And together, we're here to share a bit of what we've learned along the way. Hey, Chris, happy Friday. How's your week been? CHRIS: Happy Friday to you as well. My week's been good. It's been busy. I am taking next week off for a quick vacation. So it's that…I think I've talked about this every time before I go on a vacation on the podcast, that focusing lens that going on vacation gives you. I want to make sure everything's buttoned up and ready to hand off, and I'm not going to be blocking anyone. And so, I always like the clarity that that brings. Because a lot of times I can look at well, there are infinity things to do, how do I pick? And now I'm like, no, but really, if I'm going to be gone for a week, I must pick. And so yeah, I'm now very excited to lean into vacation mode and relax for a bit. STEPH: Yeah, that's awesome. I hear you. I always go into that same mode pre-vacation. CHRIS: But in tech news, after the most recent episode that was released where we talked about the database switching stuff, a number of listeners were very kind and reached out with some thoughts. In particular, Dan Ott is one listener who reached out not only with just some generic thoughts, but he also gave a reproducible example of how to make things slightly better. So the particular thing that a few folks honed in on was the idea that I was describing the feeling of in production; we can occasionally run into these ActiveRecord read-only errors, which is a case where you have a GET request that happens to try to create or update a record. And as a result, you're going to get this ActiveRecord read-only because you're using the follower database, which has a read-only connection. All of that is fine, but ideally, we would want to catch those before production. We want to catch them in development. And broadly, the issue that we have here is that in production, our system is running in a different way. It's running with two different database connections, one for read-only, one for writing, and that's different than in development, where we're running with a single connection. As an interesting thing, a lot of the stuff that I see on the internet is about using SQLite in development and then Postgres in production. And so that's an example of development production parity that we've really...I think thoughtbot is definitely a place that I internalize this very strongly. But you've got to have the same database, and especially because it's relatively straightforward to run Postgres locally, I'm always going to be running the same version of the database locally as in production. But in this case, I'm now getting this differentiation. And so what Dan and a handful of other folks highlighted was you can actually reproduce this functionality in development mode with a fun little trick where you end up creating a secondary connection to your development database, but you mark it as replica:true. And so, by doing that, Rails will establish a read-only connection. And then, all of the behavior that you configure for production can also be run in development. So now, as you're building out a new feature, and if you happen to implement a GET request that does some side effect in the database, that'll blow up in development as opposed to production, which is very exciting. STEPH: Yeah, that's awesome. I love that Dan reached out and shared this example with us. I actually haven't read through all of the details just yet. In fact, I just opened it up, and I started going through it, and there's a lot of really...it looks like a lot of great notes here and a really nice example that walks you through how to have that production parity locally. So this is really neat. I appreciate Dan sending this to us. CHRIS: Yeah, this is a wonderful little artifact actually that's interesting just in and of itself. We'll certainly include a link in the show notes to the gist that Dan shared. What's interesting...I think I knew of this, but I've never actually seen it before. This is a single-file Rails application, which is a very novel concept, but it's got a bundler/inline call at the very top. And then there's an inline gemfile block, and then a set of requires to pull in the relevant Rails stuff. And then it configures the database connection, configures a single controller or actually a handful of controllers, it looks like, and then it renders inline HTML. And so it has all of the pieces. And I didn't realize that at first, but then I pulled it down and I just ran it locally. So it's just Ruby and then the file that this just represents, and suddenly I had a reproducible Rails app. I believe this is used in reporting issues to Rails so you can get the minimum reproducible test case. And that's why this works is, I think, the Rails core team, over time, has pushed on any of the edges that wouldn't have worked and made it so that this is possible. But it's a really neat little thing where it's this self-contained example. And so running this file just via Ruby does all of this stuff, installs everything that's necessary. And then, you can click around in the very minimal HTML page that it provides and see the examples of the edges that it's hitting. And again, this is in development mode, so it's pushing on that. But yeah, it's both a really interesting tip as to how to work with this and a really interesting way to communicate that tip—so double points to Gryffindor, aka Dan Ott. STEPH: Double points to Gryffindor. I love it. CHRIS: I'm cool. [laughs] STEPH: That's very charming. [laughs] I've never seen anything like this either, in terms of one file that then can reproduce and run in a Rails app. Agreed, double points to Gryffindor, aka Dan. CHRIS: Aka Dan. STEPH: [chuckles] I hope Dan's a Harry Potter fan. CHRIS: I hope so. And I hope he's a Gryffindor, who knows? Maybe Ravenclaw. It's really up in the air. But the other thing that is interesting that I haven't yet figured out here is this works for development mode. I've tested it in development. It's great. I was able to remove the fix line that I had in my code where I had one of these breaking controller actions and run with this configuration in development mode. And then boom, it blew up in development, and I was like, yay, this is great. Move those errors to the left, as they say. But I realized there are some other edge cases, known ones actually. Another developer on the team mentioned something where he knows of a place that this is happening, but that code path isn't running right now just because it's a seasonal thing within the app. And I was like, oh, that's really interesting. I wish there were a way to test all the behavior. Oh, tests, that's what I need here. And so I tried to configure this in test mode, but I wasn't exactly clear on what was failing. But at a minimum, I know that the tests run in transaction, so I think that might make this more complicated because if you have two connections to the same database, but you have transactions, I feel like that might be conflating things, and it wouldn't necessarily map perfectly in. But if we could get that, that would be really great. Moving forward, any new development the development configuration will cover what I need. But retroactively, as I'm introducing the database switching to the application, it would be great if the test suite were a way to find these edge cases. So that's still an open question in my mind. But overall, the development fix is such a nice little addition to this world. And again, thank you so much to Dan for sending this in. STEPH: Yeah, I agree; having this in tests would be wonderful. I am intrigued not having read through the full example that's been provided. But I'm wondering if this is one of those we default to read-only mode, although that feels like too much because we're often creating data for each test. So maybe we default to...yeah, you have to have both because you have to have your test set up where you're going to write data. So you can't default to just being in read-only mode. But then say you want to run a controller action or something else in a read-only mode. So then you would have to change your database connection for that action, and that sounds complicated. You also said something else I'm intrigued by. You said, “Move errors to the left.” CHRIS: Yes. Now that you're asking me, I'm trying to remember the exact context. But it's the idea that there are different phases in your development and eventually getting to production life cycle, and so a bug that a user sees that's all the way to the right. That's as far along the development pipeline as it can be, and that's the worst case. You don't want a user to see a bug. So QA would be a step right before that. And if you can catch it in QA, you've moved it to the left, which is a good thing. But even better than that would be to catch it in your automated tests, and maybe even better than that would be static analysis that's running in your editor, and maybe even better than that is a type system or something like that. So the idea of moving to the left is to push those errors or when you're catching the errors closer to the point where you're actually introducing them. And that's just a general theme that I like or a Beyoncé song. STEPH: I was just going to say, all right, move over, Beyoncé. There's another phrase in town, moving to the left. [laughs] CHRIS: I'm really going for a lot of topical pop culture references today. That's what I'm about. STEPH: We've got Harry Potter, Beyoncé. We've got to pull out one more at some point. CHRIS: We'll see. I don't want to stretch myself too thin right before vacation. But yeah, thanks again to Dan and the handful of other folks that reached out either on Twitter or via email to point me in the right direction on the database switching stuff. At some point, I should definitely do a write-up on this because I've now collected together just about enough information that it feels like it's worthy of a blog post, or at least that's the story in the back of my head. I got to cross a certain threshold before I'm probably going to write a blog post. But yeah, that's a bit of what's up in my world. What's going on in your world? STEPH: I love it. You're saying write a blog post into the mic, so then that way you know it's going to encourage you to write it later. CHRIS: That's the trick right there. [chuckles] STEPH: Let's see, today's been a lovely day. It's been a lovely week in general. Today is especially lovely because it is thoughtbot's Summit, and Summit is where we all gather. We do this once a year. So the whole team, all of us across all of our...I was going to say offices but now just across all of our home offices. And we get together, and we have a day filled with events, and we usually have a wonderful team that helps organize a bunch of events that then we get together for. So a number of those fun events are like paired chats, which is one of my favorites because I often talk to people that I haven't talked to in a long time or perhaps people that I haven't even met yet that have just recently joined the company. We also have lightning talks, and I know I'm very biased, but I think we have some of the best lightning talks. They are just hilarious. So I love our lightning talks. We're also doing escape rooms. Oh, speaking of which, there's a Harry Potter-themed murder mystery that's happening. We have Nintendo Switch parties and a professional tarot card reading, which I've never done, but I'm actually doing that later today after we're done chatting. CHRIS: Wow, that is an adventurous day. And I like that it's fun, and it's connecting people and getting to know your teammates and all those nice things. STEPH: Very much. I also have a hot take. I don't know if I've shared this with you, so I'm going to share it here with you on the mic in regards to this. So previous years, for Summit, we used to have more coding projects, too. They were often opt-in, but that's something that happened. And specifically, we have Ralphapalooza, which is our hackathon. And it recently came up where a number of us were talking about Ralphapalooza, and I have come to the potentially contentious point of view that I don't like hackathons. I'm not a fan of them. CHRIS: Hot take. I like that you led in calling it a hot take, and then you provided said hot take, so I have to respond as if it's a very hot take. STEPH: That's true. Maybe it's not a hot take. Maybe people disagree. What do you think? Do you like hackathons? CHRIS: I have enjoyed them in the past. But I will say, particularly within the context of Summit or Ralphapalooza, I always felt a ton of pressure. It's so hard to right-size a project to that space, to that amount of time. You want to do something that's not trivial. You want to do something that at the end of it you're like, oh cool, I did that. Either it's like a novel thing that you're creating, or you're learning something new or whatever, but it's so hard to really do something meaningful in that amount of time. And often, people are shooting for the moon, and then they're just like, “Ah, so it's just a blank page right now. But behind the scenes, there's a machine learning algorithm that is generating the blank page. And we think with enough inputs to the model that it'll…” and it is actually super interesting work they did. But there's the wonderful pressure at the end to present, which I think is really useful. I like constraints. I like the presentations; they're always enjoyable, even in a case where it's like, this project did not go well, let's talk about that. That's even fun. But it really is so hard to get right. I've never gone to a hackathon outside of thoughtbot, so I can't speak to that, but I know that I have heard folks having a negative opinion of them. And I don't know that I'm quite at the hot take level that you are, but it's complicated, if nothing else. It's a lot of fun sometimes. I particularly remember the Elm project that you and I worked on. Well, we worked in the same group. We didn't actually work together, but same idea. That was a lot of fun. I liked that. STEPH: That's a good point. Even within the context of Ralphapalooza, our hackathons are more...I'm going to use the word sustainable because they're nine-to-five hackathons where we are showing up; we are putting in the work. There is pressure, and we do want to present. But it's not one of those stay up all night and completely leave your family for a day or two to hack on some code. [laughs] Sorry, I'm throwing some shade right now. But even with that sustainable approach, I've always felt so much pressure. I enjoyed that green space and then getting to collaborate with people I don't typically collaborate with, but it still felt like there was a lot of pressure there, especially that presentation mode always made me nervous. Even if it is welcoming to say, “Hey, this didn't go well,” that doesn't necessarily feel great to present unless you are comfortable presenting that scenario. And I also really look forward to these company events as a way to connect and have some downtime and to just relax because then the rest of our days are often more stressful. So I want more company time for me to connect with colleagues but then also feel relaxed. So I was always, in the beginning, I was like, yeah, Ralphapalooza, woo, let's go. And now I'm just like, nah, I'm good. I'd really just want a chill day with my colleagues. CHRIS: Is there an option to go for a walk with friends? Because if so, I will be taking door number two. STEPH: Cool. Well, I feel better having gotten that out into the ether now. But switching just a bit, there is something that I'm very excited about where we now have transcripts for each episode. This is something that you and I have been very excited about for a while and wanted to make happen but just weren't able to, but we now have them. And so people may have noticed them as we're adding them to the show notes. And I'm just so excited for a number of reasons, one, because there are a number of times that I have really wanted to search the shows or an episode for a particular topic and couldn't do so. So I'm just sitting there listening, trying to find a particular topic. There's also the fact that it will make the episodes more accessible. So for anyone that is hearing impaired or maybe if English isn't their first language, having it written down can make the episode more accessible. And there's the massive SEO boost that's always a win. And then I don't know if this is going to happen, but I'm excited that transcripts may help us repurpose content because there's a number of our topics that I would love to see turned into blog posts, and I think having the transcript will make that easier. CHRIS: Yeah. I'm equally super excited about the addition of transcripts, and across the line, SEO is cool, I think. Yeah, that sounds nice. Being able to reuse the content is very interesting to me because this is definitely my preferred medium. I find that I can just show up on the microphone, and it turns out I have opinions about a lot of stuff but trying to write a blog post is incredibly difficult for me. The small handful of good things that we might have collectively said over the years if we can turn those into more stuff that sounds great and honestly, just the ability to search for and find older episodes now based on like, I know we talked about inbox zero. I remember that was an episode, but I don't know which one, now that's searchable, and that's a thing that we can find. I actually still use the Upcase search for…I know I said something. I know there was a weekly iteration where I talked about some topic. And I built the search on Upcase for me as the primary user because one, I'm often referencing content on Upcase, and I want to be able to find it more easily, so I made the search. I also put a SQL injection vulnerability into the search in my first implementation so, go me. But then I got rid of it shortly after. STEPH: I love when people bring that energy of “I introduced this issue, go me,” because I find that very fun and also just very healthy in terms of we're going to make mistakes. And I have noticed a number of times at thoughtbot standup that whenever we make a mistake, or it's like, I accidentally sent out real emails on production for a job that I thought I was testing on staging. Sharing those mistakes in a very positive light is a very honest way to approach it. So I just had to comment on that because I'm a big fan of that. CHRIS: I'm glad you enjoyed my framing of it. I really enjoy that type of approach or way to communicate, although I think it is a delicate line. Like, I don't want to celebrate these sorts of things because an SQL injection vulnerability is a non-trivial thing. It shows up in tons of applications, and we need to take security seriously and all of that sort of stuff. But I think the version that I think is good for that type of thinking or communication is the psychological safety. If we're scared of admitting that we introduced a bug, that's bad. That's going to lead to worse outcomes longer term. And so having the shared communication style openness to like, yep, that happened yesterday. And there should be a certain amount of contrite in this where it's like, I feel bad that I did that. I even feel worse because when it happened, I recognized that it happened, and then I tried to exploit it in development mode to prove it to myself, and I couldn't exploit it. So I was like, I feel doubly bad as a programmer today. I both introduced a bug, and I'm not even smart enough to exploit it. But I know that an uber lead hacker out there could, and so I got to fix it. But that sort of story is part of the game. It's a delicate equilibrium, but having the ability to talk about that and having a group that can have a conversation, I do think that's very important. STEPH: Yeah, well said. I do think there's an important balance to strike there. Pivoting just a bit, we have a listener question, and this question comes from Benoit. Benoit wrote in to the show, “How do you properly implement a multi-step form in a boring Rails way?” I'm very interested in this question because I am working on a project that has a multi-step form. There are probably about maybe six, seven steps, and those steps can change based on different configurations. And our form is not implemented in a boring way at all. It's a very intricate, confusing design, I would say, which I think is fairly common when it comes to multi-step forms. I'm curious, what experience do you have with multi-step forms, and what's your general feeling with them? CHRIS: Well, I happen to be working on one right now. So generally, I don't have an oh, I got this, I know the answer. This is one of those that I'm like; I feel like each time I reinvent it a little bit. But the version that I'm working on right now is an onboarding flow. So we create a user record, which at this point I only have email associated with, and then from there, when a user lands, they need to provide a bunch of profile information, and it is a requirement. They have to fill it out. We need to have all of it before we can actually start doing the real stuff of the application. And so, the way that I've ended up modeling it is interesting. I'm going to use the word Interesting. I think I like it, but I'm not sure. So I have this model; let's call it a profile that we're going to associate with the user. And the profile has a bunch of fields: first name, last name, address, phone number, and a handful of other things. And again, I need to have these pieces of information. So I want those to be non-nullable columns. But as someone is walking through this form, I'm not going to have all the information. So there's going to be a progression. We'll get first name, then we get last name, and then we get the next piece of information. So I need a nullable storage, but I don't want to just put it into the session or something like that, which I think would be an option. So what I've done is I've introduced a secondary model. So this is a full ApplicationRecord database-backed model called partial profile. And it is almost identically the same interface as the profile, but each field is nullable. There's also a slight difference in that the profile field has an additional status column that talks about once we've gone through all of this, we can add some status and track other things. But yeah, that main difference of in the profile, everything is non-nullable, and the partial profile is nullable. So then there's a workflowy object, a command object, as I like to have in my systems these days that handles the once they've gathered all their information, turn the partial profile into a profile, send it out to an external system that does some verification and some other lookups and things like that. And then, based on the status of that, mark the status of the profile. But one of the things that I was able to do is make that transition from partial profile to full profile. I'm doing that within a transaction. So if at any point anything fails within all of this, I can roll the whole thing back, and I'll be back to only having the partial profile, which was a very important thing. I would not want to have a partial profile and a profile because that's a bad state. But a lot of this for me is about data modeling and wanting to tell truths with the database and constrain what are the valid states of my application? So one solution would be to just have a profile model that has nullable columns for all of these fields. But man, do I hate that answer. So I went what feels like an extreme take of having two fundamentally different models, but that's where it's actually working out well. I'm able to share validations across them. So as new data is added, I can conditionally validate as new things are shared, and I'm able to share that via concern in the two models. So it's progressively getting more constrained as I add data to this thing. And then, in the background, there is a single controller that skips through all of the steps and has an update action that just keeps pushing data into this partial profile until, eventually, it becomes a profile. So that's focused specifically on the data model stuff. I think there are other aspects of a more workflowy type thing in Rails, but that's our thing. What do you think, good idea, bad idea, terrible idea? STEPH: [chuckles] One, I love that you have this concrete example because I have some higher-level ideas around this particular question, but I didn't have a great example that I wanted to share. So I love that you have that, that we can talk through. I really, really like how you have found a way to represent the fact that each valid state of your application as you refer to it….so you have this concept of someone's going through the flow and their address can be nullable at this stage, but by the end of this flow, it shouldn't be nullable anymore. So you have that concept of a partial profile, and then it gets converted into a profile. I am intrigued by the fact that it's one controller because that is where I am feeling pain with the multi-step form that I'm working on where we have one very large controller that handles this entire...I'm going to call it a wizard since that's how it's referred to, and there are seven or eight different steps in this wizard. And the job of this controller is each time someone goes to a new step; this controller is trying to figure out okay; what step are you on based on the parameters that you have, based on some of the model attributes that are set? What step are you on, and what should we show you? And that has led to a very large method and then also complex, lots of conditional-based code. And instead, I would really like to flip that question around or essentially remove the what step am I on? And instead, ask what step is next? So instead, take the approach that each step of the form should have a one-to-one mapping to another controller. And that can get really hard because we're often conditioned to the idea that we should have a one-to-one correlation between each controller and an ActiveRecord model, but that's not necessarily what happens in our form. You have the concept of a partial profile versus being able to map to a full profile. So I am very much in favor of the idea of trying to map each step of the form to a controller. So that, to me, makes the code more boring. It makes it more understandable. I can see what's happening for each step. But then it's not boring in terms that it requires creativity to say, okay, I don't have a perfect ActiveRecord model that maps to this controller, but what resourceful controller can I make instead? What is the domain object that I can put here instead? Maybe it's an ActiveModel object instead. So that way, we can apply ActiveRecord-like behavior to plain old Ruby objects, or maybe it's using a form object. That way, we can still validate all the fields that the user is providing to us, but that doesn't necessarily map directly to a full profile just yet. So I really like all the things that you've said. But I am intrigued by the approach of using a single controller. How's that feeling so far? CHRIS: That part is actually feeling fine. So a couple of things you said in there stand out to me, one, where it's a very big controller. That is something that I would definitely avoid. And so, I have extracted other pieces. There is an object that I created, which at this point is just in-app models because I didn't know where else to put it, but it's called onboarding. And so the workflow that I'm trying to introduce, the resource maybe is what we would call it, is the idea of onboarding, but it's not an ActiveRecord level thing. At the ActiveRecord level, I have a profile and a partial profile, and then there's an account, and there's also a user. There are four different database level models that I want to think about. But fundamentally, from a user perspective, we're talking about onboarding. And so I have an object that is called onboarding, and it contains the logic around given the data that we have now, what step comes next? Is this a valid step? Should the user go back? Et cetera, et cetera. So that extraction is one piece that definitely makes sense. Also, thus far, mine is relatively straightforward in terms of I get data in, and I just need to update my partial profile record each time. So the update action is very straightforward. But I've done different versions of this where there are more complex things that happen. And so what I've done is basically make a splat route. So it's like onboarding/ and then the step name and that gets posted or gets put, I guess, along with everything else for the update. And so now the update says, “Well, if I'm updating for this, then handle it this way; otherwise, just update the profile record.” And so then I can extract maybe another command object that handles like, “Oh, when we're doing the address stuff, we actually have to do a little bit of a lookup and a cross-reference and some other things, but everything else is just throwing data into a database record.” And so that's another place where I would probably make an extraction, which is this specialized case of handling the update of the address is special. So I want to extract that, be able to test around that, et cetera. But fundamentally, the controller thing actually works out pretty well. The single controller with those sorts of extractions has worked out well for me. STEPH: Okay, cool. Yeah, I can see how depending on how complex your multi-step form is, having it all in one controller and then extracting those smaller objects to then handle each step makes a lot of sense and feels very friendly to read, and is very testable. For the form that I'm working in, there are enough steps and enough complexity. I'd really love to break it out. In fact, that's something that we're working on right now is taking each of those chunks, each state of the form, and introducing a controller for it. So let's say if you are filling out an appointment and we need to get your consent for something, then we actually have a consent controller that's going to handle that part, that portion of it. And I'd be intrigued for your form if things got complicated enough that it's the concept of onboarding or a wizard that leads us to having one controller because then we think of this one concept. But there are often four or five concepts that are then hiding within that general idea of an onboarding flow. So then maybe you get to the point that you have an onboarding address or something like that. So then you could break it out into something that still feels RESTful but then lets you have that very boring controller that does just enough and essentially behaves like a bi-directional linked list. So it knows, based on the route, it knows the step that it's on, and it knows where to go back, and then it knows the next step to go forward. And then that's all it's responsible for, so it doesn't have to also figure out what step am I currently on? CHRIS: I like the bi-directional link list, dropping knowledge bombs right there. STEPH: Pew-pew. CHRIS: It's interesting. I don't necessarily feel...right now; I don't feel that pressure. I feel fine with the shape of the singular controller. This is perhaps not necessarily even a good thing, but I think my bias is always to think a lot about the URL structure and really strongly embrace the user point of view. I'm going through the workflow. I don't care if I'm picking from a calendar and setting up a date versus filling in an address field or how you're storing those on the back end; that's your job, developer people. And I try as hard as I can to put myself in that mindset. And so the idea that there's this sequential thing that knows how to go back and forward and shows like, show which page we're on, that feels like it belongs in one controller in my mind, or I guess I'm fine with it being in one controller. And splitting it out feels almost more complicated in that I then need to share some of that logic across them, which is very doable by extracting some object that contains a logic of what goes back, what goes forward. But I think I like to align URL structure to how many controllers as opposed to anything else. And because I'm keeping a consistent URL structure where it's /onboarding/name /onboarding/address, and I'm stepping through in that way for all of those things, then it makes sense to me that those go to my onboardings controller. But I'm interested to see if I start to feel pain somewhere down the road because I expect this onboarding to get more complicated as time goes on. And will I bump my head on the ceiling? Probably. It seems likely. But for now, I'm liking it. STEPH: Yeah, it certainly makes sense. It's one of those areas that you want to start small and then build out as it feels reasonable. But in regard to the URLs, I'm with you, where I very much want there to be a clean, nice URL for the user to see. And then we handle out any of those details on the back end since that is our work to do. But I am still envisioning that there is a clean URL. So it may be you have an onboarding/address and then onboarding/consent, borrowing from my previous example, but then that maps to where you have an onboarding namespaced controller that is then for an address or for consent. So you don't necessarily have an object that's having to be passed along that stores the state and the next step that the person is on. But that way, you do definitively know from the route okay, I am on this step. And so then that's how you get away from that question of what step am I on? Because that's already given to us based on the URL and then the controller. So then you only have to care about validating the input that's provided on that page, but then also being able to calculate dynamically okay, if this person needs to go back, what's the previous step and if they go forward, what's the next step? CHRIS: What you're saying totally makes sense. And I'm now worried that I'm going to wake up a few days from now and look at my controllers and be like, I hate this. Why did I ever do this? I think the hesitation that I had, and this feels like a terrible reason, but in terms of what the config/routes.rb setup would be for this, it's namespace onboardings. And then within that, a bunch of singular routes and inside of that, inside that namespace, would be a bunch of singular resources so like, resource address, resource blah, blah. And I don't know why, but I don't like that. I don't like that. I don't like that. Now that I'm saying it out loud, I'm like, yeah, that actually would be a pretty clean mapping. And right now, I have implicitly what those available routes are but not explicitly. It also feels like there would be a real explosion of controllers there because there's a bunch of steps and growing in this controller or in this namespace. And they're all going to do the same thing, in my case at least, of just adding data in. But that's not a reason to not make...like, controllers are cheap; I should make controllers so, hmm. STEPH: Yeah. So I think that's the part in my mind that maps to the boring part is because we are creating controllers. There's maybe an explosion of them, and it's boring. Like, the controllers don't do very much. And then that feels a little bit wrong to us because we're like, okay, I created this controller, it does very little. So maybe I should actually group this logic somewhere else. But I think that is the heart of it and how you stay boring is where you have just that code be so simple that it almost feels wrong. CHRIS: That right there, that sound bite that we just had, that was a knowledge bomb drop, and I liked it. Now I've got to go back and refactor to the form that you're talking about because I am sold. STEPH: Oh, I'm glad you like it. I am intrigued if you do refactor then what that would look like and how it feels. But I also totally understand you're busy, so if you don't, that's cool too, no pressure. CHRIS: My honest answer is that I almost certainly won't refactor until I feel the pain. It's one of those things where like, okay, maybe I've now decided that this code is not the best, but the time to refactor it isn't when that code is just humming along working fine. It's a general thing that I think we share in terms of how we think about it. But the preemptive refactoring, I guess broadly speaking, I'm not a fan of preemptive factoring. I'm a fan of refactoring just in time or as we're feeling the pain, which is the counterpoint to that is let's not extract tech debt tickets because then they turn into preemptive refactoring again. It's like, ah, I'm not really feeling...I'm not in there right now. But the version of the code that I have now is probably fine. I don't think it's a problem although I am convinced now of the boring way. I want to go back to the boring way, but it will feel like it's worth changing down the road when I feel any pressure in that system or need to revisit it. So it's like that. That's how I think about that sort of thing. STEPH: Yeah, I wholeheartedly agree. It's one of those if you refactor...if this is a side project, if you want to refactor just for testing new software theories and then reflecting on what that new refactor looks like, that's awesome. In terms of any other refactors, then I wholeheartedly favor waiting until you feel that pain and it feels like the right thing to do; otherwise, it's unnecessary code turn. And while I strongly believe in experiments, I don't believe in putting teams through those personal experiments. CHRIS: More hot takes from Steph. I like it. STEPH: Circling back just a bit and talking about having one controller for each step of the form, that part I struggle with it frankly because it is hard to think about this is a concept, but what do I call this? Because it doesn't necessarily map to something necessarily in my database. There's a really great talk by Derek Prior that's called In Relentless Pursuit of REST, where Derek does a great job of providing some inspiration around how to create routes that don't necessarily feel like they could be RESTful, or maybe they're following that more RPC format. And he does a great job of then turning around and saying, “Well, this is how we could think about, or this is how we could shift our thinking in turning this into a more RESTful route.” So then it does map to something that's meaningful in our domain. Because we have thoughtfully, or likely very thoughtfully, grouped this form together in a meaningful way to the user. So then that's inspiration right there to give us a way to name this thing because we are showing it to the user in a meaningful way. So then that means we can also give it a meaningful name. That's all I got on multi-step forms. [laughs] CHRIS: That feels like it was a lot. We've covered data models. We've covered controller structures. We fundamentally reoriented my thinking on the matter. I feel like we covered it. STEPH: Yeah, I agree. Well, Benoit, thank you for sending in this question. I hope you found our discussion very helpful. And on that note, shall we wrap up? CHRIS: Let's wrap up STEPH: The show notes for this episode can be found at bikeshed.fm. CHRIS: This show is produced and edited by Mandy Moore. STEPH: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or review in iTunes as it helps other people find the show. CHRIS: If you have any feedback for this or any of our other episodes, you can reach us at @bikeshed on Twitter. And I'm @christoomey. STEPH: And I'm at @SViccari. CHRIS: Or you can email us at hosts@bikeshed.fm. Thanks so much for listening to The Bike Shed, and we'll see you next week. All: Byeeeee. Announcer: This podcast was brought to you by thoughtbot. thoughtbot is your expert design and development partner. Let's make your product and team a success.
On this week's episode Steph and Chris discuss some of characteristics and behaviors they've observed in high-performing teams, touching on pull request sizing and prioritizing code review, deploy cadence, error monitoring and response, and minimizing the number of themes being tackled by the team in parallel. They also touch on moving to Netlify and simplifying deploys, an odd edge case with 303 vs 302 status code, and the quirks of the ActiveRecord or method. This episode is brought to you by ScoutAPM (https://scoutapm.com/bikeshed). Give Scout a try for free today and Scout will donate $5 to the open source project of your choice when you deploy Netlify (https://www.netlify.com/) Netlify build plugins (https://www.netlify.com/products/build/plugins/) Git LFS (https://git-lfs.github.com/) Issue opened on Inertia for 302 vs 303s (https://github.com/inertiajs/inertia/issues/419) "Scoping .or clauses with common joins" post (https://thoughtbot.com/blog/scoping-or-clauses-with-common-joins) Derek Prior's Building a Culture of Code Review (https://www.youtube.com/watch?v=PJjmw9TRB7s) Charity Majors (https://twitter.com/mipsytipsy?lang=en) Honeycomb.io (https://www.honeycomb.io/) Become a Sponsor (https://thoughtbot.com/sponsorship) of The Bike Shed!
On this week's episode, Chris and Steph discuss troubleshooting a race condition, trusting your intuition and pessimistic locks. They also touch briefly on TailWind CSS before diving deep into first impressions of Inertia.js. This episode is brought to you by ExpressVPN (https://www.expressvpn.com/bikeshed). Click through to get three months for free. ActiveRecord::Locking::Pessimistic#with_lock (https://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html#method-i-with_lock) Derek Prior's blog post - "Validation, Database Constraint, or Both?" (https://thoughtbot.com/blog/validation-database-constraint-or-both) TailWind CSS (https://tailwindcss.com/) Inertia.js (https://inertiajs.com/)
Show DescriptionDerek Prior is an engineering manager at GitHub. Working to improve developer productivity, he and the team have delivered such hits as: Suggested Changes, Free Private Repos, Community Contributors, and GitHub Sponsors. Prior to that, Derek was a Development Director at thoughtbot, and co-host of The Bike Shed podcast. The Bike Shed has got to be one of my favorite podcasts out there, and I was super excited at the opportunity of catching up with Derek. Our conversation spanned all sorts of topics I've been curious about, and I came away from this session with a whole new perspective on management. On the show, we walk through his experiences learned transitioning from engineer to manager, the unexpected challenges that came along the way, and helpful advice for building a healthy and productive team. Time Jumps07:47 Give away your legos.15:55 Day as an engineering manager.20:04 A player-coach doesn't scale.24:07 Unexpected challenges.32:16 An onboarding ramp.35:28 Connecting, when remote.42:48 Implementing a strong code review culture.47:35 The ultimate decision on what to do lies with the person deploying the code.49:26 Purpose, direction, and motivation.For expanded show notes on this episode, head on over to https://www.dayasadev.com/episodes/empathetic-management-with-derek-prior.
On this very special Bike Shed, Steph and Chris celebrate reaching the 200th episode. They discuss the origins of the show and thank some of the wonderful folks who helped make it happen (thanks Derek, Sean, Amanda, Laila, and of course Thom!). They discuss Chris's recent trip to RailsConf and some strategies for making the most of conference attendance. Also, Steph's recent work hosting an intro to web development course. They wrap things up with a series of questions captured live from RailsConf at the community meetup covering career growth, naming, graphql, joy, and more. Sandi & Derek's Rules - The Bike Shed's first episode, from Oct 31 2014. New Podcast Hosts! Derek Prior Sean Griffin Laila Winner Amanda Adams (Amanda Hill at the time) Intercom Pacman rule - Eric Holscher Girl Develop It Women Who Code "What happens when you type google.com into your browser's address box and press enter?" Atom Neocities Netlify Heroku Minneapolis Sculpture Garden Dependabot Semisonic - Sculpture Garden
Derek Prior, engineering manager at GitHub, joins Sam to talk about what they've been working on these days. Derek's doing GitHub projects that can't talk about. But, he did mention that GitHub recently shipped small-scope changes to its issue templates. Sam dives deep into details about building a Ruby auto formatter and not wanting to be the sole dictator of style for the rest of Ruby for all time. Feedback from others through a request for comments (RFC) process is valuable for adoption and implementation. For the goodness of the community, get them to tell you all their style opinions! Then, everyone can look forward to using Sam's auto formatter. Derek Prior on GitHub Derek Prior on Twitter GitHub Issue Templates GitHub Actions Rust Justin Searls Hash Rockets are good actually Thoughtbot Eric Hodel Noel Rappin's Podcast The Yak Shave on Twitter Sam Phippen on Twitter Sean Griffin on Twitter
Yak Shave - Episode 8 This episode was recorded on October 21st 2018. We thought it best to get this to you, even though it's a little stale :) Remember that dark, scary time in October 2018 when GitHub went down? Sean is joined by Derek once again to discuss what they've been up to. Sean not only experienced frustration with GitHub's downtime, but Crates.io's dependency on it. There's been a lot of heated discussions in the community about Crates.io's policies, or lack thereof. Specifically, focus has been put on crate name squatting - the practice of uploading an empty crate to reserve the name. Sean wants to prevent or eliminate such disruptive and malicious behaviors and actions that force action to be taken and policies to be revised or created. Do not disrupt service to make a point. It wastes everybody's time! Sean had a stressful week and knows he will not always agree with users. But, in the future, he promises to remain engaged and responsive. Fortunately, Derek had a good week - thanks to the Suggested Changes feature he released for GitHub. Derek Prior on GitHub Derek Prior on Twitter GitHub Universe GitHub's Suggested Changes Feature Crates.io Rust Belt Rust GitHub Actions Sam Phippen on Twitter Sean Griffin on Twitter The Yak Shave on Twitter
Derek Prior and I take time to chat about how he worked with his team to release the new free private repos at GitHub, our experiences as Engineering Managers, and our love for working remotely. Show Notes Making Work Visible Book
On this episode of the Bike Shed Chris is joined by Derek Prior, former thoughtbotter and previous host of this very podcast. Derek has recently moved on from thoughtbot to try out a new role as an engineering manager at GitHub. During their conversation they talk about Derek's experience shipping the "Suggested Changes" feature on github.com, and the MVP process Derek brought to the planning and development of the feature. They also touch on the architecture of GitHub and where services and monoliths fit in the world of larger systems like GitHub. Lastly they discuss Chris & Derek's respective transitions into more roles with a bit less code and a bit more management. As usual, this one has a little bit of everything! Suggested Changes feature GitHub Universe GitHub Actions Project Papercuts at GitHub Are Services the New Rewrite Bike Shed Episode GitHub Scientist Be Plucky Manager Training
This week Sean is joined by former cohost Derek Prior. After a brief reflection on the end of The Bike Shed, we discuss WebAssembly and what it means for the future of the web as well as native sandboxing. Finally, we catch up on what Derek has been doing since leaving The Bike Shed. Corrections from this episode: Emscripten and the LLVM backend were mixed up. Emscripten is not part of LLVM, and Rust moved from Emscripten to the LLVM backend. Virtually every time Sean said "unprivileged" he meant to say "privileged" Show notes: WebAssembly Asm.js Parcel Meltdown and Spectre Lua Rust Elm Exploring how Elm can target WebAssembly Shopify Careers
An ORM that's a pleasure to use with raw SQL when needed? Sean discusses how that can be. Plus, Derek shares a new and exciting way for migrations to break! Diesel v1.3.0 RailsConf 2018: Up And Down Again: A Migration's Tale by Derek Prior ActiveSupport::Inflector
Chris Toomey joins Derek to talk about their shared experience in Elm and their excitement about GraphQL. Chris on Twitter The Reader Monad — Part 1 Kind (type theory) Monads are like burritos Scala.js GraphQL | A query language for your API Tell Me When it Closes RailsConf 2017: In Relentless Pursuit of REST by Derek Prior Jobs at thoughtbot graphiql
Panel: Charles Max Wood Guest: Derek Prior This week on My Ruby Story, Charles talks to Derek Prior. Derek speaks at conferences more often and is now a development director at Thought Bot. He first got into programming when was 7 or 8 when he got an Apple IIGS for Christmas and he started messing around with writing basic code. This really got him interested in how video games and systems worked behind the scenes and led to his interest in programming. In high school, he took programming classes and found he was actually good at it and decided to pursue programming in college. Once he was hired at Thought Bot, his developing skills really took off and he has been there for almost five years now. In particular, we dive pretty deep on: Derek intro Being a good developer doesn’t mean you’re a good developer manager How did you first get into programming? Apple IIGS as first computer Prodigy and AOL Visual Basic Basic and Pascal How did you get into being a full-time developer? IBM and Microsoft internships in college Learned Java in college How did you find Ruby? Ruby on Rails How he found Thought Bot Development skills took off at Thought Bot Likes to stay at a job for a long time How do you evaluate companies to see if you want to work there? How do you create the right environment at work? The importance of empowering employees Use company values to decide upon tough decisions And much, much more! Links: Ruby Thought Bot Visual Basic Pascal Java Rails @DerekPrior Derek’s GitHub Picks: Charles Ruby Dev Summit Ruby Hack Derek Base.cs podcast Lucky Framework The Weekend Woodworker
Panel: Charles Max Wood Guest: Derek Prior This week on My Ruby Story, Charles talks to Derek Prior. Derek speaks at conferences more often and is now a development director at Thought Bot. He first got into programming when was 7 or 8 when he got an Apple IIGS for Christmas and he started messing around with writing basic code. This really got him interested in how video games and systems worked behind the scenes and led to his interest in programming. In high school, he took programming classes and found he was actually good at it and decided to pursue programming in college. Once he was hired at Thought Bot, his developing skills really took off and he has been there for almost five years now. In particular, we dive pretty deep on: Derek intro Being a good developer doesn’t mean you’re a good developer manager How did you first get into programming? Apple IIGS as first computer Prodigy and AOL Visual Basic Basic and Pascal How did you get into being a full-time developer? IBM and Microsoft internships in college Learned Java in college How did you find Ruby? Ruby on Rails How he found Thought Bot Development skills took off at Thought Bot Likes to stay at a job for a long time How do you evaluate companies to see if you want to work there? How do you create the right environment at work? The importance of empowering employees Use company values to decide upon tough decisions And much, much more! Links: Ruby Thought Bot Visual Basic Pascal Java Rails @DerekPrior Derek’s GitHub Picks: Charles Ruby Dev Summit Ruby Hack Derek Base.cs podcast Lucky Framework The Weekend Woodworker
Panel: Charles Max Wood Guest: Derek Prior This week on My Ruby Story, Charles talks to Derek Prior. Derek speaks at conferences more often and is now a development director at Thought Bot. He first got into programming when was 7 or 8 when he got an Apple IIGS for Christmas and he started messing around with writing basic code. This really got him interested in how video games and systems worked behind the scenes and led to his interest in programming. In high school, he took programming classes and found he was actually good at it and decided to pursue programming in college. Once he was hired at Thought Bot, his developing skills really took off and he has been there for almost five years now. In particular, we dive pretty deep on: Derek intro Being a good developer doesn’t mean you’re a good developer manager How did you first get into programming? Apple IIGS as first computer Prodigy and AOL Visual Basic Basic and Pascal How did you get into being a full-time developer? IBM and Microsoft internships in college Learned Java in college How did you find Ruby? Ruby on Rails How he found Thought Bot Development skills took off at Thought Bot Likes to stay at a job for a long time How do you evaluate companies to see if you want to work there? How do you create the right environment at work? The importance of empowering employees Use company values to decide upon tough decisions And much, much more! Links: Ruby Thought Bot Visual Basic Pascal Java Rails @DerekPrior Derek’s GitHub Picks: Charles Ruby Dev Summit Ruby Hack Derek Base.cs podcast Lucky Framework The Weekend Woodworker
Derrick switches domains from getdrip.com to drip.co, begins to update the design of the Drip widget, and tries to find balance in his varying roles as CTO. Ben hands off the reigns of Upcase, switches to a low-tech project management solution, and mixes work and personal todos. Upcase FormKeep Drip Implementing a Strong Code-Review Culture- Derek Prior Seeking Wisdom Podcast 16Personalities Momentum Chrome Extension
In this episode, Adam talks to Derek Prior of thoughtbot about building web applications in Elixir with Phoenix, and how it compares to building an application in Ruby on Rails. Sponsors: Laracasts, use coupon code FULLSTACK2016 for 50% off your first month Rollbar, sign up at https://rollbar.com/fullstackradio to try their Bootstrap Plan free for 90 days Links: thoughtbot on Elixir and Phoenix The Bikeshed Podcast Exercism.io Elixir, the functional programming language Phoenix, the Elixir web framework Ecto, the Phoenix database layer Programming Elixir book by Dave Thomas Programming Phoenix book by Chris McCord, Bruce Tate, and José Valim Phoenix is not Rails by Chris McCord The Bikeshed Episode 54, where Derek talks about dates and times in Elixir Pact, the Elixir dependency injection library by Blake Williams Elixir on Slack Elixir Sips screencasts
Don’t miss out! Check out Angular Remote Conf! 02: 10 - Will Buck Introduction Twitter GitHub AngularMN 02:57 - Membership & Attendance 04:48 - Starting a Group Dinners Code Katas Coworking 08:35 - Networking with Other Groups and Organizers 09:38 - Corporate Sponsors 10:35 - Prizes & Giveaways Amazing Prize-O-Tron JetBrains Frontend Masters Pluralsight O’Reilly Media egghead.io 13:54 - Advice for Creating Meetups Content Fishbowls Katas & Hacknights Social Hours Sponsorship Advertising Meetup.com Google Groups 19:47 - Topics & Speakers Hack Nights Best Practices Beginner Topics Lightning Talks Karaoke 27:11 - Getting Started in Rural Areas Remote Hangouts Nomad JavaScript 29:31 - Beginner Stories Ruby Rogues Episode #216: Code Review Culture with Derek Prior Arrogance vs Confidence Impostor Syndrome Scott Hanselman: I'm a phony. Are you? 39:04 - Land Grab Your Social Media Slack Extras Adventures in Angular Episode #44: Visual Studio Code with Erich Gamma and Chris Dias Picks Galactic Civilizations III (Joe) Legendary Encounters: An Alien Deck Building Game (Joe) Good Mythical Morning Podcast (Katya) Coin (John) [Pluralsight] Introducing Visual Studio Code by John Papa (John) Angular Remote Conf (Chuck) Mastermind Groups (Chuck) Midwest JS YouTube Channel (Will) Last Week Tonight with John Oliver (Will) Heroes of the Storm (Will)
Don’t miss out! Check out Angular Remote Conf! 02: 10 - Will Buck Introduction Twitter GitHub AngularMN 02:57 - Membership & Attendance 04:48 - Starting a Group Dinners Code Katas Coworking 08:35 - Networking with Other Groups and Organizers 09:38 - Corporate Sponsors 10:35 - Prizes & Giveaways Amazing Prize-O-Tron JetBrains Frontend Masters Pluralsight O’Reilly Media egghead.io 13:54 - Advice for Creating Meetups Content Fishbowls Katas & Hacknights Social Hours Sponsorship Advertising Meetup.com Google Groups 19:47 - Topics & Speakers Hack Nights Best Practices Beginner Topics Lightning Talks Karaoke 27:11 - Getting Started in Rural Areas Remote Hangouts Nomad JavaScript 29:31 - Beginner Stories Ruby Rogues Episode #216: Code Review Culture with Derek Prior Arrogance vs Confidence Impostor Syndrome Scott Hanselman: I'm a phony. Are you? 39:04 - Land Grab Your Social Media Slack Extras Adventures in Angular Episode #44: Visual Studio Code with Erich Gamma and Chris Dias Picks Galactic Civilizations III (Joe) Legendary Encounters: An Alien Deck Building Game (Joe) Good Mythical Morning Podcast (Katya) Coin (John) [Pluralsight] Introducing Visual Studio Code by John Papa (John) Angular Remote Conf (Chuck) Mastermind Groups (Chuck) Midwest JS YouTube Channel (Will) Last Week Tonight with John Oliver (Will) Heroes of the Storm (Will)
Don’t miss out! Check out Angular Remote Conf! 02: 10 - Will Buck Introduction Twitter GitHub AngularMN 02:57 - Membership & Attendance 04:48 - Starting a Group Dinners Code Katas Coworking 08:35 - Networking with Other Groups and Organizers 09:38 - Corporate Sponsors 10:35 - Prizes & Giveaways Amazing Prize-O-Tron JetBrains Frontend Masters Pluralsight O’Reilly Media egghead.io 13:54 - Advice for Creating Meetups Content Fishbowls Katas & Hacknights Social Hours Sponsorship Advertising Meetup.com Google Groups 19:47 - Topics & Speakers Hack Nights Best Practices Beginner Topics Lightning Talks Karaoke 27:11 - Getting Started in Rural Areas Remote Hangouts Nomad JavaScript 29:31 - Beginner Stories Ruby Rogues Episode #216: Code Review Culture with Derek Prior Arrogance vs Confidence Impostor Syndrome Scott Hanselman: I'm a phony. Are you? 39:04 - Land Grab Your Social Media Slack Extras Adventures in Angular Episode #44: Visual Studio Code with Erich Gamma and Chris Dias Picks Galactic Civilizations III (Joe) Legendary Encounters: An Alien Deck Building Game (Joe) Good Mythical Morning Podcast (Katya) Coin (John) [Pluralsight] Introducing Visual Studio Code by John Papa (John) Angular Remote Conf (Chuck) Mastermind Groups (Chuck) Midwest JS YouTube Channel (Will) Last Week Tonight with John Oliver (Will) Heroes of the Storm (Will)
03:42 - Derek Prior Introduction Twitter GitHub Blog thoughtbot @thoughtbot thoughtbot Code Review Guides The Bike Shed Podcast @_bikeshed 04:01 - Code Reviews Derek Prior: Implementing a Strong Code-Review Culture @ RailsConf 2015 Slides 05:14 - What happens when you don’t do code reviews? 06:30 - Not Emphasizing Code Quality, Setting Code Review Up for Failure Edge Cases Diverse Feedback, Team Conflict 10:43 - Code Reviewing Yourself: Answering Your Own Questions 12:03 - The Evolution of Code Review (Code Review as an Asynchronous Process) 14:51 - Small Changes, “Pull Request Bombs” Handling Architectural Disagreements and Discussions Improving the Design of Existing Code by Martin Fowler (with Kent Beck, John Brant, William Opdyke, and Don Roberts) 23:49 - Making Code Review a Supportive Process Stop Issuing Commands; Ask Probing Questions DON’T Use “Why didn’t you ________?” DO Use “Have you considered _________?” or “That’s interesting…I might have used _______.” 30:32 - What qualities should reviewees have? 34:27 - Getting Code Reviews Introduced Into Company Culture 38:30 - Making Sure Code Reviews Get Done 40:47 - Tagging Specific Team Members LGTM = Looks Good To Me Gerrit 44:39 - Other Handy Code Review Tools Style Guides rubocop JSHint sass-lint Hound repo Code Climate 47:49 - Code Review Feedback Resources for Solo Programmers exercism.io pairprogramwith.me CodeNewbie Ruby Monday JavaScript Tuesday Python Thursday Picks Code Newbie Podcast: Sandi Metz Part I (Saron) Code Newbie Podcast: Sandi Metz Part II (Saron) If Google Were A Guy (Saron) LEGO Ideas - Lovelace & Babbage (Coraline) CoverMyMeds is offering Ruby on Rails training for experienced developers (David) CoverMyMeds Billboard 1 (David) CoverMyMeds Billboard 2 (David) The Bike Shed Podcast (Derek) The Ember RFC Process (Derek) tota11y (Derek) Eileen Uchitelle: How to Performance @ GoRuCo 2015 (Derek) Olympus SP-100EE (Avdi)
03:42 - Derek Prior Introduction Twitter GitHub Blog thoughtbot @thoughtbot thoughtbot Code Review Guides The Bike Shed Podcast @_bikeshed 04:01 - Code Reviews Derek Prior: Implementing a Strong Code-Review Culture @ RailsConf 2015 Slides 05:14 - What happens when you don’t do code reviews? 06:30 - Not Emphasizing Code Quality, Setting Code Review Up for Failure Edge Cases Diverse Feedback, Team Conflict 10:43 - Code Reviewing Yourself: Answering Your Own Questions 12:03 - The Evolution of Code Review (Code Review as an Asynchronous Process) 14:51 - Small Changes, “Pull Request Bombs” Handling Architectural Disagreements and Discussions Improving the Design of Existing Code by Martin Fowler (with Kent Beck, John Brant, William Opdyke, and Don Roberts) 23:49 - Making Code Review a Supportive Process Stop Issuing Commands; Ask Probing Questions DON’T Use “Why didn’t you ________?” DO Use “Have you considered _________?” or “That’s interesting…I might have used _______.” 30:32 - What qualities should reviewees have? 34:27 - Getting Code Reviews Introduced Into Company Culture 38:30 - Making Sure Code Reviews Get Done 40:47 - Tagging Specific Team Members LGTM = Looks Good To Me Gerrit 44:39 - Other Handy Code Review Tools Style Guides rubocop JSHint sass-lint Hound repo Code Climate 47:49 - Code Review Feedback Resources for Solo Programmers exercism.io pairprogramwith.me CodeNewbie Ruby Monday JavaScript Tuesday Python Thursday Picks Code Newbie Podcast: Sandi Metz Part I (Saron) Code Newbie Podcast: Sandi Metz Part II (Saron) If Google Were A Guy (Saron) LEGO Ideas - Lovelace & Babbage (Coraline) CoverMyMeds is offering Ruby on Rails training for experienced developers (David) CoverMyMeds Billboard 1 (David) CoverMyMeds Billboard 2 (David) The Bike Shed Podcast (Derek) The Ember RFC Process (Derek) tota11y (Derek) Eileen Uchitelle: How to Performance @ GoRuCo 2015 (Derek) Olympus SP-100EE (Avdi)
03:42 - Derek Prior Introduction Twitter GitHub Blog thoughtbot @thoughtbot thoughtbot Code Review Guides The Bike Shed Podcast @_bikeshed 04:01 - Code Reviews Derek Prior: Implementing a Strong Code-Review Culture @ RailsConf 2015 Slides 05:14 - What happens when you don’t do code reviews? 06:30 - Not Emphasizing Code Quality, Setting Code Review Up for Failure Edge Cases Diverse Feedback, Team Conflict 10:43 - Code Reviewing Yourself: Answering Your Own Questions 12:03 - The Evolution of Code Review (Code Review as an Asynchronous Process) 14:51 - Small Changes, “Pull Request Bombs” Handling Architectural Disagreements and Discussions Improving the Design of Existing Code by Martin Fowler (with Kent Beck, John Brant, William Opdyke, and Don Roberts) 23:49 - Making Code Review a Supportive Process Stop Issuing Commands; Ask Probing Questions DON’T Use “Why didn’t you ________?” DO Use “Have you considered _________?” or “That’s interesting…I might have used _______.” 30:32 - What qualities should reviewees have? 34:27 - Getting Code Reviews Introduced Into Company Culture 38:30 - Making Sure Code Reviews Get Done 40:47 - Tagging Specific Team Members LGTM = Looks Good To Me Gerrit 44:39 - Other Handy Code Review Tools Style Guides rubocop JSHint sass-lint Hound repo Code Climate 47:49 - Code Review Feedback Resources for Solo Programmers exercism.io pairprogramwith.me CodeNewbie Ruby Monday JavaScript Tuesday Python Thursday Picks Code Newbie Podcast: Sandi Metz Part I (Saron) Code Newbie Podcast: Sandi Metz Part II (Saron) If Google Were A Guy (Saron) LEGO Ideas - Lovelace & Babbage (Coraline) CoverMyMeds is offering Ruby on Rails training for experienced developers (David) CoverMyMeds Billboard 1 (David) CoverMyMeds Billboard 2 (David) The Bike Shed Podcast (Derek) The Ember RFC Process (Derek) tota11y (Derek) Eileen Uchitelle: How to Performance @ GoRuCo 2015 (Derek) Olympus SP-100EE (Avdi)
Ben talks with fellow thoughtbotter Derek Prior on conference speaking, code review, and podcasting. Implementing a Strong Code-Review Culture- Derek's RailsConf 2015 Talk How to Talk to Developers Giant Robots on Genius The Bike Shed Podcast Derek on Twitter