ALTERNATE UNIVERSE DEV

The Bike Shed

371: The "Fundamentals"

Joël has been fighting autoloading in a Rails app recently, and it's been really unpleasant. Stephanie has been experimenting with how she interacts with Slack.

What are "the fundamentals"? People often argue for the value of Computer Science classes for the jobbing programmer because we need "the fundamentals." But what are they? And does CS really provide that for us?

This episode is brought to you by Airbrake. Visit Frictionless error monitoring and performance insight for your app stack.

Transcript:

AD:

thoughtbot is thrilled to announce our own incubator launching this year. If you are a non-technical founding team with a business idea that involves a web or mobile app, we encourage you to apply for our eight-week program.

We'll help you move forward with confidence in your team, your product vision, and a roadmap for getting you there. Learn more and apply at tbot.io/incubator.

STEPHANIE: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Stephanie Minn.

JOËL: And I'm Joël Quenneville. And together, we're here to share a bit of what we've learned along the way.

STEPHANIE: So, Joël, what's new in your world?

JOËL: I have been fighting autoloading in a Rails app recently, and it's been really unpleasant. This is an older Rails 4 application, so we don't have Zeitwerk, any of the fancy modern things. But the problem I'm encountering is that people write code that references a constant somewhere. And if that constant is not named in the conventional way so that it would load from the proper file, it will raise a NameError at runtime when you try to execute the code.

And then you want to reference that constant with a big asterisk there because if anybody else has happened to have loaded that constant correctly either by a manual require or some other method, then that constant will already be in scope, and so you don't get a NameError. So it causes a situation where you have a lot of non-deterministic failures in the code that are not easy to always reproduce either locally or even in the test suite.

STEPHANIE: That sounds really frustrating because you must be getting errors left and right that you weren't expecting and then have to deal with. I'm curious, though, because you use the word non-deterministic. But in some ways, I'm thinking that you could perhaps grep or search the codebase for places where we're requiring constants like that and perhaps even audit that. Has that been something you've thought about, or do you think that's possible at all?

JOËL: I don't think just grepping is going to be good enough because it's anytime you use a constant, and that's a class name, a module name. Things like that are probably the most common cases. If you're just referencing a constant like an array or a string, it's probably defined in the same file, so you're probably good. But if you're trying to include a module, or inherit from another class, or you want to instantiate another class somewhere, then you can run into issues if the class name or the namespacing for it doesn't line up with the file name so that when Rails tries to autoload it, it doesn't find it where it expects.

STEPHANIE: Got it. Okay, that makes more sense to me now.

JOËL: When I interviewed at thoughtbot, one of the questions that I was asked, and I don't know if that's still in our interview anymore, was, "Tell us about one of your favorite features in Ruby." And then, "If you could remove one or change one thing about the language, what would you change?" And I think the goal of that was to see if people had enough expertise in the language to both have something they really liked about it but also know the warts; what are the places that are hard to work with?

And I definitely know that I said enumerable is my favorite part of Ruby because enumerable is amazing. I feel like, at the time, I didn't have a great answer for what I would change, but I don't remember what I said. I think today, if I had to answer that question, I might say the global namespace for all constants where just because you load another file, it might change what constants are available in a way that can really lead to some surprising behavior sometimes.

STEPHANIE: It's really funny that you mentioned enumerable because I think, at this point, you've been at thoughtbot for almost a decade, and you recently gave a talk about the enumerable module. [laughs] And so it sounds like that's something that's still one of your favorite and beloved features of Ruby. So that's really fun.

I also agree that autoloading is very opaque to me, especially, like you mentioned earlier; things can be totally different depending on what Rails version you're running. And it sounds like, ideally, when it works, it works. And hopefully, someone has done the legwork of making it effective for you. But when something goes wrong, then something that you had kind of taken for granted prior becomes really hairy.

JOËL: I think for those who are interested in digging into really deep how autoloading works in Rails and how it's sort of changed over time, there was a keynote at RailsConf last year that dug into that. That is an excellent talk to listen to.

STEPHANIE: Yes, that keynote from RailsConf 2022 about how Zeitwerk was developed by Xavier Noria, Xavier with an X. I was there too. That was a really awesome keynote. And I found it really interesting because, again, it was about this whole aspect that I just took for granted and had never really thought about. And I'm glad that someone else [laughs] figured it out for me. So that was a great reference.

