Code with Jason

291 - Joel Drapper

Jason Swett

Use Left/Right to seek, Home/End to jump to start or end. Hold shift to jump forward or backward.

0:00 | 1:23:21

In this episode I talk with Joel Drapper about defect-free development—not just automated testing, but the full spectrum: linting, static typing, database constraints, and especially runtime assertions. Joel's library Literal lets you define type expectations that blow up immediately when violated, catching bugs before they spread.

Links:

SPEAKER_02:

Hey, it's Jason, host of the Code with Jason podcast. You're a developer. You like to listen to podcasts. You're listening to one right now. Maybe you like to read blogs and subscribe to email newsletters and stuff like that. Keep in touch. Email newsletters are a really nice way to keep on top of what's going on in the programming world. Except they're actually not. I don't know about you, but the last thing that I want to do after a long day of staring at the screen is sit there and stare at the screen some more. That's why I started a different kind of newsletter. It's a snail mail programming newsletter. That's right. I send an actual envelope in the mail containing a paper newsletter that you can hold in your hands. You can read it on your living room couch, at your kitchen table, in your bed, or in someone else's bed. And when they say, What are you doing in my bed? You can say, I'm reading Jason's newsletter. What does it look like? You might wonder what you might find in this snail mail programming newsletter. You can read about all kinds of programming topics like object-oriented programming, testing, DevOps, AI. Most of it's pretty technology agnostic. You can also read about other non-programming topics like philosophy, evolutionary theory, business, marketing, economics, psychology, music, cooking, history, geology, language, culture, robotics, and farming. The name of the newsletter is Nonsense Monthly. Here's what some of my readers are saying about it. Helmut Kobler from Los Angeles says thanks much for sending the newsletter. I got it about a week ago and read it on my sofa. It was a totally different experience than reading it on my computer or iPad. It felt more relaxed, more meaningful, something special and out of the ordinary. I'm sure that's what you were going for, so just wanted to let you know that you succeeded. Looking forward to more. Drew Bragg from Philadelphia says, Nonsense Monthly is the only newsletter I deliberately set aside time to read. I read a lot of great newsletters, but there's just something about receiving a piece of mail, physically opening it, and sitting down to read it on paper that is just so awesome. Feels like a lost luxury. Chris Sonnier from Dickinson, Texas says, just finished reading my first nonsense monthly snail mail newsletter and truly enjoyed it. Something about holding a physical piece of paper that just feels good. Thank you for this. Can't wait for the next one. Dear listener, if you would like to get letters in the mail from yours truly every month, you can go sign up at nonsense monthly dot com. That's nonsensemonthly dot com. I'll say it one more time nonsense monthly dot com. And now without further ado, here is today's episode. Joel, welcome. Hey, great to be back. Great to have you back. Um, you've become one of my favorite guests. Uh I as of this recording, I just released our our most recent episode. And dear listener, if you haven't seen that one, I encourage you to check it out. I personally really enjoyed that one. Um anyway, Joel, what has been on your mind lately?

SPEAKER_00:

Uh I just well, I had I was just gonna tell you, um, I had a I had a bit of a follow-up from the last episode, which was I had had been trying to think. You charged me like how can you explain what abstraction is? And so I was like, I'm gonna try and explain this concept to my kids. Because if I if I can explain the concept to my kids, then maybe I've fully like grasped it. Okay. How old are your kids? Uh let's see. They are uh eight, nine, and eleven.

SPEAKER_02:

Okay. Yeah, I got a uh how old are my kids? 12 and 15. Yeah, anyway. And so you tried to explain them, explain the abstraction to your kids.

SPEAKER_00:

Yeah, so so this is this is what I came up with. And and I let's not spend too much time on this, because we I think we pretty thoroughly explored this last time. But um basically the the explanation that I used was like it's it's just when you're taking something detailed and you're basically summarizing it, making a small a smaller concept. An example I used was if I say tidy your bedroom, right, that encompasses all the different parts involved in like doing that, right? I don't say go to your room and then look around for a mess and then like crouch down and pick it up. Um I just like I just say tidy your room. And I've I've also made it generic, right, by by extracting this concept of tidy that has a generic thing to be tidied. Um and I and I basically use that as an example because I think I think that's essentially the the gist of it is like you just take a lot of smaller things and you sum them up in one kind of bigger concept that kind of encompasses the whole thing and has generic inputs, essentially. And I was able to explain it pretty well to them, the difference, and and we went over a few different examples. Um yeah, I don't know if that makes sense to you.

SPEAKER_02:

Well, huh. I think so. I I I'm I'm gonna express no opinion on that. Um I I'm not even sure what I think about it. Um except that I think that maybe we can do even better than that. Okay. Um I'm not sure how. Um, but I I just have like a gut feeling that that we can do even better than that. Um these these two books I've been talking about constantly, uh The Beginning of Infinity and The Fabric of Reality by David Deutsch. I think it was the beginning of infinity. I need to write these down. Yeah, he he um he had a bit in the book, it was maybe even an entire chapter titled The Reality of Abstractions. And I thought that was a really interesting way to put it, the reality of abstractions. To me, that implies abstractions are a naturally occurring phenomenon, not just a human created idea. Okay. Um sadly I don't recall off the top of my head the content of that chapter. Um but I've I've listened to other physicists, specifically Sean Carroll, um, talk about the concept of abstraction. And and Sean Carroll said um he he he talked about abstraction as throwing away information. Okay. Um which I found really interesting. And you know, I I I think my personal definition of abstraction for my last episode was something like um replacing lower level details replacing lower level information with higher level information in order to make it an idea uh easier to understand. Um and and the room cleaning thing, um like the teddy room, maybe that that is a good example of that, but I feel like maybe we can do even better than that. But I'll I'll just pause there.

SPEAKER_00:

Yeah, may we might be able to do better than that. I just it's just one that came to mind as like I would never ask my kids to like well when they're really young, maybe I would. Like I'd go and like do it with them, and I'd be like, right, let's pick this up and then let's pick that up. But I'm not gonna say that to my kids anymore because I don't need to. Um and there's there's even a higher level abstraction on top of that, which might be like do your chores. Like you know, like you know, you know what you need to do, like you know that this is a thing that you need to do every week. Um I don't know.

SPEAKER_02:

I I just like so so there's there's an abstraction and there's also just like a um label because there is this like uh piece of knowledge.

SPEAKER_00:

Abstractions basically are labels.

SPEAKER_02:

I'm not so sure about that. I'm I'm remembering part of the content of this book now. Um Okay. He talked about how like I'm I'm gonna get a lot of this wrong, but like he talked about how a lot of people think that like the only everything that's like real and worth studying is like the low-level fundamental stuff like um atoms and laws of physics and stuff like that. Um but there are these emergent phenomena, like for example, evolution that is just as real as these lower level phenomena.

SPEAKER_00:

Um yeah, I'd I'd it's I agree that it's real, but it's real in the same sense that like numbers are real, right? It's not like they don't actually exist, but but they're a concept that has properties that we can test and measure.

