This week Chris talks about Bifunctor optics and introduces an app he's been liking recently called CleanShot X, which is a replacement for the built-in screenshot utilities on OSX.
Steph talks about her experience using New Relic Browser Stats to troubleshoot a slow page and burnout. Who's feeling it? (Raise your hand.) How do we identify it? What do we do about it?
CHRIS: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. And we're off the rails already, everybody. It's going to be a good one. 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, how's your week going?
STEPH: Hey, Chris, it's going really well. We talked recently that I have a new laptop. So I have been migrating the things that I'm accustomed to over to my new laptop, but I also love that clean, fresh start. So as part of that fresh start, I was like, what if I use Safari? What if I just switch? I'm a Chrome user, for the record. I'm pretty sure you know that, but just to share that. I was like, well, what if I just switched and I try out Safari for a while? So that was the thing.
CHRIS: So I heard the words try and the phrase that was the thing, but I'm going to probe a little deeper. How'd that go? Was it good, great, not so great?
STEPH: Honestly, it was fine. I did enjoy being in a new environment to see how Safari handles bookmarks and then also the inspector. So it was novel to be in a different browser where I really don't spend much time in a different browser other than when I need to test this specific UI bug or things like that.
But the reason that I ended up migrating back to Chrome was frankly for Chrome profiles because I really like that I can have this clear separation now between my work life and my personal life, and then it also keeps me signed in. So my personal email versus my thoughtbot one versus before the Chrome profiles, which I'm not sure how recent of an addition that is where Chrome introduced that feature. But before, I just always had to be signed into both, and it was just all together in one spot. But now I really like that I can separate. And it's more intentional where I'm like, oh, I'm going into work mode, so I just want that profile versus I do need to hop over to my personal side for a while. So that was the thing that brought me back.
CHRIS: Interesting. I don't take advantage of that at all. I know of the feature, but it's never really called to me. And if anything, I do the opposite. So specifically, this doesn't work in the browser, but on my phone, I use the iOS Gmail client, and I use the unified inbox. So I just have everything come together. And I subscribe to the idea of, I don't know, it's all work and stuff. And hopefully, people aren't sending me a lot on the weekends, and I will defer and snooze and all of that. But that holistic view pulls me in. And so it's interesting that you're just on the other side of that. It totally makes sense. I actually think I'm wrong here. I think I'm doing the wrong, bad thing. But it's interesting just the way we're on the two sides of that.
STEPH: I can see the merits for your approach where it all goes to one place. So you have one place to go and triage, and I think that makes total sense. I haven't triaged my personal life well enough that I want it to come into my work life. And that's the one that needs the more immediate response typically. So I want to prioritize all of my work emails and focus on that and then have my personal ones more like, okay, I've got some time, and I want to check on this. But I don't want to blend those together. Because frankly, I need to do some more triage on the personal side if I'm just going to bring it all into one space.
CHRIS: Interesting. Yeah, I would almost view it from the other point of view of I want to protect my personal space, and this is obviously not what I do based on what I just said but protect my personal time so that when it's evenings or weekends or whatever, that I'm not seeing work emails in there. I do my best to snooze them and get them out of the way. But if they are coming in, maybe there's something I need to respond to or, I don't know, maybe it's FOMO in a certain way, FOMO but professional FOMO. I don't really know. It's interesting that that's the feature that brought you back. But overall, how was your experience using Safari?
I have heard loosely that now most of the browsers are evergreen; even Edge has really caught up and is now implementing features in a similar way. And so Chrome, Firefox, and Edge are very similar. And my understanding is Safari is the one that actually lags behind or even holds back web standards and implementations and things like that. So, did you find any rough edges of that sort, or was it otherwise just fine, and it was mostly the profile stuff that made you switch?
STEPH: It was honestly just fine. I also may have not used it long enough to run into any of those rough edges. But overall, it was just fine. It worked. I just got to that point where I've run into this situation before where I'm signed into my personal email, and then I'm signed into my work email. And then I'm going to Google Hangouts, and Google Hangouts gets confused, and it's like, which person are you? And then I have that moment of where I have to sign out of one, or sometimes it just gets complicated. And what I found with profiles is that that's just never an issue. I don't have to worry about it anymore.
So yeah, overall, Safari was fine. I wouldn't mind going back to using it. I just really like the profile feature. This was one of those moments where it helped me notice how much I really liked that feature because I had just opted into it a while back. But this was that moment where I was like, oh yeah, I really miss that. So I'm going to go back to it.
CHRIS: The thing you said a minute ago about which person am I? [chuckles] There's a deep philosophical underpinning there, but I've definitely struggled with that and trying to trick the browser into it. So Trello is the one that I'm struggling with that right now. I am one human in the world, I assure you. But GitHub gets this; GitHub plays the game correctly where I'm one human, but I have multiple internet identities. I am the work person. I am the open-source person. And I'm able to route notifications and things to the different inboxes based on the organization that they're part of, et cetera. And I really liked that, that GitHub seems to understand that I am a human that is multifaceted, whereas Trello does not. And so I use Trello in a professional context a lot, but it's my personal...like, I would have to create a distinct account on Trello. And I'm like, Trello, that's not true; I'm still one person. Just understand this Trello organization is a separate facet of my existence. Got on my soapbox on that part.
The other thing I want to say is I do feel bad about the fact that I'm just on Chrome. Because increasingly, Chrome has got so much of the market share, and it's becoming the new deeply dominant thing. And so I want to be the agent of change or like, no, we should use different browsers, and we should support them and make sure that we're testing against them and all of that. And then I'm just on Chrome all the time, and I feel bad about it. But it's one of those like; I have so much muscle memory and built-up knowledge around how to use Chrome. And I've just used it for so long now that the switching cost would be pretty high, I assume. I actually haven't even really tried, but I feel bad about it. I'm now saying two things which are I feel bad about it, and I've never even really tried. So I don't feel great in this moment, but these are my truths.
STEPH: [laughs] Well, I can plus-one your truths. Those resonate with me. Well, there's always stuff that I am trying that's new all the time. So I feel like I need some constant in my life until I'm ready for other things to be the constant in my life. And then I can muck around with which browser I'm using and change other things. And you have to be in that moment. You have to be ready for it.
So going back to a thing that you said a minute ago about separating your work life and your personal life, I very much like that framing, and that's a nice segue, frankly. And it's something that I've been thinking about where you and I often start with technical topics. But I have a very people-centric topic that I'd love to chat with you about today, and it is emphasis on burnout. And who's feeling it? How do we identify it? What do we do about it?
And that's been very much on my mind because I have noticed a lot of people around me, including myself; I just feel like we are more inclined to experience burnout right now or are going through it actively. And it feels even more important to have those conversations with each other and with ourselves to talk about what does it look like when we're burned out? How do we recognize when we're there? Because often, when we are burned out, it's not something that happens gradually, or at least it's not something that we notice happening gradually. It's like, okay, I'm fine. And then suddenly, okay, I'm burned out. And I'm at a place where then I can't really focus. I feel overwhelmed. I'm drained.
For anyone that is less familiar with burnout, one, hooray, and then two, burnout is a state of emotional, physical, and mental exhaustion that can be caused by excessive and prolonged stress. So then that's what leads to us feeling very overwhelmed, emotionally drained, unable to meet constant demands, or those are the things that are causing our burnout.
So I have been doing what I typically do when I'm thinking about a particular topic, and then I'm looking to gather knowledge and information is I try to go pretty wide where I start looking for podcasts, books, just people that are having similar conversations and trying to synthesize a lot of the information that they're sharing. And I have found some really good stuff. The question is now, what does one do once one has that content, and then how do you bring it back and help apply that content to yourself and then people that are around you? So I have some thoughts there, but before I go further, I'm curious, what's your experience with burnout?
CHRIS: I think for me, I've been somewhat lucky in that I don't think I've ever really got into an acute period of burnout, but I've definitely had periods where I just felt weighed down. And there are ebbs and flows of life, and of work, all of those things. There is something that I've been thinking about recently, which is the inherent nature of the work that we do where consistently we're working on something where we don't quite know how to do it, and we're struggling, and we're struggling. And finally, we figure out how to do the thing, how to trick the computer into doing what we want. And then the minute we do that, in trying to encode, ideally, we're automating that away. And we take that solved problem, and we just ship it off. And then, we pick up the next unsolved problem from the list. This isn't entirely true, but it feels like sometimes unspecificity in sort of I'm just working on things that are underdefined, underspecified, and I'm trying to solve little puzzles constantly. And, I don't know, I was feeling that recently of the nature of the work was burning me out.
And I think earlier on in my career; I definitely experienced this in a certain way. And then, in the middle of my career, there was this perfect inflection point of my skills and the level of tasks that I was going for. But then, the further I got into my career, the more I tend to take the weird underspecified stuff and anything that's relatively clear I'm giving to other folks on the team. I'm like, "Oh, here's this well-defined piece of work here. Can you go implement this admin page?" Whereas I am doing the investigate integrating with third-party platform XYZ that uses a SOAP API that isn't documented. I'm like, okay, cool. Let me roll up my sleeves and figure out what that means. And I noticed in my work that that was starting to weigh on me, and I was able to shake that off and shift around some of the tasks that I was doing.
But that was a particular form where the work itself was weighing on me. And I took a step back, and I was like, why do we do the things that we do as developers? Because there's something just fundamental like, you have to enjoy that nature of challenge and constantly escalating challenge to a certain degree, I think, to really like this work, but it can be a lot sometimes. So I feel like maybe that's a slight digression from the topic, but it was a thing that I was feeling in this space. And that's a little bit of my story.
STEPH: Well, the beautiful thing about that is it highlights everybody experiences burnout differently. So that could really be how someone is experiencing burnout where they're taking on all these very complicated, different tasks, and they're feeling just worn down by that and that they're not able to meet demand. And they get to that point where maybe they lose their interest in tech and coding because they have pressed too hard in one direction. And so then they need to take a step back.
As for me, I've been thinking back over the last couple of months because there was once or twice where you and I had, I think a conversation here on The Bike Shed where I shared that things were okay, but I didn't feel like my normal self. I was losing some of my interest and energy for technology and coding. And I'm very fortunate; I love what I do. So the fact that I wasn't feeling that interest was a really big sign to me that something's different; something feels off. And it does vary depending on the client that I'm working with. And I think feeling that burnout then was a mix of some of those client pressures that I was feeling and that I was working perhaps too many hours as I was very interested in that client's success.
And then the other stuff was more personal because we only have, to borrow from the spoon theory, we only have so many spoons to give. And so if you have a lot going on in your personal life as well, that's going to detract from the energy that you also have to give to work. Are you familiar with the spoon theory?
CHRIS: I am not.
STEPH: I recently heard about it, and I can't stop using it now because I really like it. But it essentially...and there's a really great article that we can link to so others can read about it because I'm not going to remember exactly who came up with this theory. But the idea is that each spoon represents a unit of energy. And let's say if you start each day with only ten units of energy and you use spoons to represent that, as someone needs energy from you, maybe it's work, maybe it's a personal commitment, maybe you're dealing with a chronic illness, then you are giving a spoon away to each of those. So at some point, you're going to run out of spoons. And you want to also be mindful of who you're giving these spoons to because you are giving that energy away.
CHRIS: I definitely liked the idea of we start each day with a certain amount of energy, and different things can pull from that pool and whatnot. I'm intrigued by spoons as the unit. It just feels like a weird...I got this little bag of spoons that I walk around with, [chuckles], and I give them out throughout the day. I guess it could be anything in there, you know, objects. But, I don't know, spoons are interesting to me.
STEPH: I think it's because this person who came up with the idea was literally having a conversation with their friend in a cafe. And so that was just something that was in front of them. And they're like, oh, I can use spoons to represent. Well, we'll have to double-check the article to make sure, but I think that's why spoons became the representation.
So circling back to once you're in burnout, what do you do with it? And that is one of my questions right now. And that's what I'm trying to synthesize a lot of information around. Because once you're in that state, I don't know of a lot of great ways to help other than take time off because, at that point, you're in a crisis state. And you need to step away, and you need to find out how you can recover from having entered this state of crisis. So that feels really important to identify ways that once someone is in that state, that then we can help them. And that feels good. We can advise someone to take PTO. I still don't feel great about it in terms that then, as a manager myself, I don't really know of other helpful ways to then help someone through that period.
So then I really started thinking about the fact that once someone is in that burnout stage, frankly, it's too late. We have let someone get to that point that now they are in that crisis instead of addressing it early on. So that is the other thing that's on my mind is one, how do we help people that are already in that crisis state? But then two, how do we start identifying that someone is starting to go in that direction? And then how do we help them tell us? How do we then triage those situations? How do we prevent them from getting to that burnout state? And that's where I've also found some really good content.
And specifically, there is a podcast that I've started listening to called The Burnout Show. They essentially share their experience with burnout, and what they did about it, how they recovered from it, and then how they continue to fight it because a lot of people then still go back to the workforce. So then, once you do find a way to recover, then how do you go back to work? And there have been some really great episodes. And I'll be sure to include a link for it in the show notes.
There's one particular episode with Grant Gurewitz, who is a guest on the show. And he speaks specifically to the strategy of Three Good Pockets. And this speaks to the idea that there are many things that we can't control in our day. It could be work, family, other commitments, but we can strive for Three Good Pockets of time where we focus on something that's just for us. This is time that's reserved for you and any activity that you find restorative or joyful. And each pocket can vary in size. So perhaps that first pocket is spent just reading a few pages from a book that you're enjoying, and then the next pocket of time is spent outside or calling a friend.
And Grant also has a great suggestion around if you're worried that you'll get sidetracked and not actually step away, which I felt called out for that one because that one's definitely me. I will have good intentions, but then I won't actually take the break that I set for myself. So Grant recommends creating a list of restorative activities so that way when it is time for that break when your calendar is reminding you, then you have a list of these activities to choose from. So it makes it easier to say, okay, then I can do this for a couple of minutes, and I can truly step away from work and step away from my screen.
But especially now, when so many of us when we're sharing our workspace with our restorative space, for everybody who is still working from home or working remotely, then creating those daily breaks are incredibly important to our wellbeing. And so, it has me thinking about what restorative activities can I add to my day? How can I encourage other people to add more restorative activities to their day? So I really appreciated that advice.
And I have noticed that the idea of burnout, but not so much burnout specifically, I've been thinking of it as recovery and balance is a theme for me. And it is something that I am purposely choosing as a theme right now where I want to research and understand more of how we handle these situations and continue to make progress not just for myself but also for my team.
CHRIS: I think finding that right cadence and structure and way to reinvest in yourself and ideally gain more spoons if that is at all possible or at least defend the spoons that you have, those all feel very meaningful.
I do have a question. I'm interested in your thoughts on this. I feel like we hear about burnout a lot in our industry. I get the sense maybe that it is a more common thing. Like, I hear so many developers talking about how their dream is just to give up tech and go get a cabin and just farm in the woods or something like that. And I wonder, is it a more pervasive thing in our industry? So that's one question.
Another is just an observation that we actually do work in a wonderfully...it's an amazing industry where being a developer, there are so many jobs out there. And I don't want to discredit anyone's efforts if they're earlier on and struggling with that. But broadly speaking, it is a developer's market trying to go out there and get jobs and extremely well-compensated, as a general rule. But does that come with this inherent burnout? And if so, which I'm not sure is true, I wonder if maybe we're just more vocal and maybe we actually share more in public. We have more blogs and podcasts and things like that. And that's just a common thing for developers, and so we hear the stories more often, whereas maybe in other industries, it is actually very common, but people are suffering in silence.
But also I do wonder, our industry is still so young. The work that we're doing is changing constantly, and that churn and that working in the unknown maybe there is an inherent nature. So that's a bunch of pontifications off the top of my head. And I have no idea what the answer to any of them is. But I am intrigued because it does feel like the shape of burnout as a concept in the developer world is perhaps a little overrepresented, or maybe it shows up more than I would expect. And, I don't know, is the work that hard? I don't know. But then I hear these stories constantly, and I definitely have felt it myself, so maybe.
STEPH: Yeah, maybe. Yes, I do think the work is that hard for the record. It's challenging work. I enjoy it, but it is challenging work between figuring out the tech but then also everything else that comes with that.
I don't have anything to back this up, but I suspect that a lot of other industries are also experiencing burnout. And I just happen to be more aware of it right now because I'm hearing it more from my friends and the people that I work with. And I suspect that's more directly related to we all just went through 2020, and probably a number of us were trying to forge ahead and get through that time. And so there may be a lot of us that are just now dealing with those consequences of where we just pushed ourselves through a very hard time. And now a lot of that is manifesting and surfacing around really identifying the damage that we may have done to ourselves by just prioritizing work and trying to put our head down and get the work done even though there was so much happening around us.
And I suspect that may be a contributing factor is that now people are really starting to recognize, like, oh, I feel this way. And maybe there's time for me to address it. Or frankly, it may not even be that there's time, but your body is just like, okay, I'm done. I made it through the past year or however many months, and I'm going to start shutting down on you. I've given you all the warning signs, but now we're here. We're at a breaking point. So I don't know about the other industries, but I do know the reason that it's more on my mind is because I'm just hearing it more from people, and they're just expressing it. And so, it has become more of a focal point for me, and I've experienced it myself more recently.
I'm sure I experienced this back early on in my career, but I took a strategy of well, I'm just a junior, and I just have to get through this. And I have to build experience. For the record, that is not a healthy mentality. I'm just being honest about where I was in my life. And so, I didn't really stop to think about it, but perhaps it is becoming more normalized where people are having more open, honest discussions about where they're at. And if other industries aren't talking about this, I would love for them to.
So to round that out a bit, this is something that is just very interesting to me. It's very top of mind. So I suspect I will be sharing a lot more content in future episodes that are just around this. How do we recover? And then how do we balance? How do we work hard without burning out?
CHRIS: Work hard, play hard; those are the two placards that you have. Well, I look forward to continued conversations on all of those topics because they are sort of that's the story that underpins all of the work that we do. So I'm very interested to chat more about that.
STEPH: Thanks. So what's going on in your world? How's your week been?
CHRIS: Oh, my week has been fine. My topics are going to be way more mundane and tech-focused. But let's see, a couple of things, so one is that Stack Overflow has their I think it's Annual Developer Survey. And this year, the results came out, and there was an interesting standout, which was that Svelte was the most beloved framework, which was very exciting to see. Granted, you always have to take these sorts of stats with a grain of salt.
But Svelte was 71% loved and 23% dreaded, which they give it as a ratio of how many people really love this thing versus how many people really hate this thing. And so Svelte, 23% of people who have used it are like, I hate that, but 71% loved, so that's a 48% net approval rating. Versus React which was 69% loved, 31% hated or dreaded as the word would be, so that's a 38% net approval. And then Vue, interestingly, was 64% loved, 36% dreaded for a 28% net approval rating. So, yeah, Svelte was decidedly winning in that.
STEPH: That's really cool. I liked how you described that as in the very early adopters’ strong fan base stage.
CHRIS: But nonetheless, the people that are using Svelte do seem to really like it; that’s coming through in these numbers. And that definitely is my experience. I love Svelte and would love to continue using it for as long as possible. But really, I want a lot of other people to start using it. I want to really grow the usage base so that there are more libraries, and frameworks, and blog posts, and just mindshare in that space because I really do believe there are some wonderful ideas in Svelte. And it's just so straightforward to implement things that I just want more people hanging out. So that's one quick thing.
Another quick thing is, I've been using a utility lately or a program called CleanShot X, which is a replacement for the built-in screenshot utilities on OSX, and it is just fantastic. So I can capture a screenshot. I can capture a window. You can capture a GIF or a video. And then you can do little trims and annotations. And then it has this really nice feature where after you take a screenshot, it just hovers in the bottom corner of your screen and is easily accessible. So if you take a video, and then you want to upload it to a Trello card, it's just floating there waiting for you. You can actually dismiss it and push it down, but it's still peeking up from the bottom of your screen, and you can pull it back up, and you can have a couple of them. But it just really makes the whole workflow of grabbing screenshots or videos so easy.
And I cared deeply about that because now that I have this tool, I'm all the more inclined to grab a screenshot or a video with just about every piece of work that I do. So it's going into pull requests; it's going into Trello cards. And it's so nice to have a utility that just really makes that as easy as possible.
STEPH: I really liked how you mentioned that you can annotate because I often...I'm laughing as I'm thinking about this. When I am taking a video of something that I'm going to share with someone, I will use my mouse to indicate, oh, this is important. And so I circle around it and do silly things with my mouse to try to indicate but being able to annotate would be so much nicer.
I know there is another tool that you're really excited about that I can't remember off the top of my head right now. Do you know the name of the tool I'm thinking of?
CHRIS: Was it Loom?
STEPH: Yes, Loom, because I also used that for a little while, and I've really enjoyed it. So I'm curious, how does Loom and CleanShot X stack up? Is one replacing the other, or are they complementary tools?
CHRIS: Mostly complimentary. Loom is great because it hosts the videos, and you can also do audio capture, although I wonder if CleanShot has that as well. CleanShot also, I think, has a hosting thing. So I think there's a strong overlap in their functionality, but right now, I'm using both. And definitely for screenshots and things, CleanShot owns that end of it. And I think it's more likely that I could have CleanShot as the entire tool that I'm using. But I'm still using Loom for this is a walkthrough where I'm going to talk to you about a thing. I want to make it available at a URL that everyone can see rather than actually getting a GIF or MOV artifact file on my computer. So ever so slightly different, but I think of them, CleanShot X is probably the ideal one. But yeah, I'm still reaching for both.
So the one other thing I did want to talk about is I have been expanding our use of the dry-monads within the project that I'm working on. And I've done some things. I did some stuff, Steph, and I think it's good.
STEPH: Shtuff with Shteph. [chuckles]
CHRIS: Shtuff with Shteph, yeah. I'm definitely pushing the envelope of how much we're leaning on these concepts within the app, and I continue to question it. I'm really intrigued to see what happens when other folks come into the project, and they're like, "Why can't I just get the value? It should be a string. Why isn't it a string? Why is it a string that I have to do a ceremony and a dance to get at?" And I'm like, "Well, because everything can fail, you know, like life."
But what I have done here so dry-monads is the project that we're using, particularly their result type. So the result represents something that can either succeed or fail. And so we either have a success, which is this wrapper around the value that's successfully executed. So say we make an API call, we get back a response. If we get a 200 or maybe even a 300, then we get the data, and that's a success, or we get a failure and the error message. But fundamentally, we're modeling that in our system in a way that downstream from that, we have to basically determine if it was success or failure. So we're really encoding into the system; listen, pretty much everything can fail, so let's be careful with that. Let's be intentional and purposeful with it.
But there is an interesting thing where these objects have fmap as a method on them. So fmap is a way to transform that wrapped value, but fmap works specifically on the success case. So if you make an API request, you get back the data. Everything's great. You can call fmap, and it will yield into a block that data. You can transform that data in some way, and then it will rewrap it up as a success object. So you can operate on this thing as if it has been successful. But in the case that it's a failure, it will just ignore that transformation because you don't want to transform the failure. It's going to be a totally different shape of data. So you want to separate those. We're getting into functors and monads here. So I'm going to handwave a bunch. But fundamentally, that's the thing that we're going for here.
But we found ourselves really wanting to work with both sides. So we make this API request, and in the case that it succeeds, we actually want to transform and actually slice out a piece of data from the nested object that we get back. So that's one transformation that we want to apply on the success portion of the aisle. But then, we also want to transform the failure message. It turns out this backend is giving us very unfriendly error messages. So we want to take those and transform them into friendlier user-facing error messages.
So it turns out we want to map both sides. And so I went to dry-monads, and I was like, what do you got? I want to know about this in the world. And it turns out they did not have anything. So I started looking into it, and it turns out this is a concept in the world of functors, specifically. Or, more specifically, I reached out to a former colleague, Sid Raval, a former thoughtboter as well. And he likes the functional programming stuff, so I knew he was the right person to ask about this. And he pointed me at bifunctors. So I found myself in a new space and category theory which I never thought I would explore a category theory in this way, but here we were.
So a bifunctor basically is exactly what I was talking about where there are these two branches. In our case, it's either success or failure, but it allows you to operate on both sides, both branches. So the method or the function that gets applied there is bimap. So it's fmap which I don't know why it's f why that's typically what it's called. Success map would be a really great word in this context in my mind. So success map only deals with the success side, but bimap takes two different transformations, one for the successful outcome and one for the failure outcome. And it allows you to very directly talk about what you want to do with that.
To be clear, dry-monads has a function called Either or Either, depending on how you want to pronounce it. And that takes two Lambda proc-type things because it's Ruby, and functions are kind of weird in Ruby. But it yields you either the successful value or the failure value, but then it doesn't rewrap them. So it's meant to be the terminal. You use that in a controller when you're either redirect or render or whatever it is you want to do. What I wanted was something for mapping, so staying in the success object or the failure object but yeah, bimaps. So I introduced my own extra wrapping layer. This is where things go off the rails, I think. We now have our own internal result objects.
I thought about monkey patching for a while. I convinced myself monkey patching was a bad idea. Now that I've implemented as an extra layer of wrapping and I got the wrapping wrong like four times, or I kept recursively wrapping and re-wrapping, and there's a reason people aren't supposed to write these things themselves. But I think monkey patching may have been a better idea here, or maybe I should have never done any of this. We ended up with a stable working implementation and a nice test suite that covers it. But I introduced bimap and failmap as two different methods on our success object. And I did it by doubly wrapping the result objects. So we have our internal result, which wraps the dry-monad result. And I'm worried about that future situation where a junior developer comes on the team and is like, "I don't know what any of this is."
STEPH: I love the weekly progression of I've done some things, and let's talk about it. And then seeing this glimpse into your argument with yourself as to yes, but we need it, and we want it, and it's not something that's defined. So let's go ahead and implement it. You ended on a high there where you talked about the fact that it is nice to work with. It does the thing that you'd like it to do, and it's well tested; I love that part.
I'm deciding which thread to go with because there were a lot of interesting bits in everything that you shared. And I'm intrigued about the monkey patching as to why you think that could have been a better approach. Could you talk more about that one?
CHRIS: Sure. So I ended up having to introduce the secondary object that then wraps the result. And so if you poke at that even a tiny bit, you start to see this like Russian doll nesting of it's our result wrapping a result, wrapping a value. And it's a burrito with three different tortillas wrapped around it. And if you want to add guacamole, you got to unwrap all three burritos. I'm sorry, this is a terrible monad joke here. [laughs]
STEPH: For the record, if Taco Bell is not offering that, they should now, now that you've created that. [laughter]
CHRIS: There is one, but there's usually cheese in between the layers. They're not just extra layers of tortillas, so that's what I've got here. It's way too much tortilla, and that's sad. You don't want that. And it's confusing. You're like, wait, does this one have two or three layers of tortilla? So when I was working on it and when I was implementing our additional wrapping layer, I tricked myself multiple times. And my test suite was telling me that things were working, but I was testing incorrectly. And I was like, oh man; this is very subtle. And even though I'm deeply immersed in the context here, I'm still struggling with this.
So the question is, did I successfully encapsulate all of this? And now anyone downstream just gets to use it,, and everything will be fine. Am I the heroic programmer that made the perfect abstraction that no one's ever going to struggle against, or was that pain that I was feeling representative of the complexity of what I'm trying to do here, and maybe I got it wrong?
And so then the monkey patching side is we've got this one layer of wrapping. What if we just monkey patch the result such that it's got these new methods? I'm not adding an additional layer. I don't need to deal with double mapping through multiple layers, and I just get to deal with the context that I have. So I did an initial spike of an implementation that way, but I talked myself out of it because #monkeypatchingisbad, but I don't know. I don't know where I'm ending here. I am happy with where we're at, but I am aware that I may be sad down the road.
STEPH: I'm just dying over here [laughs] from everything you're saying. I have this image of you staring out the window thinking, am I the hero, or am I the villain? And figuring out who you are in this scenario with this abstraction that you've created.
CHRIS: To be clear, it's raining, and I have a nice rocks glass of scotch in my hand. And I'm just wistfully looking out the window trying to determine what's true. That's pretty accurate, actually. That's pretty much what's going on here.
STEPH: I think we're just going to need updates as it progresses along.
CHRIS: I will say overall this paradigm...so failures can happen at all levels. These command objects, which are the core of where this is coming in in the application, really represent the workflows of the app in a wonderful, testable, straightforward way. So I love that part. And if I have that, it implies that I have to have this other stuff. I don't think I can get away from that.
The other thing is I've always loved Gary Bernhardt's Functional Core, Imperative Shell, which is a conference talk that he gave a while back. I talked to Gary about it actually when he was on the show, and we can link to both that and the conference talk because they are fantastic. And I just love the way he thinks about software. But that was always a little bit abstract for me. Like, what does that actually look like, though, in say, a Rails architecture? And I didn't have a great answer.
And now this thing that I'm doing is the closest I think to that where the innards of the system are almost functional even though it's Ruby. We're leaning into that. And so we have these command objects that take in some data, and then they operate on it, and they yield out these results. Did it go well, or did it not? We've got the railway-oriented stuff, which again I'll link to. I link to now every third episode, apparently, but here we are. And that just models the reality of these programs in a really great way.
And then we've been trying to introduce some, not rules per se, but guidelines as to how we interact with these things. So inside of the core of the application, we're trying to be as functional as possible. We do transformations on these result objects, but that's it. And then it's only really in the controllers or the mailers that we are doing unwrap or deal with the actual nested value, but everything else is working on conceptual values or whatever it is…these result objects. And that's actually been really nice, and it's allowed us to have really nice error handling within the app. The logging is very straightforward. A lot of apps that I've worked on in the past, I've just silently thrown away many error cases or edge cases. And this is a really great way to sequence the work that needs to be done but never throw away data that you would want. And so far, I'm finding it to be really great. And I'm seeing very obvious ways to hook into it like, oh, this is where we need to find the user-facing error message versus this is where we figure out what we want to log. And so, in some ways, it's great, but I am still open to the idea that this was a terrible idea. I always remain open to that idea to be clear. [chuckles]
STEPH: I love how that's the feedback that you're always open to it. You're always trying something new, and then you're constantly going back to revisit; was this a good idea or a bad idea?
To go back just a little bit, I do absolutely love the priority and focus that you're giving to the failure state because I feel like that is an area that we, as developers we're very skittish of that failure state. And I realize I'm projecting here. So if you're listening, feel free to take that however you like; maybe it doesn't apply to you, maybe it does. But the failure state is like you said, it's life, it's important, and it's something that is going to happen. And it is something that we should make accommodations for.
And I find that we're often very hand-wavy with a failure state. So I love, love how much you always prioritize the failure state and make that something that people can work with and understand. Versus then when something goes wrong, then that's when we have to start to understand the failure state. I recognize there's a balance there because you're not going to know the failure state until you encounter it, but there are ways that we can still optimize to have observability into that failure state for when we do encounter that failure.
CHRIS: I've definitely seen that as an evolution in my own thinking, how much am I focused on how easily can I do the core thing, the happy path versus how robustly can I do all of the variations of what this app needs to do? What if the network's down? What do we do there? I do occasionally worry that I've overcorrected on that. And it's like, you know what? This thing that I'm worried about that I'm protecting against in the application is a 0.01% edge case. It's going to affect almost no users, and we're both putting time into trying to avoid it. But also, there's code complexity that comes from trying to handle all the different variants. And so there's definitely an optimization, and I feel like, at different points in my life, I've been undercorrected or overcorrected on that.
But I think if I were to describe the arc of my career, it is desperately searching for that optimal path and trying to find exactly the right amount of error handling to apply, and then yeah, then I'll be happy, then it'll be great. But it is like when I look at DHH's classic 15-minute I'm making a blog, look how much I'm not doing, I'm like, sure, sure, sure. Show me that in three years, though. What does the blog look like? How easily can I add a new feature? What happens when there's a bug in production, and a user reports it? Can I chase it down? Can I figure it? Can I fix it? These are the questions that I care about now, almost to the exclusion of what's the first run experience like? I almost don't care about that at this point. Because I spend my time…six months and on that's where the hard work is. And so the first couple of months where you're figuring things out, that's not the hard work of this thing. And so, I'm very strongly focused on those later periods of time. But again, I'm open to the idea that maybe I am overcorrected there.
STEPH: I think it does highlight more of a shift in our career. We're still building, but we have experienced the maintenance side as well, and we felt that pain. And so that has led to perhaps overcorrecting, or maybe it's the correct amount of correction.
But I do like how you highlighted there is always a cost to each side and those are usually the questions that I'm asking myself when I'm thinking about the failure mode and how much I want to optimize for the failure mode is how much does it cost when this fails? Who's it going to impact? And how much does it cost for me to make this more observable or to address this failure state? And then I try to find the balance between those two. Because you're right, it's not free to address that failure state. And so I may not want to fully optimize to handle that if it's going to be a very small percentage of users that actually are impacted by this failure state, or it seems very rare this is going to happen. But then still finding ways to know that if it does fail, then I can say, "Okay, I'll come back, and now it's worth the investment to improve this."
CHRIS: When you said earlier that this is really hard work that we do, I don't know that I believed you. What you just described sounds super easy. You just handle all the stuff, and you dynamically optimize for the needs at the point in time. And that seems easy. [laughs]
STEPH: Super easy, yeah. [laughs] Way to bring it back. Well, speaking about observability and failure states, that does lead nicely into a bug that I was working on this past week where there was a particular page that was loading very slowly. And it was something that we'd heard from users that then they let us know that this page was either taking a very long time to load or, frankly, it was just crashing. And then they were never getting to that page. So I happened to be the one that then picked up that ticket. And I went to reproduce the issue, and sure enough, when I clicked on this particular link and then started counting, it took about 14 seconds for that page to load, which is a very long time. And then also sometimes it was just crashing.
So the first place that I went was to our error tracking. So I went to New Relic to then look to see okay; maybe there's a slow query. There's something here that's creating this performance issue, but I couldn't find anything. And New Relic does a great job of breaking down all the different response times so I can see how long Postgres is taking, Redis, and Ruby. All of those looked very normal. I couldn't find anything that seemed alarming that was indicating that the page was struggling to load even though I could reproduce the problem. Because I was clicking on it several times thinking, okay, well, if I just do this a couple of times, New Relic's going to notice, and then I'll get to see something, a little breadcrumb that's going to lead me in the right direction.
And while I was waiting for New Relic to surface something helpful to me, I mentioned to another developer the issue that I was triaging. And they said, "Yeah, that page has been getting progressively slower, and we don't know why." And I thought, ooh, okay, I'm intrigued even more now as this is something that has been escalating over time, and now we've hit this threshold that we're working on it.
And I discovered that in New Relic, I can look specifically at Postgres, Redis, Ruby, all those different response times. But there's a browser monitoring tool that I had not used before. And it showed a lot of helpful information around first paint, First Contentful Paint, window load, all of those areas. So I started diving in and found session tracing, and it was there that then I saw New Relic was telling me, "Hey, you have a page that's taking about 14 to 15 seconds to load." And I thought, okay, I feel validated now that at least New Relic is recognizing this issue. I have seen this issue, but I still didn't know why it's occurring.
So the next tool that I used that I don't know if I've used before or it's just been a very long time; it felt fresh in the moment, but it's the Chrome DevTools, the performance tool. And so you can open that up in your inspector, and then you can go to the page that you want to track the performance. And then you can essentially say, "Hey, go ahead and start profiling and reload this page." And it has so many stats when it finally does load. It has CPU flames charts, which essentially it visualizes a collection of all the stack traces. It has a film strip, so you can actually see the rendering progress of your website along different time points. So if you wanted to go back to a specific time, you can see what did the webpage look like at this point? And then if you go a little further, okay, how much was loaded at this point? So there's a lot of interesting and a little overwhelming information that's there.
So once I knew it was a rendering issue, I went to look specifically at that view, and we have a form on that page that was generating an empty HTML select option for every record that's in the system. So let's say that you're ordering from a restaurant. On this page, there was a form where it had a list of all the restaurants, and, in our particular case, we had about 17,000 restaurants. So there were 17,000 empty HTML selection options, which could have some significant impact on the DOM and page load time. And that was the piece that was really leading to the performance, is the fact that we were rendering that empty select option.
So from there, it was then just triaging okay; we don't really need to render all of these restaurants. There are ways we can scope this down. And that way, we're only showing a little bit at a time versus creating all of these empty options. I should clarify they're empty because part of this form is you select from the first dropdown, and then it populates the other one, and it gives you more information. But the way that this form was implemented, it was actually trying to show all of them at once. But it didn't actually have the data yet, but it was doing like a restaurant.all type of count. And so then that's how we were getting that many empty options.
So it was a very interesting journey. It was very helpful to learn that New Relic has this browser monitoring tool. And I really appreciated their performance tool. And circling back to Chrome, Safari may have something similar. But I found Chrome's performance tool very helpful because then it helped me realize that it was the rendering. And so then I could really focus on the markup and the view versus knowing it wasn't more in the database layer.
CHRIS: I really love the description, almost like a mystery novel of these bugs when we encounter them. Because if you just get to the end and you're like, oh, I was rendering a select, and it had all of this, that loses so much of this story because again, the coding is not the hard part of the work that we do; it's the figuring out what needs to be done. And in this case, that journey that you went on to find the bug. I really like the point where you said, "And someone mentioned this page has just been getting progressively slower over time." I was like, ooh, that's interesting. Now we got a clue. Now we've got a lead, and we'll chase it down, and then finding the browser tools and all of that.
And also, as an aside, browsers are just such an immensely impressive piece of technology, everything that they do. And then you add the DevTools on top of them and magical stuff going on there. But yeah, also probably don't render 17,000 empty selects. [laughs] That seems like it will get you in trouble pretty quickly. But also very easy to get to and especially if there is this incremental, slow creep over time where it's like, oh, that page seems like it's a little slower. It's a little slower still, and it just keeps creeping up over time. But yes, I appreciate you taking us on that journey with you.
STEPH: Yeah, it was a fun discovery. And it made me realize that while we have alerting set up for some of our other queries, we don't have anything set up for the browser time. So that would be a good optimization on our side is to start alerting us before a page gets to the point that it's taking that long to load to notify us sooner. So we don't have to wait for a user to reach out to us, but we can triage sooner.
CHRIS: I also do love the idea of extending the metrics that we hold ourselves accountable to all the way through to the user, and so the First Contentful Paint and all of that. The one that I really love recently that has captured an idea that I struggled to put words to is the Cumulative Layout Shift. Are you familiar with this piece?
CHRIS: So there's like, how quickly does the page render? That's the thing that we want to know. But a lot of applications these days, particularly single-page apps, render pretty quickly, but they render what ends up being a skeleton or a shell of the page. And then behind the scenes, there are like ten different AJAX requests happening. And as the data comes in, suddenly, a part of the screen will render. And they'll render a list of items that they just got back from the back end, but they're still waiting for the information to populate the header. And so if you look at that page, it's constantly shifting as it's loading and just feels, I don't know, flimsy in my mind. But I didn't have a good word, or I didn't have a metric or a number to attach to that. And then I learned about Cumulative Layout Shift, and I was like, oh, that's the one. Now you've mapped the thing that I was feeling. And I like when that happens.
STEPH: Is that the difference between the first paint and First Contentful paint? Is that similar?
CHRIS: I think it's more than that because there are not just two discrete events in this. It can be multiple. And so it's like, how many different times did the thing that's rendered on the screen move around? And so if images are loading, but you didn't have a proper image height and width set, that's another way that this can happen. Then initially, the browser is not going to reserve any space for that because it's like, I don't know how big this is. And then, when the image shows up, it now knows the intrinsic height and width of the image. So suddenly, your page is going to jump from that. You can get ahead of that by putting the height and width on your image, and that's great to do. And frameworks like Next.js have done some really amazing work of making that a build time step as opposed to something you have to do manually.
But then also, more generally, how do we handle this? React is doing some interesting work with Suspense, where you can aggregate together multiple different loading states into one collective thing. It's almost like promise.all, but for your page. I haven't followed that too closely, but I know that that's framework-level work that's happening over there. GraphQL does a really great job of allowing you to group queries together. So there's a solution on that side. But broadly, if you just render some HTML on the server and you send it to the front end, then you don't have this problem because you just have one ball of HTML. The browser is pretty good at rendering that in one pass versus if you have single-page applications that are making a handful of AJAX requests that will resolve in their own timelines and eventually paint to the screen. You get this different shape. And then the worst case of it in my mind is you render half the page. And then suddenly, one of the requests realizes the JWT has expired, and suddenly, you get thrashed over to the login page. Please don't give me that experience, developers, please. Please do something else that isn't that. That makes me sad in my heart.
STEPH: Prioritize the failure state. That's what I'm hearing.
STEPH: Well, on that wonderful circular reference, shall we wrap up?
CHRIS: Wait, I thought circular references were bad...Never mind. 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 @bikeshed, or reach me on Twitter @SViccari.
CHRIS: And I'm @christoomey.
STEPH: Or you can reach us at firstname.lastname@example.org via email.
CHRIS: Thanks so much for listening to The Bike Shed, and we'll see you next week.
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.Support The Bike Shed