Speaking of conference talks, in prior episodes, we had mentioned the talks that you and I gave at RubConf Mini back in November, and the videos for those talks are out. So if you want to check out Joël's talk about enumerable or my talks about pair programming and non-violent communication, we'll link the videos of those talks in the show notes.

JOËL: Excellent. So, what's new in your world, Stephanie?

STEPHANIE: I've been experimenting with how I interact with Slack. So I used to be very distracted by Slack as someone who needs to mark everything as read in order for the little red badge to go away or for the bold channel name to become unbold. I was constantly clicking around in Slack whenever I had it open for the sake of completing that task of reading messages, even if I wasn't necessarily in a space to fully read or even had time to be spending distracted on Slack.

But naturally, I would like, oh, click on this channel because it's bold, so I've unread messages. And then I'd get sucked in and be like, oh, I totally lost like five minutes of my time [laughs] and have forgotten what I was doing prior. So I started experimenting with using Slack as an inbox instead, so more of a pull than push in terms of receiving notifications. And I think it's been working well for me.

I've also been leaning on Slack's native keyboard shortcuts instead of using a mouse to interact with the Slack client because that helps me avoid that distracted clicking or going into this channel just to see what's up, and that has also been just okay. I think their keyboard navigation is not my favorite. There are no customization options.

So at one point, the shortcut to close the thread window pane was conflicting with my 1Password keyboard shortcuts, so I had to change my 1Password situation. And whenever you have to learn keyboard shortcuts for something different and in ways that might clash with your regular muscle memory for other applications, it's kind of annoying. But that's my journey with using Slack mostly on the keyboard so far.

JOËL: What kind of impact have you seen on your focus since you've been using this workflow?

STEPHANIE: I think it's been helpful for me to tune out things that I just can't prioritize my time and energy to at the moment. So I'm also pretty decisive when it comes to muting and leaving channels. I'm not in a ton of fun, casual channels because, again, I find them a little bit distracting. If I do want to go see cute dog content, I will go into the pets channel. But it's easier for me to have that be an intentional decision that I'm making as opposed to, oh, look, there are more messages in the dog channel. [laughs] Let me go check them out now.

I think it has helped me focus my time and energy on the things that are most important to me. And the trade-off there is that I missed out on some content, but I think that I've become okay with that. And the channels that I am more subscribed to, like our dev channel that we've mentioned on the podcast before any project or team-related communications those, are top of mind for me. And when I do need a little bit of a break and do need some fun banter, I will hop into other channels for that.

JOËL: So you brought up a little bit of this idea of FOMO around Slack channels. I think there's an area where our industry at large has a lot of FOMO, and this is around the computer science degree. A lot of people that are in the industry do not have one. There are a lot of different paths that can come into becoming a developer. Some people are entirely self-taught. Some people have gone through a bootcamp. Some people have kind of transferred from other similar or not at all similar industries. So there are a lot of different journeys that people have.

But for many people, if you don't have that, there is some FOMO around, “Did I miss out on something?” And there's a word that people always kind of toss around when talking about computer science and specifically the things you might be missing out on if you don't have it, and that is the fundamentals. You might be missing out on the fundamentals or, oh, well, what if I don't know the fundamentals, then I'm faced with a problem? And I won't know what to do, or it might make me learn more slowly. Is that something that you've heard thrown around?

STEPHANIE: I agree that the word fundamentals is extremely vague. In fact, I'm just going to say it: I have no idea what most people mean when they say the fundamentals, or at least I think they could mean a lot of different things. And so when that term is used, maybe I should just be asking for clarification [laughs] because I think that we could be talking about a lot of different things. Before we get into what fundamentals of programming or computer science or whatever means to each of us, Joël, do you want to share a little bit about where you're coming from in terms of any education prior to becoming a developer?

JOËL: Sure. So I do have a CS degree. I actually learned to code before college. I read some books, did some tutorials on the internet, played around with some code on my own, had a lot of side projects, and even at some point was, freelancing a few small projects as well. But I always struggled trying to build projects larger than a certain size. I felt like they would sort of implode under the weight of their own complexity.