SPEAKER_02:

Yeah. Oh man, that's something else he talks about. He he how do you know if it sounds like I have to read this book?

SPEAKER_00:

Yeah, yeah.

SPEAKER_02:

He addresses once I've read it. He just is addresses the question, how do you decide if something's real? And and he has an answer. Like it's a I remember it's a short answer to how do you decide if something's real. I just can't remember it.

SPEAKER_00:

Uh well, I'm gonna so beginning of infinity and fabric of reality.

SPEAKER_02:

Yeah, yeah.

SPEAKER_00:

I'm gonna read those.

SPEAKER_02:

Yeah, I listened to both those on audio and then I read them in text. And by the way, it's crazy like how when I read them in text, I realized just like how little I absorbed when I listened to it via audio. And then even when you read something, it's crazy how much you forget. Um, so I need to go and read those again. And and they're the kind of book that you can like read repeatedly and get something new every time. Cool.

SPEAKER_00:

Yeah, I'm gonna get those.

SPEAKER_02:

Yeah, um going in a little bit of a different direction with abstraction for a second. Um I I've been thinking about it later lately, uh applied differently. Um I bet that you've had this experience, Joel, of like being in meetings at work, um, maybe a planning meeting or something like that. And the discussion is at too low of a level of abstraction.

SPEAKER_00:

Right.

SPEAKER_02:

And maybe there's like, I don't know, I've been in like quarterly planning meetings or something like that. And you know, it's a quarterly planning meeting. We're trying to think at the level of three months, but the discussion is at way too low of a level of abstraction. And and I want to, oh, this this word is important. I I I want to talk about what's essential. I think that's that's an important idea in an abstraction. Um, we're talking about what's essential, and we're throwing away information that is not essential. And and we're trying to focus on like the one like what's the one important thing over the next three months, and how we how can we forget about all these details for a minute and just talk about the appropriate level of abstraction for what we're trying to plan?

SPEAKER_00:

Yeah, it's a it's a kind of zooming out. When you like abstractions are zooming out, and then implementations are zooming in. And they're and you can do that through so many different layers. Um like the to go back to the tidy in your room example, right? If you zoom right in, um then you're thinking about like messages being sent from the brain to the muscles to um you know actuate the arms to pick things up off the floor, right? It's like and we and we we talk conceptually about all of these different layers of abstraction on top of those ideas. Yes, I'm thinking about you doing that when I say tidy your room, um, but I'm not actually like thinking about you doing that. It's all it's all summed up.

SPEAKER_02:

Yeah. Um you know what? Um, okay, so uh uh we're still early in the conversation, so I want to get this out of the way before we go much further. Um I have a hunch that somebody's listening right now and they're like, man, these guys are just like totally in the clouds. This is like not practical. But for me, and I'm curious if this if this is the way you look at it, um for for me, the only reason I'm interested in this like philosophical sort of stuff is because it has practical applications.

SPEAKER_00:

For sure. Yeah, yeah. Yeah, yeah, 100%. Because it's how you it's like especially for programmers, right? It's it is it's essential for how you think about what your code is actually meant to be doing. Because our brains can't fit a thousand tiny little concepts in them at once. We have to we have to group them up and zoom out and be able to think about the whole.

SPEAKER_02:

Yeah. Um and and like uh the more I think about it, the more broadly some of these ideas apply. Um again with the planning. For for me, for for for so many years, you know, I've been in the industry for 20 years at this point. Um meetings, like people get so yeah, the the the they get off target so much, and I've always been like so frustrated, but only in the last little while have I like been able to put my finger on how exactly I think they should behave differently. Um and and the way I think they should behave differently is by focusing at the right level of abstraction. I'm hesitant to bring this up with people because I'm like, hey, you know what your problem is, Dave? Uh yeah, how do you explain that? Yeah, you're you're speaking at the wrong level of abstraction. They're gonna be like, What are you talking about? Like, we're we're we're doing a quarterly planning meeting. Now you're talking about abstraction, that's like a programming thing. I'm afraid I would like immediately lose people.

SPEAKER_00:

Right. And yet that is exactly the problem that you're facing. And it's it's amazing how it's that problem when once you see it abstractly, you you start to see it in every aspect of practical life. Like not just the code you write, but also literally everything that you do.

SPEAKER_02:

Right. Right. Um yeah, so I I I'm like frustrated with myself for not remembering what was in this book as well as I thought I did. There's there's nothing that shows you how poorly you understand something than trying to explain it to someone else and discovering that you can't.

SPEAKER_00:

Well, let's let's cycle back to this another another time. We can uh we can move on. And I'm I'm gonna read these books.

SPEAKER_02:

Yeah, yeah. Um, so I I want to talk with you about something else you brought up, was which is the idea of like bug-free coding. And I've encountered the same idea um under the name of uh zero defect uh software development. Um so tell me about that. And and while you're doing that, I'm gonna go grab the beginning of infinity off off my shelf. Um but tell us about that.

SPEAKER_00:

Can you still hear me?

SPEAKER_02:

Oh yeah.

SPEAKER_00:

Yeah, yeah, yeah. Yeah, so um how to how to approach this. It's not exactly bug-free development. Um, it's more a case of like I've been looking at how you can remove bugs. Like you can produce software with with fewer bugs, um, and all the different like techniques and approaches that you can use to do that. Um, so like we the main thing that Ruby has seemed to talk about is automated testing. Um and that's like part of it, that's an important part. But there's also I think a bunch of different layers. I'll I I've listed them out, so I'll just run through the list and we can kind of dive into them. So uh the first one is manual testing. Um I actually think that manual testing sometimes is better than automated testing, and depending on what you're doing, uh, you've pretty much always got to manually test the thing, at least one decent like one path through what you're making as well as you can. Um the next layer is like linters and static analysis. Um this is like Rubicop, um uh OXC for for JavaScript or ESLint. Um this can really help. Um this can help find a lot of bugs, especially if you have a type-safe um language like TypeScript. Actually, that's the third, the third layer, static typing. Um, and I would include into this things like database constraints um as a kind of static typing. Though they're also somewhat a runtime level uh static typing thing. Um actually maybe we should talk about those with runtime type checking. Um so the fourth the fourth layer is automated testing, which we we've talked about a bunch before. Um and then there's AI-based linting, right? Having an artificial intelligence look at your code, it can often find errors. Um and then there's runtime assertions. So these this is code that runs in production with your um with your software that basically just makes sure that everything is running as you expect it to be. There are ways that you can build software where like you kind of encode these expectations throughout the software, and then if the expectations aren't met, it can blow up really quickly and just raise an exception and stop, which prevents bad things from happening and also like brings attention to it. One of these is database constraints, right? The database has a non-nullable field, or it has a foreign key constraint, or it's gonna check that this integer that you've given me as an ID actually is the ID of a record in this other table. Um the next layer is QA, which is like having someone else do manual testing. Um and then there's your users, right? Users are actually running your application, they're gonna report things to you, they're gonna click around your app, give it a true receptions.

SPEAKER_02:

Okay, so I want to pause you there. Um, because I want to frame this topic in a certain way. Um Yeah. First of all, I think this is like such a profitable area to think about because so much expense can be saved. You can go so much faster if you it if you keep things if you keep these things in mind and if you take development approaches that that are consistent with these ideas.

SPEAKER_00:

To a higher level of abstraction.

SPEAKER_02:

Yeah, yeah. And and talk about um the bigger picture a little bit. Um yeah. So you know why why should we care about this? Is is one idea. It seems like an obvious question, but like why should we want to think about this kind of thing? Um and like what is a bug or a defect and stuff like that? Um maybe let's start there. What what is a bug? What's what's a defect, and why would we want to do this like zero defect, bug-free coding kind of thing?

SPEAKER_00:

Yeah, so a bug would be any time that the software doesn't do what you thought it would do, I think. Can I read that? No, I don't know for sure if that's yeah, I don't know if that's quite right.

SPEAKER_02:

It's it's when the program behaves differently than the programmer intended, right?

SPEAKER_00:

Well, sort of. But maybe you intended it to work one way that was wrong, and your intentions were wrong, but it actually worked correctly, and and uh looking back at it, you're like, actually, that's right. Yeah, so this is not even that. Yeah, this is why I make a distinction. You would have wanted it to work that way.

SPEAKER_02:

This is why I make a distinction between bugs and defects. So to me, there's three kinds of defects. Um, one kind of defect is a bug, um, another is a design flaw. So that's when it works as intended, but the way that you intended it to work is not good. Right. And then the third is a missing feature. Um, and and that's I didn't come up with that. Uh this guy, Soren Lausen, in this book User Interface Design, came up with that. Um, yeah, I think it's more useful to think of that broader concept of a defect.

SPEAKER_00:

Yeah. Yeah, so so what why is it important? Um I don't know. That's a I mean, that just seems like such an obvious question. We we're building software for a reason. There's a reason that we that we have features um and we want the software to do what it's meant to do, and and and also to not do what it's not meant to do. Right? That's that's in in a lot of ways, that's actually the more important thing. Um and that's that's why I would say that if you're in a scenario where like your application knows that something's gone wrong, basically it should just stop. It should throw an exception, should be really loud, um, and it should just stop working. And the reason is it's much it's usually much worse for the application to do something it wasn't meant to do than it is for it to not do the thing that it was meant to do.

SPEAKER_02:

It's an interesting point.

SPEAKER_00:

Um Because the the the the number of things that it's not meant to do is basically infinite, right? Um and you don't you don't know what the consequences are. Um and I like if you're like the consequences could be things like irreparably deleting or modifying data, right? Or or sending sending emails to millions of people that weren't meant to receive emails with information that they weren't meant to receive. There's the the the space of like bugs that that do things that they weren't meant to do is just like massive. And so it's usually unless you unless you have some kind of system where it running is like essential, like like if it stops running, people are gonna die. Um like you just have to throw an exception. I think that's that's always the safest option.

SPEAKER_02:

Um okay, so two things. I want to go back to the why for a second. Um also I want to talk to you about this off the air. What if we taught what if you and me did like a long ass session and like taught a uh workshop on this topic or something like that? We can talk about that later. Um, sounds great. Yeah, yeah. Anyway, the why. So I don't think it's like I don't think everybody would agree that that this idea of striving for zero defects is like what you should do. I think right, I think it's something that has to be argued for. Um in my person, oh man, this is such a big topic. Um so like a big part of this, like like like upstream of all this stuff is the design of the product. Um like I worked for a company that I think you know, um where they that their kind of way of of looking at software was like how much can we make it do? Let's make it do the most amount of things possible with very little regard to the the design of the system. And when I say design of the of the system, I mean like the conceptual um design of the system. Because I've I've talked about this before, like a software system is superficially made out of code, but at a more fundamental level, it's it's made out of ideas. Um and like if you have a system that is conceptually corrupt, it proliferates defects um kind of by necessity, because like in in the the the places where two ideas are in necessary conflict is where the bugs live. That's one place bugs can live. Um, and so if you have thousands of ideas which are conceptually in conflict with each other, uh, and again, I'm talking like necessarily incompatible, there's no way they can be reconciled with each other and still make sense, you're just gonna have so many bugs. Um again, necessarily.

SPEAKER_00:

And so that's like Yeah, it must be the case that if you fix one bug, you've introduced another. Yeah. Right. Like I if if you if you actually have incompatible ideas of what the what the product's meant to do. And they're true, if they're truly incompatible, right? Yeah, then you're breaking one expectation. So you fix that, and and by necess by like out of necessity, you've broken another expectation. Yeah, yeah. Oh well, your expectations weren't actually incompatible.

SPEAKER_02:

Yeah, yeah. Like if I can give a quick example, like uh a society can be like free or fair. There's three things. Free or fair or equal. I think that it's yeah, free, fair, and equal. Um, but you can't have all three at the same time. Um interesting. Because if it's fair, not every if it's fair and free, not everybody will be equal, you know. Um and if everybody's equal, then it can't be free and fair because you'll have to impose constraints to ensure that equitable outcome. Um, so those are three ideas which are conceptually incompatible by necessity. So there's one example.

SPEAKER_00:

Okay. Yeah.

unknown:

Yeah.

SPEAKER_00:

I mean, obviously it depends on your definitions of free, fair, and equal, but I I get the I get the concept. Um yeah, so obviously the what one thing that that can be useful about writing an application if you have this like these incompatible concepts of what it should do, is it will tell you eventually. Um it will hopefully highlight those those issues and and you'll hopefully have the um you know the ability to zoom out and fix and fix them at a higher level than you know just whack a mole on the code until yeah, yeah.

SPEAKER_02:

And if I can remove that test, if I can give like a more realistic code-related example, um you've probably worked on systems where there's like um pricing of products and stuff like that, and obviously the price of a product can change over time. Um but if if you if you construct the data model in like a naive way where you just have like a product table and each product has one price, then what happens when there was a sale of that product? The thing costs 10 bucks, then later the price changes to 12 bucks. Uh now that sale in the past uh shows that it cost 12 bucks at that time in the past, even though it didn't. So that's like a a scenario where where the system's conceptually corrupt.

SPEAKER_00:

Yeah, if if if one of the requirements is that there's a live connection between the product price and the receipt, and the other requirement is that the receipt tells you how much you paid, that that's like not gonna happen without without a design change. It's the same with like um uh like at my work, we uh we do payroll, right? And we produce these um pay slips. And obviously, if you change your address, it's important for us to figure out like where when you when you change your address, what does that mean in terms of all the artifacts you produce? Some of the older artifacts need to be fixed to your old address. Um, and then your new the new ones need to have your new address, but also why are you changing it? Are you changing it because it was wrong in the first place? There was a spelling mistake. Maybe that needs to be backfilled. But like but if you're changing it because you moved house, then we need to know when you moved. Um like yeah, there's all there's all sorts of ways that that you can uh yeah, find find holes in in requirements that are very closely related, but but you have to you have to find the very specific line where where everything is compatible and everything makes sense conceptually.