I definitely felt like there's got to be some underlying fundamentals that I'm missing, some theory of writing code that would explain how to structure things in a way that scales. And this is not scaling to millions of users; this is scaling beyond 100, 200 lines of code, maintainability. And so, I had really high hopes for a computer science degree. And honestly, I think I was a little bit disappointed. I learned a lot of other interesting theoretical things, but not a lot that was actually answering that underlying problem.

And I didn't really get the answers I was looking for until I started working more in the industry, doing various internships and then later on full-time jobs as well. And just over the years, that has sort of built up a lot of answers to those questions that I had. But I didn't necessarily find them in my computer science degree, so I have mixed feelings.

STEPHANIE: I had no idea that you were doing that level of coding prior to studying in college. That's really cool. Now that I'm thinking about it, I think that's the case for a lot of people. They might get a taste of coding when they're young. It was true for me. I was a young girl on the internet in the early days writing HTML and CSS on neopets.com. And that is also how I got my first taste, and kind of wanted to explore further.

So, in college, I studied journalism actually. That was where my interests were at the time. But I did take some computer science courses on the side and ended up completing it as a minor. But I'm with you that the education I got didn't quite match up with what I was expecting. In fact, I kind of struggled, I think, because there weren't a lot of more relatable applications to what I was learning, and so I was very bored and disengaged, I think.

And so when I came out of college, I didn't think I wanted to do software development, or programming, or anything like that because I didn't love taking those classes. And, I don't know, I'm going to get into my whole career history today. But I basically fell into the role of development. It was kind of like, oh, you have these coding skills; we need these coding skills. I was like, okay, I guess this is my job now. [laughs] And I think you and I are actually kind of similar in the sense that once you started doing the work, you started to see a lot of the things that you had learned previously that you could then apply. Does that sound right?

JOËL: Yes. And I think maybe more so over the years, there are some things where it's like, oh, with a five-year mark, it's like, oh, finally now I feel like I've got enough practical experience where I can start to appreciate some of these underlying things, or I'm getting into things beyond just the basics of writing a simple Rails app where I start to need some of those other concepts. And some of those it's five years, some of those are ten years. And so it's sometimes nice to have something to go back to. Although after ten years, there's only so much I remember, so sometimes it's just having a keyword that I can Google and dig into further.

MID-ROLL AD:

Debugging errors can be a developer’s worst nightmare...but it doesn’t have to be. Airbrake is an award-winning error monitoring, performance, and deployment tracking tool created by developers for developers that can actually help cut your debugging time in half.

So why do developers love Airbrake? It has all of the information that web developers need to monitor their application - including error management, performance insights, and deploy tracking!

Airbrake’s debugging tool catches all of your project errors, intelligently groups them, and points you to the issue in the code so you can quickly fix the bug before customers are impacted.

In addition to stellar error monitoring, Airbrake’s lightweight APM helps developers to track the performance and availability of their application through metrics like HTTP requests, response times, error occurrences, and user satisfaction.

Finally, Airbrake Deploy Tracking helps developers track trends, fix bad deploys, and improve code quality.

Since 2008, Airbrake has been a staple in the Ruby community and has grown to cover all major programming languages. Airbrake seamlessly integrates with your favorite apps to include modern features like single sign-on and SDK-based installation. From testing to production, Airbrake notifiers have your back.

Your time is valuable, so why waste it combing through logs, waiting for user reports, or retrofitting other tools to monitor your application? You literally have nothing to lose. Head on over to airbrake.io/try/bikeshed to create your FREE developer account today!

JOËL: So earlier, you mentioned that fundamentals is a bit of a weasel word; it means different things to different people. So I'm curious for you, what do you think of when you think of fundamentals And, maybe more specifically, from the perspective of a web developer?

STEPHANIE: Yeah, I want to caveat this by saying that I think you can learn these different skills in many different ways; that could be formal education, that could be a bootcamp, that could be self-taught. But these three categories are what I think are useful education for a web developer.

So firstly, understanding how computer systems work at the abstraction you're using and maybe even one level lower. So this is likely the programming language or framework and the tools that you're using, and I think a lot of bootcamps teach this. They want to teach you the skills you need to get a job and do that job.