SPEAKER_02:

Yeah. Yeah, so like when people talk about clean code and stuff like that, like there's so much focus on like write small functions and small classes and stuff like that. Yeah, and like all that stuff is absolutely true and and good and and necessary and stuff like that. Yeah. Um, but something that is not discussed nearly as much as I think it should be are these higher level things, because I always say good coding is downstream of clear thinking. And so the the design of the product has to be coherent. Um and the tragic thing is um as programmers, we often find ourselves working on systems that are conceptually corrupt, and the corruptions are so deeply baked into the system that they will that they will never get fixed. Um, and and there's this again tragic aspect of human nature. I think we talked about these things in our last episode a little bit. Um where people have a a short-term, long-term um confusion, or or maybe you could say they have high time preference, which is where they um favor the short-term over the long term. Um where where like there are investments that are clearly worth making that people decline to make because they they consciously or unconsciously prefer a short the the they want to um pay a permanent high price in exchange for a temporary small gain, um, which is so sad, but that's that's reality.

SPEAKER_00:

Yeah, that's like I mean, yeah, that's that's that's just like the story of so many, so many problems in this industry.

SPEAKER_02:

Yeah, or even on this planet, you could say yeah, it's it's uh it's sad, but okay, so one of my like one of my like life principles is to you know live in reality and meet reality on its own terms and not like wish reality is different because that that's not productive. Um so like the the question that I kind of pose to myself is like okay, given the fact that there are these these realities of human nature that lead to these undesirable outcomes, um what do you do about it? But that that's maybe a whole different topic.

SPEAKER_00:

Yeah, I think it's a whole different topic. Um let's let's get back to uh to like bug free development. So do you I'm not sure that I can make the case. Uh I'm I like I haven't thought about this enough to be able to make the case why you should care about bug-free development. Um, but I think you already spoke to that a bit. Oh, because it's cheaper. Right. Yes. Yeah. I I mean I believe I believe you. I don't know if I could make that case very effectively right now. Um but yeah, yeah, I mean, it's obviously cheaper in the long run.

SPEAKER_02:

Yeah. I mean, maybe it's not obviously cheaper. Um I I think there are people who would say it's obviously cheaper to not worry about the bugs. Um, you know, just just get the get the plane off the ground and then like worry about the problems later, you know? Yeah. Um, and and that's another tough thing because like there are things that can be empirically demonstrated, and so it's then it's like, okay, you're just wrong, and I'm right because we've empirically demonstrated it. And then there's other things where you can only make an argument for it, and if the other person doesn't buy your argument, well, it's just like there's not really anything you can do about it because it can't be proven. So I to me it's clear that that building software that's defect-free is gonna be cheaper than than building software that's buggy. Not everybody believes that, but maybe we just have to leave that at that. I don't know.

SPEAKER_00:

Yeah, there's also this idea that there's that there's always a downside, right? That um if I do this, then I'm gonna have to pay the cost of doing this, right? I'm gonna like I I do I want to do runtime assertions. I'm gonna pay the cost of doing runtime assertions. Things are gonna be maybe maybe the cost is that it's gonna be slower to run in production, or maybe the cost is that it's gonna take longer to do the development, or the code is gonna be less easy to read, right? But we get the benefit of um fewer bugs. And I think I think that that like we tend to think that for any for any benefit there must be a drawback as well. Like almost like it's a rule of nature that there must be an equal and opposite drawback, but it's just it's just not always the case. There are truly win-win scenarios, and there are things that you can do that you know that like make all of the different measurable aspects actually better than it than they were before.

SPEAKER_02:

Well, here's here's what I think the significant thing is. I I think it's maybe not that there aren't costs, because you know, writing an automated test, like it's totally a win because you pay a small cost and get a huge benefit on average. Um so to me, it's it's not that there aren't costs, but I think the significant thing that's being missed by a lot of people is that software development is multiplicative or or it it it's compounding, something like that. Do you know what I mean?

SPEAKER_00:

Yeah, yeah. Yeah, like compounding interest. Um it both in both in debt but also in savings. Um in the investments that you make, they're gonna pay off in the long run as well through compounding interest.

SPEAKER_02:

Right. And maybe a better metaphor is health. Um because you can't get like a thousand times as healthy, but you you can get way less healthy and die.

SPEAKER_00:

Yeah.

SPEAKER_02:

Um, so the question isn't like how can I get a thousand times as healthy healthy? The question is how can I avoid getting way less because like with a uh software system, it is tell me if you see this differently, but I think it's like uh a natural law of the universe that the system is gonna get worse and worse over time.

SPEAKER_00:

Yeah, usually.

SPEAKER_02:

Yeah, I mean I think it's necessarily true.

SPEAKER_00:

I don't know that that's true. I th like I definitely have projects where I would say they have not gotten worse and worse over time.

SPEAKER_02:

Maybe worse is too uh low resolution.

SPEAKER_00:

Okay.

SPEAKER_02:

Like it'll get more expensive to maintain, adding new making changes will be slower. That's true, stuff like that because of entropy.

SPEAKER_00:

Yep. Or just because of the amount of stuff that you have to deal with now. Like it's just getting uh it it's more like um not so much entropy as inertia, right? This it takes a lot of power to move this thing in a different direction because the the heavier that it is. Good. So the the larger that your project is. Like um, I'll give you an example and just throw in uh a side topic, which is that I'm working on a compiler for flex. Uh yeah, so the what once we've finished implementing this compiler, it's gonna be a lot more expensive to change the API of Flex, because so much of that is gonna be coded into significantly more code than we had before. Um and so even though like it doesn't matter how high quality this compiler is, like the the the actual code, if you look at it, could be really, really well done. Um but there's still the weight of significant amounts of code that depend on on things being a certain way. And so the inertia is there, like it it would be very difficult to change direction, if that makes sense. More so than a younger project.

SPEAKER_02:

Okay, and this is such an Important topic. So I I mentioned that business where their way of looking at everything is like how much stuff can we make it do? Whereas like a company like 37 Signals and their product base camp, they're like, How little can we make it do? Right. Because uh from from what I've read of their like mindset and stuff like that, they realize that like every time you add a piece of behavior to a program, it's like adopting a pet. You know, you're gonna have to feed and water that pet for forever. Um and so let's make a choice not to expand the scope of the program's behavior because that has an ongoing cost. Um and so there's there's this like continuum of programming concerns and business concerns where it's like um every programming decision is kind of a business decision and every business decision is kind of a programming decision. And so like for with uh Saturn CI, for example, I'm the product I'm working on, I'm very conscious about uh aggressively what's aggressively not expanding the scope of behavior because looking for leverage.

SPEAKER_00:

Sorry? Leverage looking for leverage. So what is what is something that I can do that is very lightweight um that still has a a a really big impact.

SPEAKER_02:

Yeah, yeah, and Fred Brooks talks about this in the mythical Man Month. Um I forget the way he the the term that he uses, but it's like basically the ratio of the number of concepts to the richness of behavior that can be achieved, if that makes sense. So like yeah, like I think Legos are a good example. Like uh you can have a relatively small amount of different kinds of Lego pieces, but the amount of different kinds of things you can build out of Legos is is basically infinite. Whereas you can also imagine a very um complicated system. I don't know, take take my like car that I drive, like you can pretty much only use the parts of that car to build that car. So that's kind of the opposite end of the spectrum. I don't remember my point.

SPEAKER_00:

Just um just like on leverage, it's like uh some but sometimes you can you really can get so much for so little. Um like the the router that we're that we're building for um my web framework. I don't know if we've talked about this on on the show before, but I'm building a web framework called Yippy.

SPEAKER_02:

And sorry, what's it called?

SPEAKER_00:

It's called Yippie. Like hooray.

SPEAKER_02:

Um Y-I-P-P-E-E.

SPEAKER_00:

Yes. Okay. Um it's not it's not available yet, um, but parts of it already are because um you know one part of it is flex, another part is literal, which we might talk about um going back to testing. But um it is so that the the router is so much simpler than Rails router. Um it's inspired by uh this router called, or it I don't know if it's a router or a framework. It's kind of like a very, very small framework called Roder. Um and the way that Rhoder works is it it basically uses blocks and conditional execution of blocks to do routing. And so the implementation is incredibly simple, but it's it's like I think something like 10 times faster than Rails at just like routing a deeply nested um URL from uh I've realized I'm saying routing, and I should have been saying routing from the American No, we can uh we can handle it.

SPEAKER_02:

But but this this this rota framework, real quick. I've heard about this. It's like R O D A or R O T A. Yes. Yes, yeah. Um yeah, this this guy, somebody in the Ruby community built it. Jeremy some Jeremy Evans. Jeremy Evans, yeah. Yeah. Yeah, okay. Anyway, you were inspired by this rota router.

SPEAKER_00:

Right. The rota router. Because it's like it can achieve significantly better performance. And I think that um a slight variation on it can achieve really, really good, flexible kind of like design uh just aesthetically, in terms of like how you set up routers and controllers and and things like that. Um I've done a whole video on this. Um but the the point is it's extremely high leverage because for a very, very small implementation code, like maybe a couple of thousand lines in total, you can have something that can you know has all the features of a of a of a router or router, um and and is like significantly faster than um you know than the status quo than than than Rails. Um and so it's like you can you can often if you look closely you can find these points of liver of leverage where um where you can kind of get that if that makes sense. Yeah. I don't know how we got onto onto leverage, and we should probably get back to uh to bug-free development.

SPEAKER_02:

But well, I I think we're still on bug-free development, um, because all these you know everything's connected to everything else.

SPEAKER_00:

Um and actually speaking of this, right? Um we can think of bugs to some extent as being a property of of code, right? The the more code you have, the more bugs you have, right? It's you can actually like if you want a completely bug-free piece of code, just do like one plus two, and there you have it. Right? That that code has no bugs in it. Um but the more code you have, the more surface area you have for bugs. Um, and so like one of the things you can do to reduce bugs is literally just write less code.

SPEAKER_02:

Thank you so much for saying that. Um and and again, it goes back to the like the continuum of programming decisions and business decisions. It's like, how do you have fewer bugs? Just choose to do less stuff, have a smaller product.

SPEAKER_00:

Yeah. Yeah. Have a smaller product. This is also where metaprogramming comes in for me. Uh I think that one of the amazing things about Ruby is how you can take a very a concept that would typically require a lot of duplication of code, and you can reduce it to just a small function that produces code instead or defines those methods. And when you when you do that, you know that all of those things that are being defined are consistent, right? They're following this pattern exactly. You're not relying on people following the pattern.

SPEAKER_02:

But wait, isn't metaprogramming evil?

SPEAKER_00:

No, metaprogramming is awesome. Um it needs to be used responsibly, but it's so awesome.

SPEAKER_02:

This is one of those like oversimplified. Okay, so uh the the problem with talking with you, Joel, is that I just want to talk about everything. Um there's this idea of of like a I don't know, like a meme lesson. Um, this is not like a real thing, this is just something I came up with. Um, but some things are taught and learned because they're true. Some things are taught and learned because they're easy to teach and easy to learn. Um, like, you know, in the last episode, we like so thoroughly decimated the idea of duplication is cheaper than the wrong abstraction. Yeah. That's a meme lesson. It's it's easy to teach and easy to learn. You know, it's so easy just to utter the words duplication is cheaper than the wrong abstraction. Um and and that meme has spread not because it's true, but because you know, the definition of a definition of a meme is a self-replicator. You know, it's it's good at replicating itself. Um and so what was the thing? Metaprogramming is bad. Yeah, yeah, yeah. That's the meme lesson. It's easy to teach and easy to learn, but it's it's it's it's lacking some very important nuance.

SPEAKER_00:

Yeah. Yeah, that that metaprogramming can be used to make uh code that is safer, um, more consistent, faster, even, right? One of the things that we often think about when we're talking about metaprogramming is it makes things slow. And it's true. If you use a lot of like method missing, um stuff like that, and you don't really understand what's happening, you can make some really, really slow programs with metaprogramming. Um, but you could also use metaprogramming um to make things extremely fast, right? Um I would say what we're doing with the flex compiler is a kind of metaprogramming. Um and the the result is that templates render something like 20 times faster after this. Oh wow. So it's like you can you can absolutely use it for good. You just need to know what you're doing, and and obviously like Yeah, be careful.

SPEAKER_02:

Yeah, it's one of those things where it's like meta pro it's it's not accurate to say metaprogramming is bad, it's just that bad metaprogramming is bad.

SPEAKER_00:

And like Yeah, it's I mean as is always the case. Like it's it depends.

SPEAKER_02:

Yeah, um, you know, similar thing with like Rails callbacks. People say that like well, actually I shouldn't say this because uh they're just they're just bad. Well, I I was gonna say that people say that that that Rails callbacks are bad, but actually when I looked into it and like looked at the blog posts about it, that's not really what they say. They say like they are dangerous and should be like not just used cavalierly, they should be used thoughtfully because they have a price and they should only be used when the benefit outweighs the price. So again, it's it's not that callbacks are in my opinion, it's not that callbacks are bad, just bad callbacks are bad.

SPEAKER_00:

Yeah. Yeah.

SPEAKER_02:

Um, I don't know how we got here, but so we were talking about leverage, and you said let's get back to to defect-free programming and I programming are. So so the the the reason that I said that is because I think the idea of leverage is closely related to the idea of elegance. So to me, elegance is when a problem is solved by a uh simple solution. Or a solution is more elegant, the simpler, the more simply a solution is more elegant, the more simply it solves more things.

SPEAKER_00:

Right.

SPEAKER_02:

Like the ratio of complexity to what you get for it, or something like that. And so I think that's closely related because um you're gonna have fewer defects. Again, the fewer concepts you have in the system, the less room there is for defects.