The second category, I would say, is more theoretical. So the theories of computer science and math that you and I alluded to earlier as not having been super practical at the time that we learned them. I guess that's probably why they're called theories. [laughs] But I'm thinking like algorithms, data structures, and other concepts like O notation or whatever.

And then thirdly, this one, I think, doesn't get talked about enough. But there's this whole world of practical skills that we do in the industry that I don't quite think are taught in either environment, so that looks like reading code and reviewing code, especially as it relates to working in an existing application as well as writing tests and documentation, and, in general, working with other people. I think a lot of programming education focuses on the act of writing code when I think there's a lot to learn from reading code and analyzing it. And that's something that I have been thinking a lot about the more I spend doing it in my job.

JOËL: I like the way you've sort of broken these down into more relatable categories rather than just this generic idea of the fundamentals. I think when people think of the fundamentals, they're probably thinking mostly of your category two, the more theoretical underpinnings. Some of those are actually quite relevant, I think, to the day-to-day work that we do. And then some of them are very kind of abstract and maybe even to the point of mostly being relevant if you're doing research but not that interesting if you're writing code on a day to day. Does that sound about right to you?

STEPHANIE: That's fair. I revisited a series of blog posts and conference talks that my friend Mercedes Bernard gave called "Fun, Friendly Computer Science," that was aimed towards people who didn't have a formal CS background to give them a vocabulary or some exposure to computer science concepts that would be helpful in day to day programming work. And one thing that stood out to me was the idea of set theory and how working with relational databases is pretty much working with set theory, even if you don't use that vocabulary or reference those underlying concepts.

I think another aspect of these theoretical fundamentals that companies might interview people on, like algorithms or data structures, there's also a lot of talk about how those aren't part of your day-to-day jobs. And yes, knowing about them is useful, but the benefits of working in an existing programming language is that those have all been figured out for you. And you are just using those data structures and have to worry a bit less about how they are working under the hood.

That's not to say you should know nothing about them, but I think I had mentioned earlier maybe it's helpful to understand things at a lower level than what you're working with. And that can come up when you run into problems that those data structures aren't quite able to help you solve, and you need something different, or you need to be a little more creative. But I would say that almost all of the time, you have the luxury of not having to think about that.

JOËL: It's interesting you mentioned set theory because I was thinking of a set theory metaphor here, which is we have a set of things, which is what a traditional computer science degree will teach you. We have a set of things that are the quote, unquote, "fundamentals." And there's definitely an intersection between those two sets, but they don't totally overlap over each other.

And so there are some things in a computer science degree that are absolutely going to be fundamentals that you need to use and that you're going to use in your day-to-day job. You don't have to learn them through CS. You can learn them from all sorts of other sources. And then there are also some things in CS that are maybe not part of the fundamentals.

All knowledge has value. And so these things can be mental models, or they can connect to other things. But they're not necessarily fundamental to you being able to do a good job as a working developer out in the world. And again, this will vary a lot depending on the type of development that you're doing. I'm mostly thinking of web development because that's what you and I do, both front end and back end.

I want to come back to something that you mentioned earlier because I feel like when everybody thinks of the fundamentals of computer science, the first thing that comes to mind are data structures and algorithms. Those are the things that you're working on when you're doing leet code. They are the kind of things you get asked on interviews. It's the kind of thing that's kind of fun to show off to other people. And it sounds like you're saying that data structures and algorithms are actually kind of overrated; that's a hot take.

STEPHANIE: I think it's overrated if you are working in web development. Like we were talking about earlier, at that level of abstraction, you're using the tools of the language to build software that works for your users. Like, I am really not thinking all that much about how to implement a linked list or something like that. [laughs] I have my trusty hash in Ruby. I can use an array if I need to and just put my data in there, and that is perfectly fine and acceptable. And I have not really needed to do anything too fancy beyond that.

In fact, I think you and I have talked a lot on this podcast about paradigms and design patterns. And those are the things that I find really interesting and want to learn more about at this point in my career and because they're more relevant to my day-to-day work. And I think we should be interviewed on the work that we will be doing.

JOËL: I think that lines up a lot with my experience as well. I have had to implement some trees, some linked lists, and things occasionally throughout time. I think especially working in Elm, sometimes is a little bit more lower-level data structures to work with or to construct. And sometimes you might need that to know some basics around things like trees if you're operating over the DOM, which is a tree, things like that. But, again, a lot of those things are already pre-built for you. So having the 10-minute version might be good enough to get what you need to do.

I think one thing that's probably the most useful thing that I would pull out of an algorithms class is the concept of a binary search, not just literally how do I implement a binary search on an array or a linked list but the idea of it and then taking that and applying it to a very broad set of problems. And a classic one is when you're debugging, and there are all sorts of ways that your program might fail. And if you are looking at it just process of elimination, just one little thing at a time, it's going to take you forever to check every possible cause.

But if you can find a way to eliminate half of the possibilities, and that might be by putting a conditional high in your decision tree, or there are a lot of different ways you can do that, all of a sudden, it makes it much easier to narrow down your search and to find a bug. And so that is a technique that I think is just hugely valuable that you learn in an algorithms class, but that can be generalized to all sorts of problems. I'm curious, in writing in Ruby, or JavaScript, or any of the web languages that we tend to write in, have you ever had to calculate the big O of a method or function you've written?

STEPHANIE: Only in the context of performance and Rails performance. The only times I think I've ever really pulled it out is when I am seeing a database query that is O of n or worse, and then I rewrite the function to avoid that inefficiency. Otherwise, I think most functions are perfectly fine, and there's no need to really optimize for that the first time around.

Though, I am going to plug another conference talk that I watched recently from Jemma Issroff and Jacob Evelyn about their developing of a gem called MemoWise which involves memoization and caching. But they did a really cool job of deep diving into the source code of their gem to make things as efficient as possible. And that did involve investigating different O notations and stuff like that.
111
JOËL: Yeah, I found that in practice, most performance bottlenecks on the web tend to be I/O bound rather than CPU bound. I just realized I threw out some fancy technical terms that you probably would learn in a CS degree, and that might feel confusing for those who don't have that background. So that means that your problem is slow. It's waiting on, usually, some sort of network or file system, or database, or something like that rather than waiting for processing speed.

As an aside, we've talked about the value of having specialized vocabulary and names to add to problems. So that is a value that you get out of a more formal education path is that you do learn some of those technical terms. And that can sometimes help you to build a mental model of the solutions you can apply to a problem.

STEPHANIE: I might have mentioned it in that episode, but I do think I learned best, having had to wrestle with something in my personal life and experience and then going out and seeking more information about it and learning about it. And at that point, it's much more interesting to me because I can relate it to something that is in front of me as opposed to reading a textbook and trying to imagine ways that this information would be useful.

A lot of these concepts, it's totally okay to go explore them once you need them. You're right that it is tough if you have no idea where to even begin or what to search for, or what to look for. But I don't know; I think maybe I'm just being efficient with my time this way. [laughs]

JOËL: I'd like to throw a metaphor at you that you kind of introduced earlier when you were talking about Slack and how you're trying to change from a push to a pull mode, and I think this can apply to learning as well. Any sort of push approach to learning, you're kind of pre-learning some things because you think they're going to be useful or they might power some more learning. And it's going to be good to have that sort of already there in the situation where you need it.

Then that might be going to do a four-year degree, or maybe that's just saying, this year, I want to learn a little bit of this theoretical idea to build some better understanding of the quote, unquote, "fundamentals." And that could just be sort of general continuing education that you do. But sometimes, like you said, it's just in time. You say until I encounter a problem, it's like, okay, this problem is slow. I don't know a whole lot about performance. Let me go read up about performance.

And then you get to be like, oh, the first question you ask is, is your problem I/O bound or CPU bound? What does that mean? Okay, now there are different strategies for how you deal with things and different analysis tools. And then you go and learn that at that moment rather than having learned it the summer before because you just were trying to fill out a sort of broader foundation for your knowledge.

STEPHANIE: Wow. Excellent callback. Again, only you can find multiple ways to reference something [laughs] I said earlier. I also really enjoy listening to someone who's an expert at something or particularly knowledgeable talk about something that they're excited about. And so I was thinking that I don't have as robust of a computer science education as some of my peers or coworkers, but I know that I have people to go to with my problems.