SPEAKER_00:

Yep. Yeah. Uh definitely if you can write less code, you're gonna have fewer defects. Um, and you might decide like um yeah. Like, let's let's think about a concrete example of this. Um, my library flex. You could implement my library uh the the flex library, probably in, I'm gonna guess, about maybe 800 lines of code. Um it's currently closer to two and a half thousand. And I think with the compiler, it's gonna be closer to three and a half thousand. Um actually more more like five thousand if you count some some separate libraries that we've had to make to support this. Um now I guarantee you that we will have fewer bugs with that 800-line implementation. Um like it's just there like you're just not gonna get them. But there are things that you are gonna lose by doing that, um, and mainly it's performance. Um because the the 800-line implementation isn't going to have so many you know specialized code paths, it's not gonna be doing so much um you know analysis and rewriting of code to be able to um you know just get the get the best possible performance. Um and so you you really just need to consciously decide, am I what's the important thing here? Is it is it that this just accomplishes the goal with the simplest implementation possible and the fewest, you know, the best chance of having no bugs? Or is it is there a more important aspect to this? Um that yes, it's gonna take a lot more work. We're gonna have to spend loads of time trying to find these bugs, because we're introducing so many places where bugs could come in. Um even like just between uh you know, you had an HTML element that had no attributes versus had attributes, right? They're completely different code paths, dozens of them, um in those scenarios, right? Whereas it could be the same code path in a in a in a simplified implementation. So I'm not saying that you always want to go for the the the less code. Sometimes you don't, but you just can be aware that the cost is more bugs or like more more likelihood of bugs, more work involved in in fit in making sure that there aren't those additional bugs, if that makes sense.

SPEAKER_02:

Right. It's like this is like a subjective thing, but but I think you should be like um I don't know what the right word is exactly, like parsimonious about your your like spending. It's like more more functionality costs something. So don't be like a profligate spender and and cavalierly purchase all these behaviors. Um be parsimonious about it and be like, I don't know. That's gonna that's gonna cost something, so we'll just say no. We'll just say no to that and and not do that. Uh so that you can save your limited budget to buy the things that really matter to you, uh, like in your case these performance benefits.

SPEAKER_00:

Right. Yeah, for sure.

SPEAKER_02:

Yeah, and I think what we've been talking about uh so far, uh although we haven't used the label, is uh shifting left. Like choosing not to build something is like shifting left. You're you're not like uh, you know, we talked about automated testing and manual QA and stuff like that, and like you can skip all that by by shifting left. But sorry, I think you're maybe about to say something.

SPEAKER_00:

Oh, I was gonna say we should go into some of the like so you do decide you're gonna build this thing. What are some ways that you can um, you know, besides automated testing, which we covered a lot, what are some ways that you can make this thing mostly bug free?

SPEAKER_02:

Yeah, okay. Let's talk about that.

SPEAKER_00:

So uh we we've got we've got we've got manual testing, um, we've got linting and static analysis. Like you should be using Rubicop um and various other things that you that can catch code errors. I think I I I'm a big fan of TypeScript. Um I know that a lot of people are not. Um but for me TypeScript is just like a way to avoid having to do a lot of more a lot more work later fixing bugs. Um and it save it saves me a lot of time. And if I have the opportunity to use it, I'm gonna I'm gonna use that over JavaScript when it makes sense. Um but let's not get into that either.

SPEAKER_02:

Well, if I can make a quick comment. Yeah. I think of TypeScript and tell me if you would look at it in a similar way. Um I I look at it like bumper bowling. Like maybe JavaScript is regular bowling where you can roll a gutter ball, but TypeScript you put the bumpers in, so it's physically impossible to get a butter call a butter call. Right. Uh gutter ball. Would you think about it the same way?

SPEAKER_00:

Yeah, similarly. Yeah. Um and like it has pros and cons, right? There isn't so much metaprogramming in JavaScript that you can do. Or at least when you're using TypeScript, you wouldn't really be doing that kind of thing. Um and so I think that there are some benefits that you get, I've already mentioned, from doing metaprogramming in Ruby that can give you a lot of leverage. But from my perspective, when I'm building stuff for the browser, testing stuff in the browser is such a pain anyway. Um it's such a pain. I like pretty much not going to do browser-based automated testing. I'm just going to test everything manually. Um and to give myself the best chance of not introducing a bug, if I can write type, you know, statically typed code and have the type checker find 90% of the issues before they even make it to the browser, that's amazing. I'm going to take that. That's like such such a worthwhile investment for me.

SPEAKER_02:

Would you call that shifting left?

SPEAKER_00:

Uh I don't quite know what you mean by shifting left, but I'm like I'm choosing to I'm choosing to spend a bit more on putting up these guardrails to make sure that I I don't fall over the edge because I know how hard it is to um you know to to to do the testing in an automated way in the browser.

SPEAKER_02:

Yeah, what I mean in this context by shifting left is like catching things earlier upstream.

SPEAKER_00:

Right. Yeah.

SPEAKER_02:

Yeah. And okay, this is a big mistake that I see in Rails, and I'm sure it's not unique to Rails, that's just where I've seen it. Like people relying so much on system specs, um, and so little on model specs. And and and I understand it because it's like not necessarily obvious. It's not necessarily obvious that you should try to shift those things left, and then it's not obvious how. But like for me, you know, if if you want to write fewer system specs, often you have to change the entire structure of your code. Um that's that's not exactly easy. I I used to like in consulting, I used to try to help people with their automated test suites, but what I realized was that the problem is so further upstream and so much more fundamental. You can't fix the test suite without fixing the application. Um anyway, you you can you can get that benefit by trying to structure your code so everything happens lower level. Like instead of putting things in in The controller, put them in the model, and instead of putting things like in an active record model where the behavior is tightly coupled with database I.O. Put those things in poros, plain old Ruby objects, and make your assertions on the poros so that the database doesn't have to come into the picture. Um things like that, and you can again um put those constraints in place earlier upstream for a much lower price.

SPEAKER_00:

Yep. And also like moving constraints up into the database from uh from your model layer as well is another example of that. But just to just to finish uh on static typing in TypeScript, like it is so often the case that I can refactor like a 500-line piece of code, like completely changing significant portions of it. And I get to the point that the type checker is happy and I run it and it works correctly first time. That happens like basically all the time. It's so rare for the type checker to say, yep, this is good, and then I run it in the browser and it's broken. Like it's super, super rare. And that's just like speaks to how how much of an impact it can have if you actually invest in in doing in static typing. Um, at least in my experience. And I know other people hate TypeScript, and that's fine. I I I don't I for me, it's just it's just like a win-win. Um it makes it easier for me to work with the code and it and it's and it's like less likely to have bugs. The the thing I really want to talk about though is runtime assertions.

SPEAKER_02:

Yeah.

SPEAKER_00:

Um, because I think that's the one that has such a big impact that almost no one is doing. Um and actually, to before we get on to runtime assertions, let's talk about exceptions briefly. So basically, exceptions I think should be used only when there's a an exceptional thing has happened. Um and there has actually been some kind of error, and you want to halt the system essentially. You should probably not be rescuing exceptions to do anything other than re-raise a slightly different, maybe better exception with more information. Right? We don't want to be using exceptions as part of our everyday code and just rescuing them as a standard practice, and then everything just continues as normal. Um, it might be appropriate to do this to wrap some kind of library that raises an exception where it probably should throw. Um, but if you want the kind of like the um uh the kind of shape of an exception, but without without using an exception, you can use throw catch in Ruby. There's no reason to use exceptions for that. Um But like at the end of the day, we want exceptions to to basically halt the application and um and tell us that something went wrong and also prevent the thing from going wrong even further as soon as possible. And I think that when you when you um if you remove all of the exceptions that shouldn't really be exceptions, you can get to a point where you your team basically just has a zero exceptions policy. It is the number one property for the team to fix that exception until it's until it's fixed. And the fix might be fix the bug, or it could just be stop that thing from raising an exception because it's not actually an exception. Um in either case, there's a bug, and if you take that really seriously and get back get down to like inbox zero on your on your exception tracker, um you can run an application like that for a really long time. Um Yeah.

SPEAKER_02:

And and I wanna okay, so I was gonna make a statement, but let me actually make it a question. Why it's gonna sound like a dumb question, but why is that a good idea?

SPEAKER_00:

Um so because when like first of all, exceptions are not the best way to do I wait, wait, what's why is what a good idea? Why is it not a good idea to use exceptions in in part of your regular code?

SPEAKER_02:

No, sorry. Um, why is it a good idea to have like kind of an inbox zero policy with I'll I'll use the term defect, um, even though that's not exactly what you're saying. But yeah, why why should I want to do that?

SPEAKER_00:

So the the reason is um because you you're otherwise you're gonna miss the the really bad ones, basically. You're gonna get so used to there being oh, there's there's you know 100 exceptions a minute, that's normal. Um, you're gonna miss the ones that are actually really serious. Um in a lot of these places, there are exceptions that are kind of just tolerated, like oh, that happens all the time. I would say that the bug there is that you're wearing out the the on call team because you're saying there's a something's gone wrong, but it hasn't actually gone wrong. And so you're gonna get like alert fatigue. I think you can get rid of those.

SPEAKER_02:

I understood you when you said router. I didn't understand you when you said on-call team. I thought you said uncle team.

SPEAKER_00:

And I'm like, on call team.

SPEAKER_02:

What is that? Um yeah, okay, you're gonna like exhaust the on-call team.

SPEAKER_00:

Yeah, you're just gonna get used to there being bugs. Um, you know, and it's like if an exception has happened, it's a user hasn't been impacted. So I just think that it's uh it should be a high priority to fix those.

SPEAKER_02:

Yeah, yeah, I totally agree. Um, there is a psychological benefit to keeping on top of bugs because if you just constantly have bugs, you're like, oh yeah, well, you know, this application is a piece of shit, so like who cares? Um and and you kind of set that level of standards, and people just like start to care less about everything. They you can't feel like as proud of what you're doing, and that has so many negative downstream consequences, and even like you know, I would categorize performance problems as defects, even if they're not user-facing performance problems. Like for me, sometimes in Saturn CI, I've discovered one day I realized that like things were kind of laggy and slow in the UI, and it made it feel janky. And I was just like, ah, yeah, that's kind of how this thing is. And I'm like, wait a second, this sucks. Like, that's a bad way to be. Um and so like when I start to have that feeling I want to improve improve the performance, even if the only audience is myself, just for the psychological benefit, so that I restore that like pride and motivation to continue trying to do a really good job. I think that's really important.

SPEAKER_00:

Right. Yeah, exactly. Um and so if you get if you can get to this place where you have inbox zero on your exception monitoring, no exceptions, um pun intended, then um then you can start looking at um runtime assertions. And I think these are incredibly powerful. So runtime assertion is basically an assertion that that is part of your regular code. Um it's almost like you're testing, but inside your code. And you're just gonna say, essentially, here's an expectation, and if that expectation isn't met, then raise an exception. And you'd think that if you did this a lot, that you would end up with lots of exceptions, but you don't. You end up with the opposite. You end up with fewer exceptions. Because those exceptions are they they they are usually triggered very early, they usually have very clear messages, and and if they are dealt with appropriately, then you're gonna you're gonna fix them straight away. They also, because they're in your your code, they're gonna be triggered as you run your regular test suite. So every time you're testing um your application, you're gonna go through those code paths. And if any of your test data would trigger one of those exceptions, you're gonna also trigger them there.

SPEAKER_02:

Um sorry, how how similar or different or whatever would you say runtime assertions are to like alerting and monitoring and stuff like that?

SPEAKER_00:

So a runtime assertion would be um I'll give you an example of the the the majority of runtime assertions that I make are type as sessions. But they're not static type sessions. And this is quite important because people I think feel like typing is hard and difficult because and it and it really is, and it's because you're working with um two different worlds. Like in TypeScript, you have a world of types and you have a world of runtime, and that and those worlds are not in the slightest bit connected. Um and it's it can be so difficult to try to marry those two concepts together and get the type checker that only has static insight to be able to understand what's going on at runtime. Um but if we think about doing some kind of type checking at runtime, you have all the information there, right? You can check. You can write a type really easily that says, I expect this to be a string that starts with a capital letter and is between 10 and 15 characters long, right? I can write a type like that in one line of code um with uh with my library literal, right? It is basically um a way a way of building these like runtime types, which are just objects that respond to triple equals. Um and so I could define a type like string, starts with a capital letter, um length 10 to 15. Um and then I can create plain Ruby objects and say this property on this object, um, the name property, must be of this type. And when I create when I try to create an object, um it's gonna run that type check at runtime and it's gonna raise an exception if that type is not met. And so when you when you kind of put these tools together and you go throughout your code base and you basically say every time there's an object um that has an initializer and it's it has properties, we're gonna check that all of those properties are exactly what we expect them to be, with these very, very precise types that are able to um kind of do lots of like runtime stuff. Um if you do that, then you end up in a scenario where your application is very fragile in a sense, right? The slightest provocation will make it explode. But also that's like a that's like a benefit. That means that if anything goes wrong, you know about it straight away. Um and it's usually picked up by tests, but if it's not, it's gonna be picked up in production, you're gonna get an exception, you're gonna fix it really quickly. Um so it could be as simple as saying must exist, right? It can't be nil. How many how many times have we had like a method does not exist on nil error, right? You get rid of all of those errors. Um but it's much better if it's like this must exist and it must be of this type. It's either a string or a or a symbol, or it's uh you know, it's an integer and it must be greater than 50. You can you can just define these expectations and they're quite easy to define. Um and you can run them at runtime. There's barely any performance overhead. You also have other benefits, which is like whenever you're looking at an object, you can just scroll to the top to see what all of the properties are. Um so there's like really good documentation there. Um and if you code like this, basically you can get rid of most of your bugs. It's even better than static type checking because it can cover more scenarios, right? It it can cover way more scenarios than static type checking can.

SPEAKER_02:

So I think this idea might be kind of alien to a lot of people. Um so let me try to connect it to something that more people might be more familiar with. And tell me if this seems like a fitting example to you. Um I've often done this or something like this when I'm working on a mysterious bug where it's like, okay, I know everything has gone fine up to this point, and I know at this certain later point things are no longer okay. I just don't know exactly where in between things are going wrong. And so I add some runtime assertions in between where I'm like, um, the the example that I can think of is is like an a uh request and response. And so it's like okay, uh first of all, let me make an assertion that the data that I'm sending in my request is actually present. Um and then here's here's a more common one, I think. Like when the response comes back, let me make an assertion that it's a 200. And if it's anything but a 200, just halt and raise an exception.

SPEAKER_00:

Yep.

SPEAKER_02:

Um and then, you know, assuming that it's a 200, um let's make an assertion that the data is present or it's it's in JSON as opposed to anything else or or something like that. Do you think that's a fitting example?

SPEAKER_00:

Yeah, kind of. Um if you if you do it at the object initialization layer, basically you get really, really good developer experience for that. Um so let's say you're receiving JSON. Um, rather than just checking that it's JSON, why not pass that JSON, send it to an object, um, and that object specifies what all of the expectations, right? So it says I'm expecting um what's an example of an object, right? Um an address. An address. Okay. So I'm expecting there to be a street property that's a string and is not blank. I'm expect expecting there to be um a country property that is one of these this list of countries that it could be, right? Here's a list of country codes. Um and you and when you when you do that, um you and you might say, like um the city, um, that could be left out, maybe. Um so that's nullable, but the other fields are required. Uh you immediately find out very quickly if there's some data that doesn't match that that expectation. And and you've made an abstraction as well, because now everything that you specified about that address, um on your other object that takes an address, right, you just have to say it has a property called address, and it is an address. And you just reference the name of the class. And you know that if you've got one of those classes, then its initialization step will have blown up if it didn't match all of those requirements. So you know that since you've just asked for it to be an address, that if it hasn't blown up, then you have 100% got an address. It will 100% be a street. It will absolutely have a country that will be one of these countries.

SPEAKER_02:

Um and I want to ask you a couple questions before we have to wrap up. Um with with this, okay. So so these runtime inert runtime assertions, I find this really fascinating. Is there like somewhere where you have some examples or where like, or can I just like Google runtime assertions and find a bunch of info on this? Like, how can I learn more?

SPEAKER_00:

Um, I think there are a few examples on the website. Uh this is this is part of my library literal, um, which you can find at literal.fun. Um, let me see. Yeah, so on the first page, uh the introduction page, there's there's one that's just like a name object that has first and last, and they both have to be strings with the length of one or more characters.

SPEAKER_02:

Okay, my other question is like let's say I hear about this stuff and I'm like, I don't know, Joel. This feels like a huge time investment to solve like a problem that's like not really gonna happen that much. Like, this really doesn't seem worth it.

SPEAKER_00:

It's I don't think it is a huge time investment. Um like if you think about it, when you when you create an object, you have to define an initializer, and that initializer will typically take arguments, so you're gonna specify all of your properties as arguments, um, and then you have to assign them to instance variables. So just in the initializer, you've repeated three times um the um the properties, and then you're probably gonna want attribute assessors, maybe attribute readers. So you're gonna define them again. Um maybe you're gonna have a predicate, so you're gonna define them again. Yeah, you're like to have a basic like plain old Ruby object, you've probably specified the um the properties of that object like five times. Um whereas instead you could just specify them one time, right? You specify with literal, you specify prop and then the name of the prop as a symbol, and then it's type. Um and then you can say reader, reader true, um writer, yes or no. Um does it have a predicate method? Um how do I want to validate it? How do I want to coerce it? And you can do all of this in in one place, and that basically, like again, it's using metaprogramming to avoid errors, right? Instead of specifying your properties five different times for five different things, you just specify them one time, and metaprogramming does all the rest.

SPEAKER_02:

Oh, so the cost might actually be lower, not higher.

SPEAKER_00:

I think it's much lower. But then it also gives you the documentation benefits because you scroll to the top of an object and you just like, what is this? What is this property called data? What is it? And I can immediately see, oh, it's an array of addresses. Right? I can just see that instantly. And AI can see that instantly. It doesn't need to go and you know track down all of the call sites that are initializing this object. It can just look and see, oh, this is just described as an array of of addresses.

SPEAKER_02:

Yeah, interesting. Okay. Well, this is this is great. This is like a new idea that I was not familiar with that seems compelling. I personally I I've grown increasingly what's the word? Um well, I I've become less uh interested in using libraries in my programs um because of all the costs that come along with dependencies. So I'm wary of that. But um at a conceptual level, I I don't think there's any reason that this needs to be done with a third party library. Um you could implement one could implement all this stuff oneself and still get the same benefits without using a library, or maybe you know, sometimes using a library is worth the price, and maybe this is one of those cases.

SPEAKER_00:

Yeah, you you could definitely implement this. Stuff yourself. The reason that I think the library makes sense here is it has a bunch of like generic types that you can create. So like string length one, for example, being able to do that in one line. And all of these generic types are very, very carefully designed so that they don't do any allocation at runtime. And also so that if when they raise an exception, they they give you a really, really good error. So if I have a type called data that's an array of addresses, and then um there's a problem with the third item in that array, the error will actually tell you that it's the third item. It's not just gonna say, you know, there was an error, go try and find it yourself. It's gonna say, like, we expected this, we got this, uh, the third item was wrong, basically.

SPEAKER_02:

Yeah. And that itself is such a valuable concept of like designing your failure messages so that they are meaningful and informative. Um I I have to restrain myself from from pulling us down yet another rabbit hole. Um I always deeply enjoy these conversations. I hope we can have many more of them. Um before we go, final question for you where do you want to send people to check out uh flex, literal, anything else you're working on?

SPEAKER_00:

Okay, so um literal is at literal.fun. Uh flex is at flex.fun with a ph ph l e x. Uh you can see if you go to my website, joel.drapper.me, drapper with two p's, um, I wrote an article called um what did I call it? Yippie, our vision from modern full stack Ruby framework. Um, and this basically covers the the bigger picture idea, like what we're bringing Flex into and and literal and some other libraries I've been working on is this new from scratch framework, um, the router that I mentioned as well. Um basically like trying to figure out how we would build a web framework from scratch today in Ruby using all the techniques and stuff that we know now, um, including ideas like what you said about the database and the plain old Ruby objects. Like our database queries are gonna return plain Ruby objects, right? There's gonna be a very clear separation there. Um, so check that out, and uh, I'd love to hear what people think of it, if people have any other ideas about that stuff.

SPEAKER_02:

Um Yeah, wow. Okay. That'll have to be a future episode. Like I want to hear so much more about that. But anyway, again, really enjoyed this. And Joel, thanks so much for coming on the show.

SPEAKER_00:

Thank you.