Like you, for example, you might pull out, oh, this reminds me of graph theory. [laughs] I know we've talked about dependency graphs a lot on this podcast. And, in some ways, I am absorbing that education through you. And maybe in the future, I will encounter something that reminds me of a conversation we had, and I have a starting place. So I think having people with diverse backgrounds in this field can be really valuable as well.

JOËL: I love that because that means that even in your day-to-day, there's kind of a sort of mix of push and pull that's happening. You might just be having a conversation with somebody, and they're really excited about dependency graphs, and they tell you why they're excited about it. And that's maybe a little bit more of a push because you don't immediately need it, but you're gathering some knowledge.

But then you might also be encountering a problem on a client, and then you ask in our dev channel, "Hey, I'm encountering this sort of problem. What should I do?" And somebody says, "Oh, you might want to look into calculating the big O of this function because that looks suspicious. Tell me about that."

STEPHANIE: Exactly. And now it's my turn to call back to my Slack anecdote earlier because I do think in this field, there's just an infinite amount of things to learn. And I do have to accept that I'm not going to learn everything. And I have found a way that works for me, you know, that combination of, oh, here's a problem I'm facing.

And I really need to find out what is going on in this C code so I can better understand this Ruby code I'm writing or something like that. Or people sharing different insights that they have, and I'm getting that information that way. And you said it earlier that however you receive this information or get this education, there's no one way to do it. There's no one correct way.

JOËL: And I think everybody does a mix of both, right? You've mentioned several times that you had attended a conference talk, or read a book, or read an article on a topic related to more theoretical underpinnings. And I'm pretty sure you weren't going to that talk because you had a problem that needed to be solved, and you're like, oh, if only I could get the answer, this is where I'll get it. It's probably a little bit more in preparation, saying, oh, I'm at a conference. The whole point of a conference is to get some information ahead of time. And this particular topic sounds like it would be helpful. Does that sound right?

STEPHANIE: Yeah, that is a really great way of putting it. I hadn't thought of it that way. But that also kind of checks off the box of listening to someone else explain things to me [laughs] that they've already done a lot of research on and feel excited enough to share with the world. And that is inherently more interesting to me than reading a textbook.

JOËL: Oh yeah. Textbooks are boring. We'd done a whole episode recently about where to focus our learning. So I think if listeners are interested in digging deeper into that and maybe the push versus the pull, there are a lot of great thoughts there as well.

STEPHANIE: So before we wrap up, are there any underrated quote, unquote, "fundamental" computer science topics or concepts that you think are particularly valuable to you and your work as a developer?

JOËL: I would like to plug discrete math as a topic. And I know we've talked about, oh, there are some theoretical ideas that are maybe very firmly in the theoretical realm and aren't that useful in day-to-day work, and math sounds like it would be in that branch. But discrete math is basically all the practical math that is useful to you as a developer. It's kind of a mishmash of 10 different subjects.

So it's a bit of an overview of here's an intro to Boolean algebra. Here's an intro to propositional logic. Here's an intro to predicate logic. Let's talk about set theory. Let's talk about combinatorics. Let's talk about recursive functions from a mathematical perspective. Let's talk about a little bit of graph theory. So it just touches on a bunch of these topics, and they're all generally quite useful.

I find things like Boolean algebra I use absolutely every day because writing Boolean expressions is a thing that we do all the time in our code. And you might think there's not that much to doing Boolean expressions. And you might even have picked up on some of the patterns by yourself just by doing the work long enough. But there are some really interesting laws and rules that can be applied and analysis techniques that, even in just the small portion of a course dedicated to that topic, you get a lot of value out of it.

So I would recommend either digging into some of these topics a little bit on your own, so digging a little bit into Boolean algebra, digging a little bit into set theory, or digging into discrete math as a whole, sort of looking at all the different little sections together. I think that really gives you a lot of tools to improve your day-to-day work.

STEPHANIE: That was a great sell on discrete math. And who knows? Maybe you've influenced some of our listeners to go check that out.

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: Thank you so much for listening to The Bike Shed, and we'll see you next week.

ALL: Byeeeeeeeeee!!!!!!!!!

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.

Sponsored By:

Support The Bike Shed

Episode source