Crafting Code Podcast

~/podcast

$ cd episodes/020-the-humble-programmer

~/podcast/episodes/020-the-humble-programmer $ ls -1a
. .. episode-summary.txt references.txt themes.txt transcript.txt get-mp3.sh
~/podcast/episodes/020-the-humble-programmer $ cat episode-summary.txt

The works of Edsger W. Dijkstra have made a huge impact on the software industry. In this episode, your hosts dive into his paper, The Humble Programmer, to discuss how well it holds up to today's programming world. We cover hardware, cleverness, abstraction, proofs, and more. Even when we disagree with his specifics, we find ourselves still nodding in general agreement.

~/podcast/episodes/020-the-humble-programmer $ cat references.txt ~/podcast/episodes/020-the-humble-programmer $ cat themes.txt ~/podcast/episodes/020-the-humble-programmer
$ cat transcript.txt

[00:00:16] Allan Stewart: Welcome to episode 20 of the Crafting Code podcast, where we discuss the importance of doing the right thing at the right time with the right tools. I'm Allan Stewart, a software architect, and lately I've been thinking about the importance of the numbers 0, 1, and N when it comes to coding and canines.

[00:00:36] Dave Adsit: I'm Dave Adsit, an e-commerce tools integrator, and this week I've been thinking a lot about small reversible steps and CICD pipelines.

[00:00:48] Matt Baker: My name is Matt. This week I've been writing some blockchain code, so I feel like I'm officially minted as a blockchain developer. The topic of this episode is The Humble Programmer.

[00:01:01] Allan Stewart: This is a paper written by Dykstra in 1972. If you'd like to get a copy of it, take a look at our show notes, and that way you can read the paper at your leisure and learn more about it. We thought this was an interesting one that we wanted to give some time to discuss on the podcast. So Matt, what brought this paper from many years ago to your attention now?

[00:01:24] Matt Baker: Good question. A friend of mine sent the paper over to me. I read it, and I happened to really enjoy it, so I've read it a few more times and just let it sink in. Part of the reason I like this paper is when I read it, there's some confirmations of my own professional opinion, so it's a bit of like, yeah, I preached Dykstra, and then some of it, yeah, it's interesting. This paper, like some others, they stand the test of time, so you go back and read this paper written in 1972, and it seems readily applicable today. Okay. So it's just one of those papers that seems to last, and I've enjoyed it, and then preparing for this podcast, it's been fun to really dive deep on it and dissect it and sort out my own thoughts on it. So before we talk about the paper, a few words on Dykstra. He's a Turing Award winner, which in the computer science world, it's pretty cool. He's known for creating the shortest path algorithm in a graph, so if you've ever used that, you have Dykstra to thank you for. Thank you for that. Among other things, that's just one that sticks out to me. Some people say that in the world of computer science, Dykstra is known as a character. He could tend to be abrasive and abrupt with colleagues and would kind of just come off as a jerk maybe sometimes, and if you program for a living, that can't be terribly surprising to you that some people in this profession are that way. I'm sure we've all been that way at times. A few other interesting kind of cool tidbits about him. He... In the 1990s, in order to conduct an interview for someone, he just gave them a puzzle, and when they solved the puzzle, he said, okay, I'll hire you. So if you're a fan of the interviews where you sit down and you're given a seemingly disconnected puzzle, disconnected as in not relevant to the job, if you like that, maybe you could throw some praise to Dykstra. If you hate that, well, do the reverse. One of the things I really like about Dykstra, and I think it comes through in this paper in spades, is... I've heard it coined as his love affair with simplicity. There's an anecdote on his Wikipedia page where his mother... Where he asked his mother whether or not trigonometry was a difficult topic, and she replied something to the effect of, if you require more than five lines to prove anything, you're on the wrong track. So I think she laid some of those early roads for, you know, appreciate the simple things and be wary of the complex as things grow, and it's hard to manage them intellectually, you know, beware.

[00:03:54] Allan Stewart: Yeah, Dykstra is definitely an interesting character, for sure, and one that you're likely to run into at some point in your career if you're doing computer programming for any amount of time. Personally, I think it's really interesting how relevant this paper has stayed over these years. A quick summary or outline of the paper. He starts off by saying, kind of talking about the slowness in which the programming profession emerged. He talks about how... From his perspective, there were three generations of computers setting the stage for how programming evolved over time, how software had changed over the years with the introduction of things like closed subroutines, new languages, BNF. And then he talks about his vision for the future. His vision was that we'd be able to build systems that currently strain our ability. I think that that's proven true over time. We've been able to build more and more. We've been able to build more and more complicated systems that just were not possible in the past. But his second argument, his second idea of what the future could be like was that reliable software would become cheaper because you don't introduce bugs in it in the first place. And they were kind of standing at this point in time where it felt like software was buggy and how are we ever going to keep making bigger and bigger systems, more complexity when it felt like... They were already drowning in complexity. And so he goes over three conditions for this vision. Like how could this become true? And this, I think, is kind of the bulk of the paper. He talks about recognizing the need for the change. They can't just keep doing things the way it had always been done, that there would need to be an economic need and it would need to be technically feasible. And out of technically feasible, being Dijkstra, he really goes in depth into what are all these things that are going to be required. And he talks about that. He talks about things like program correctness and abstraction, tools that influence your thinking, only considering programs that are intellectually manageable and a number of other things like that. So if you haven't read the paper, that's kind of an outline of where he goes in the paper. He talks about all of these things and summarizes with the idea that we need to approach the profession or the task of programming as very humble programmers, as a recognition of how

[00:06:23] Dave Adsit: One of the first things that stood out to me in this paper was the discussion of the hardware and how the hardware was all one of a kind originally and extraordinarily expensive. Programmers got away with a lot because the computers were so expensive that the cost of hiring a full-time programmer to write one simple program didn't seem completely unreasonable. And as I was reading the paper, I was thinking about the latest computer that I have acquired and its current generation MacBook Pro M1. It's probably the most expensive laptop I've ever used. And even so, it's still a very small fraction of a single year's salary for a software engineer.

[00:07:19] Matt Baker: Which he alluded to in the paper, right? He predicted. I think. Based on what hardware manufacturers were saying at the time that the price of hardware would reduce by a factor of 10 over some period of time. I can't remember from the perspective of 1970. And it seemed that seems true.

[00:07:38] Dave Adsit: Yeah. If you look at the number of cycles per second and the cost per cycle, I'm sure that the cost for hardware has dropped many orders of magnitude.

[00:07:49] Matt Baker: Interesting. I think some amount of engineers. That are working today have to think about this, but so many of us don't have to think about writing code specifically for the machine that we're on. We've successfully abstracted that away in a lot of places. And so reading this paper, I was struck by that. I was struck by the thought that man, like imagine every time you sit down to write code, you've got to learn how to write code against that machine. And then that constraint, the constraints of that machine are now your constraints. It's just something I take for granted. There's been times in my career where I've had a. I've had to think about the hardware, but I don't, I don't think that's been the majority.

[00:08:28] Dave Adsit: And building on that, Matt, I would say that a lot of us are maybe not the majority, but many developers are going to be writing JavaScript or TypeScript. And all you know is it's running in a browser somewhere. You don't even have a way of knowing conclusively what the machine is that it's running on, whether it's a desktop laptop, mobile, maybe even a watch. A lot of us are also deploying. And so we're just kind of sitting in containers, which the whole point of containers is that you don't care where the container runs.

[00:09:02] Allan Stewart: It's interesting that you mentioned mobile, because I think that is one area where we do care about the hardware more. But even so it's a different abstractions. If I'm programming for an iPhone, I might do things differently than if I'm doing for Android, but I'm still, I'm not worried about a lot of the underlying hardware. What chip set is this one specifically? Do I have to have a different. Instruction set in order to be able to write my code. We're working at higher levels of abstraction, even things like watches, right? Like a smartwatch today is so powerful compared to what they had at the time that he wrote this paper. And that kind of segues into, for me, one of the quotes that I really liked in here, he says when there were no machines, then programming was not a problem. When we had weak computers, programming became a mild problem. And now we have gigantic computers given the era, right? Gigantic could mean. Like literally it's filling a room, but I think also more importantly, gigantic in terms of how much Ram do you have? How much processing power do you have? How much disk space do you have to write to? Those things have increased so dramatically over time and it has become bigger and bigger problems to the point where we have things today. There's a lot of people out there that complain about how slow websites are because they're just Chuck full of garbage. And even if you take away a lot of the ads and trackers kind of stuff that is causing a lot of download time, then there's like all these other frameworks and there's just a mess of code for a lot of websites. That's not really needed. And then I think it's interesting to see some of the pendulum swings around things like static websites where you have just a much lighter load and all of a sudden it just pops right up. And it's just such a different experience than if you have a vanilla browser. Yeah. That doesn't have any blockers turned on or anything. And you're experiencing the web in the slowest way possible.

[00:10:59] Matt Baker: I like that quote too. I had it highlighted in my notes as well. The difficulty of the programming problem grew in proportion to the size of the computer. And I couldn't help but think of cloud. The paper talked about the software crisis being a term that was officially accepted, I think, in 62 at a software conference. Someone set it out loud. And then it went from like the shadowy side of the profession to, okay, yeah, we're actually in a software crisis. And when I think about this machine quote, I think about cloud and I wonder if we're entering this era of cloud crisis, right? Because when he talked about gigantic computers, I think you're right, Allan. He was talking about computers that filled a room. Imagine what he would think. Like, I wonder what the comment would be when it's entire data centers. And we're not talking like small rooms. We're talking massive data centers that you can instrument. It's trivial to write software in the cloud that can instrument that. So when I think about like the power that we have at our fingertips and with that, the ability to screw things up that we have at our fingertips, I just wonder if we're just the software crisis might be a blip as we enter this new era of like, you can, your monstrosities can be global now.

[00:12:12] Allan Stewart: Right. Everybody's filling their rooms with computers now. If you're over at Amazon, then they're making data centers that are just full, truck full. Right. Google and Microsoft, anybody who's in that space. But just even at home, we're putting computers in our refrigerators and on our wrists and we're putting them on our desks and all kinds of places that they're just everywhere.

[00:12:36] Matt Baker: Yeah. And I think following his reasoning, each of those is the place where we can screw things up. The main thrust of this paper for me was like, be very aware of how you're writing software and very aware of like its implications. Yeah. For any. No. Number of reasons. And I just, you're right. There's just been an explosion of where the CPUs are now. They're everywhere. And it's just, there's so many chances to make a muck of things.

[00:13:01] Dave Adsit: So when we talk about the computers, one of the things that he said is in retrospect, one can only wonder that those machines worked at all, at least sometimes. And I thought, oh, that sounds like a lot of the programs that I've worked on. Yeah. You wonder how they work at all, if they even do. Then he dove into a little bit about how the machines at least were visible. And so if someone came to visit, you could show them the machine and they could be impressed by it. And even now, if you take someone into a data center, it's loud and it's cold. And it's impressive in a weird way. And you definitely want to leave the room quickly. But if you go and show someone, your software development staff, what do they even see? It's just a bunch of people, heads down, probably with headphones on, staring at big screens. And it's really hard to see what the magnitude of the software itself actually is.

[00:14:21] Matt Baker: I liked his little blurb early on in the paper about the programming profession. You talk about going and showing the programmers. And he said a few times, the poor programmer was kind of just relegated to the corner. And the machine was fancy. No one cared what the programmer was doing. And he shared this anecdote in the paper where when he was getting married in 57, he was required to list his profession in Amsterdam. And they actually wouldn't let him put computer programmer. So he had to default to what he calls the ridiculous title, quote unquote, theoretical physicist. They talked a little bit about how he had to choose. He had a tension in his studies where he had to choose between pursuing being a theoretical physicist, which is a much more established career path in school of thought than being a programmer. And he had a professor that tipped him over to programming by saying, Hey, you might be one of the people that's here to help establish programming as a profession, just as worthwhile as theoretical physicist. But it's funny. I think it's definitely morphed a lot since then. I think programming is a much more accepted profession. But there's still times where I feel like just the person in the corner that no one wants to put up with. Yeah, definitely.

[00:15:41] Dave Adsit: One of the other things he stated there is that the programmer knew that his work was a very little, change it, it doesn't change. There's no concept of entropy in the program after it's been compiled, completed, committed, whatever. But it still doesn't have much long-term value because

[00:16:34] Matt Baker: everything is always changing. Yeah. I can't help but feel like instead of dealing with unique computers that limit our code's lifespan, we oftentimes are writing code inside companies that limit the code's lifespan. Maybe the most extreme example would be if you're working on an internal tools platform, that code's never going to see, well, most of the time it'll never see the light of day outside of the company. And it's very purpose-built for that company. And as that company changes, like you're saying, Dave, then you have to bring the code along with

[00:17:03] Allan Stewart: you. And in a lot of cases, that invisibility of the magnitude of what the software is and what it does and what it means, I've just seen it over and over cause trouble, cause problems. And we talk about things like the technical debt metaphor. We talk about the need to help business people or product people or just any people at all to understand what it means when we make certain technical decisions, just because they don't see it. They don't even see the computers anymore. It's a screen that they think about, right? It's like, I'm thinking about the Apple iMac. It used to be the, computer is a big deal. And it's like this, the whole case and plenty of people have that, but there are also computers that are just the screen or your tablet or your phone. It's just the screen. And that's what a lot of people think about it as. And it's hard for them to understand what's going on down at like a processor level or like lines of code being executed. As you build up these complex systems, what does it mean? What kinds of trade-offs are we going to have to make? They're really hard to visualize. Unless you're a person who's actually interacting with that code and building up that sort of mental model of how does this actually work in practice? What are all of these bricks that you've laid in the castle in the sky that you've created that nobody else can even view, but they're using it. And the effect of all of that work is apparent somewhere.

[00:18:39] Matt Baker: I think about the programmer in the seventies, how frustrating it must've been when the people paying their paychecks switched the machine on them, right? And all your code is purpose-built for that machine. And I would venture a guess that they were probably expected to port the code quickly. And it's, you know, sometimes that works and other times that doesn't, and I can't help, but think of a changing company. We don't deal with that, right? We don't deal with someone coming in and saying, I switched it to a new piece of hardware. And so you have to rewrite all your code for that for the most part. I don't deal with that personally. I've never had to deal with that problem in my career. Some people do. But I do think about it when the most glaring thing for me is reorgs. Software in these companies is a reflection of the company structure, right? It's an attempt to capture like what the company needs to produce, but it just seems to always follow that it's also a reflection of the way the company is structured. So when you change the structure of the company, just like in the seventies, if you changed a machine, you had to change the software. And I do know what it feels like to have someone who doesn't know anything about the code that's underpinning all the team composition when they don't know anything about that coming in and changing the team composition. And then you're just, you're left to foot the bill and figure out how to make it work. And that's insanely frustrating, right? And it makes me feel like, to me, an analogy for that is walking out to my car. I know we pick on reorgs a lot. Maybe it's just a sensitive topic for me, but for me, it's analogous to walking out to my car, throwing the hood open, not knowing anything about how the engine works and just unplugging and rearranging the engine, plugging things into different places, and then just being amazed when it doesn't turn on. Right. And it's like, well, that's what I feel like when someone goes in that doesn't know anything about software or the software in a particular company and the way it's all put together intimately, like Allan's talking about, like, you know, the actual files, you know, what methods are where, and they change things. It's just like the poor programmer, right? To borrow Dykstra's term, the poor programmer in the corner is just left to figure the crap out. And I think that's a really good point. Well, the people that changed the machine or changed the company structure are off with like pretty little slide decks describing how great it's going to be.

[00:20:54] Dave Adsit: One of the things that that has me thinking about is just different mindsets of programmers. One of the things that I hear often is this idea that we're going to build for reuse. We're going to build for either reuse in the small or reuse in the large, or we're going to build the system in a perfect way so that it doesn't have to change. And, you know, the counterpoint to that is I see other developers who build systems with the intention of changing them regularly and rapidly. Earlier in my career, we talked a lot about you should have every feature that you can imagine and then configure it so that the user gets the ones that they want. And that was intended to be done in like a runtime environment. You would change it so that it works in different ways. And now the more mature programmers that I see are talking about, you know, the building fewer things and changing to meet the needs as the needs change. And I think that that's one of the advantages of software over hardware is that it is in fact more malleable and we can change it if we have the mindset of I'm doing the best I can today. I'm going to learn more tomorrow and I'm going to change the program as I learn. And that's going to be either I learn to be a better programmer. I learn more about the user. I learn more about the functionality that they care about. I learn more about the business. There's a lot of reasons to change. And so we should be thinking about how to make our code malleable, soft so that we can take advantage of the fact that it is software and not hardwired hardware circuits. And Dykstra talks about the mindset that people had about programmers in the 70s, the 60s and 70s. And he said that there were that he had two main opinions about what programmers did. And one of them is that a competent programmer would be very puzzle-minded and good at solving tricks or creating clever tricks. And the other one is just that the programmer was an optimizer because the algorithms were known, the solutions to the problems were known, et cetera, et cetera. They were just like taking a known solution and applying it to a computer. When I thought about those, I was like, oh, both of these feel incongruous with my opinion. One of the things I hate most when I encounter a new code base is a bunch of clever tricks. Like if there's a bunch of cleverness in there, I'm just going to get very frustrated. Or if you've done unnecessary optimizations because it makes you feel better versus being needed by the consumer of the program, both of those feel

[00:23:45] Matt Baker: Yeah. I don't think he meant so many words when he talked about that. When he was talking about programming languages that permit those clever tricks. I'm just going to read from here. He said, I'm sorry, but I must regard this as one of the most damning things that can be said about a programming language. And I really liked his take on clever tricks and the tools and programming languages that allow them and his battle cry for not only not using clever tricks, but getting rid even permit those in the first place.

[00:24:17] Allan Stewart: Playing another quote, he says, the competent programmer is fully aware of the strictly limited size of his own skull. Therefore, he approaches the programming task in full humility, and among other things, he avoids clever tricks like the plague. I think that's just, it's so important. Like, I mean, clearly Dykstra liked his puzzles. He liked a certain amount of cleverness. I think that there is a place for that inside the stable of programming, as it were. You need to be able to figure out things and understand these complex ideas that don't have a really good physical analog. For some people that I've worked with or encountered over the years, that's just a roadblock for them. And programming is always difficult for them because they just haven't figured out that code sense, as it were. But when it comes to actually writing code, you need to put your cleverness in solving things in a particular way, like understanding a puzzle really deeply, understanding a problem so that you can express the problem well in code and have a solution that actually fits the problem. I think that part of cleverness makes sense. But there's another part of cleverness that just makes code so hard to deal with. And going back to Dave's comment, a lot of it has to do with coupling. The more that we've coupled things to other things makes it hard for them to change. They're not malleable anymore. It's no longer software, some brittleware instead. And when the first blow of the wind comes by, then everything shatters and it's hard to deal with. And to take it one step further, I think what Matt was saying about reorgs, it's almost criminal how people just don't appreciate and how many people just don't even recognize the importance of a concept like Conway's law when it comes to the coupling between your software and your organizational structure.

[00:26:21] Matt Baker: His fifth argument for the technical feasibility of his vision was we should not write code to solve problems until the problem has been nicely factored. And I spent a little bit of time looking at the code and trying to understand what he meant by nicely factored, and I couldn't find any definitive source on it, so I can only assume. But going back to what you said, Allan, when you talk about cleverness, that's a great place to invest cleverness is to make sure you clearly understand the problem so that you can write code that solves it as simply as possible. But I think the problem starts to arise, and he called them one-liners, and I just think that's the best way to describe it. I'm sure we've all had an experience where some bad, bad, bad, bad, bad, bad, bad, bad, bad, bad, bad, bad, bad, bad, bad, to factor the problem until it's so dead simple that the code just falls out. And it's relatively easy to understand. I, that's what I took from his argument was that your code will be so much more manageable if you can contort the problem into a factor that makes it easy to write the code for, and I think that's cleverness, right? You can be clever there. You can use those puzzle solving abilities there to really decompose the problem you're trying to solve.

[00:27:54] Dave Adsit: Well, and I think that one of the things that we can feel boxed in as programmers is like, well, I have to work a certain way at work, but there's other outlets, like we talk a lot about going to the Utah SC meetup or other meetups like that. It's a great outlet. One of the things that I think is fascinating is there used to be an international obfuscated C competition where you would write literally the most cryptic C that you could as an art form. It's beautiful as an art form. But if I encounter that. Yeah. In my production code, I'm going to want to rip the code out of the code base and probably your heart out of your chest because code is intended to communicate. It's not intended to communicate with computers because they don't care about our symbols. It's intended to communicate with other people. Cleverness is the antithesis of communication. It's honestly, in some ways I think of it as something that a, a person with limited. Yeah. Self-esteem might do as a way to show that they are some in some way better than others around them. And that makes me sad when I see it. But I think in general, we're trying to communicate with other programmers so that software can be continuously evolved to deliver new value to customers. Once we realize what that value should be.

[00:29:22] Matt Baker: Yeah. I feel like when you encounter one of these one-liners in code, that's super clever. It, you really start to not care about its aesthetic when you have to debug it. You know, like you ha you almost always have to break it apart anyway. Right. That one liner goes back to the 10 or 15 lines. It should have been. And then you have to put in the debug statements to figure out what's going on with it. And it's just frustrating. Yeah. It's just frustrating. I'm thinking of like, not to pick on.net, but I'm thinking of some link link statements I've seen that were intense or like some list comprehensions in Python that were just wild. And once you understand what they're doing, there is. I can appreciate the appeal and I can understand it's not lost on me, the, the aesthetic of it. But at the same time, when I'm getting paid to work in that code base, it's not fun. Cause like I said, you always have to tear those one-liners apart and blow them back out to what they should have been in the first place in order to debug and I'm going to work with them. And hopefully you leave it like that once you do it. And it's the last time you have to deal with that one line.

[00:30:24] Allan Stewart: I think this ties in with the concept of abstraction. And there's a quote in here that I just absolutely love. I used it in a presentation before, and I don't know if I'd ever read this paper, but like I found this quote, he says, we all know that the only mental tool by means of which very finite piece of reasoning can cover a myriad cases is called abstraction. As a result, the effective exploitation of his powers of abstraction must be regarded as one of the most vital activities of a competent person. So I think about abstraction. I think about just the massive complexity that there is. A lot of the problem with those clever one-liners is that there's a ton of complexity all nested in, in a way that is very difficult to read because sometimes those list comprehensions make total sense. They can almost seem like fluent code when they're written well, and you can say, oh, I totally understand what's going on here, but it's very easy to just go around the corner from there. Into the pit of disastrous despair where nobody can understand what is even happening in the code. I think that the readability and the abstractions go hand in hand. Ultimately, the world is way too complex. Even the physical world is way too complex for us to really get our heads around and understand things. An example that I like to bring up is tying your shoes. Is that just one thing that you do during the day? Or is it the interaction of billions of ads? Or is it the interaction of millions of atoms as you're trying to thread the laces and take the bunny around the tree and tie the loops? That is massively complex. If you try to think about it at low levels of abstraction and you're trying to think, well, what are the interactions of these atoms as I pull these laces tight? That's crazy. That's so complex. But as humans, we live in abstraction. We take that for granted and we don't even talk about tying your shoes. We say. Just get out the door. And there's this implicit assumption that's like, oh, well, if you're going to get out the door, you're going to have to put on your shoes. And if those shoes have laces, you're going to probably want to tie them. It's not a thing that you have to spend brainpower on. And I think code has to be the same way. You have to write the code in a way where you create a strong abstraction that isn't leaky. People talk about leaky abstractions where the details of how the thing works are leaking out. In addition to having to know how to use the abstraction. You also have to know how to use the abstraction. You have to know how to avoid the problems that are coming out of this leaky box. Code just has to be different from that. You have to find a way to express yourself in a clear way. In the paper, he says you're not making abstractions to be vague and just like this hand wavy. Oh, you just, you know, do the initialization thing. But you're creating a new semantic level in which one can be absolutely precise so that when somebody sees your bit of code and they read the name of the code, it's like, oh, I'm going to do this. And then you have to understand the name of the function that they have this high degree of confidence. Like, I know what this does. I can use this and move on with my life. I can understand what's going on rather than having to say, oh, okay, buckle up, everybody. We're going into the function to see what it does because the name is a lie and there's probably 17 side effects and we have no idea what's going to happen when we call this function. But it has a name like load. I don't know what's going to be loaded. I don't know where it's going to try and load it. Is this loading from S3? Is it connecting to a database? We don't know. So buckle up.

[00:34:04] Dave Adsit: When I read that section, it got me thinking about pattern languages and the concept of the ubiquitous language from domain driven design. And I was thinking, yes, we need to have concise and precise ways of communicating. And that means that we're using a language that everyone needs. And that means that everyone has the possibility of understanding. Everyone involved in the development of this software, not just a couple of programmers who happen to initially name this variable X and that one Y.

[00:34:40] Matt Baker: I had a real similar take with DDD, Dave. I started thinking about how do you create good abstractions? Because in the paper, he said something to the effect of in the 70s, certain people, some people were proposing that a program's conceptual complexity. Yeah. Was calculated by squaring the lines of code. And he took issue with that and said, you know, if you can use proper abstractions, then you can make your programs conceptual complexity, proportional, directly proportional lines of code instead of growing exponentially. And so I started thinking just like you, Dave. Well, how do you create a proper abstraction? What is a proper abstraction? Because it's not load like Allan was saying. But I do think the closer it is to the ubiquitous. Language, the more correct it would be. So like if you have a shopping website that has a cart or a basket, when the spec or your product manager says, check out the basket or check out the cart, the code should say, check out basket, check out cart. And how that basket is persisted, where it's stored, how it's loaded. Those are of no consequence to that level of abstraction. Right. I think that's the point he was. Driving home, like tuck that away. It doesn't matter when someone's reading code at that semantic level. They, in my opinion, they should be able to pull out the spec or they should be able to sit with the product manager and say, tell me what should happen when a customer wants to buy a water bottle. And then the code should read just like they talk. And I think that's a sign that you you've got the correct abstraction. And then when you're at that semantic level, thinking about a basket, you don't have to consider the how it's implemented. And I feel like that's. What Dykstra was saying in response to people saying that a program's conceptual complexity is calculated by squaring the lines of code. I feel like he was saying, no, no, no. If you get the right abstraction, there's so much you don't have to think about when you're focused on one semantic level.

[00:36:45] Dave Adsit: Well, and that got me thinking a lot about people systems and communication lines. And yes, structure is critical and not just critical. At the program layer, but at the software development system layer, as you grow the number of people on a team working on a single code base, the communication lines grow geometrically. But if you can break the code base down and have two smaller groups working on different separate isolated parts, then you can maintain a smaller number of communication lines overall. And I was thinking about how that applied to the systems, the software and the people system building the software. I don't know. I think it's all intertwined. We are lucky to be software developers in the time when we are because a lot of these people have come before us. I think somebody already mentioned Conway's law and how some of those concepts apply to some of the things Dykstra is saying about complexity of a program. And how the complexity of a program is.

[00:37:57] Matt Baker: Yeah. Can you imagine sitting in a meeting? Let's say you are in a place that has three teams. And in order to discuss anything meaningful about the product, you have to go to the length of how the bits are stored on disk. The conversation is going to be impossible to have unless you can click up to that semantic level like Allan was talking about. And have the system composed in such a way that you can mentally or conceptually understand the system. And then you can actually rather think of those things as distinct things and not have to go so deep on how they are implemented. So I guess what I'm thinking about is this concept of abstraction doesn't stop in the code. Like I think even in meetings where we are talking about teams or we are talking about entire companies and how to integrate them, we are still at a level of abstraction and the cleaner those abstractions are, the easier the conversation will be.

[00:38:50] Allan Stewart: I'm seeing that really firsthand in the work that I'm doing currently. I'm working in a situational environment. where this is a kind of late stage startup. It's finding its place in the market. Customers are coming. And in the traditional fashion, the code is a whole bunch of spaghetti that was thrown on the wall to see what would stick. It's a mess. The messy code causes problems. But then when we try to talk with the people on the business side about like, well, what is this supposed to do? They are also impacted by the complexity of the system because a lot of these abstractions were not defined very well. The ubiquitous language from the DDD concept is just not there. And so when I'm looking in code, I'm dealing with lots of levels of abstraction at the same time. It's like, okay, what is this function supposed to do? Like, what is the concept of the business in the first place that we're trying to achieve? But also in the middle of that is a SQL And so it's loading some data from the database and you have to understand, well, what are all these pieces and which data matters for this reason and which data is used for something else. And so they're all just mixed together. And then when you talk with the business people, they're impacted by that because you ask a question, hey, is this supposed to work like this? And they say, yes. Oh, hold on. There's an exception that in this one particular case, on this one page of the website, if you click this button first and then this other button, then it's supposed to do something different. And so it goes back and forth affecting each other as you're trying to deal with those situations. It can be incredibly frustrating. I kind of like working in those scenarios because I like bringing some order to all of the chaos and defining those terms and getting it into a place where you can really understand it better and better. I find that very rewarding, but there's certainly some frustration to it. And it makes it more difficult for a lot of developers

[00:40:54] Dave Adsit: to be even capable of working in the system. I wonder if this is one of the areas where I would have a disagreement with Dijkstra, because I think, or at least in my experience, the process of creation is messy. When it comes to businesses, programs, new ventures of many That messiness is inherent to the process, I believe. And I think that if you don't have a certain amount of messiness, you don't have the flexibility to create something successful. And so many new businesses and new programs, new companies, new everything just fail either because they don't have enough rigor or they have too much. So I think there's some kind of a sweet the reason when you work at a scale up, you are dealing with a bunch of messy code and people aren't even exactly sure what it's supposed to do is because they had to try a lot of things before they could even figure out what was creating value.

[00:42:09] Matt Baker: It's a good point. Like his stress on finding a nice way to factor the problem for writing code is a bit, if I understand you correctly, David, it doesn't really ring for me either because sometimes it's through the process of implementing that you realize how far off you were and you just have to go through that and so when you say messiness dave i think of like the you know the footprints that get left when you're shipping code and and building things to try and understand a problem and in doing so the real properties or additional properties that end up changing the way you ultimately write the code emerge and you can't get those unless you knock on the door like you you can't sit at the whiteboard and anticipate all of those you have to go try like you're going to create messiness and i think that's inherent to me that's part of the it's part of the process you get it wrong to understand how close you were to getting it right and then you just slowly move your way there and i think one of the important things you have to do is take the time as those properties emerge to refactor you know maybe instead of diktra saying factor up front and get it right before you write code i think i'm falling in line with you dave or i would say like refactor early and often when properties emerge well and i would say that you're also

[00:43:26] Dave Adsit: going to move into that stage where you are refactoring the code so that it is in a good state that's reasonable and understandable but it also speaks a lot to some of his other points which is don't be clever because the programmer who's is going to get caught in your trap is probably you. Be very communicative in your code so that you can continue to evolve it. And he also talks about the concept of growing a proof of correctness with the program. And to me, that says you should be using TDD concepts. You should be writing tests before you write code. A, so that you've expressed your intent, and B, so that the code does what you intended. It's so often that we test after the fact, and our test is just confirmation rather than disconfirmation. I think overall, I would say I'm fairly well aligned. I just think that there are so many problems that we are building software to solve right now that we don't know everyone else until they start trying some stuff.

[00:44:45] Matt Baker: Kind of an interesting contradiction that you're highlighting in the paper that I haven't thought about until now. Factor the solution nicely before coding versus grow the proof with a code, which both things that he highlighted in the paper, and I definitely know day to day, I spend more time growing the proof with the code than I do factoring the problem up front.

[00:45:07] Allan Stewart: I think that speaks to the concept of discipline. Because if you are very undisciplined in how you write your code, you can generate mess so much faster than you can clean it up. You can generate complexities that become very difficult to understand rapidly, but getting it under control is hard. And so I think you do have to do a little bit of what he's talking about. Like you do have to factor some things as you're building them. Don't just throw out a mess and hope that it works. You're going to have to do a lot of iteration. You're going to have to do a lot of exploring. You can't say, hey, we're going to sit in a think tank and figure out what the right thing is to build. And then we're going to go and build it. And then it's all done and it's perfect. But when you're in that messy part of the process, if you are also messy in how you're writing the code, you're going to have a huge problem. Martin Fowler has a kitchen metaphor that I really like. And basically the gist of it is that if you're going to cook something, you're going to make a mess in the kitchen. And now you have a decision. Do you clean up that mess or do you leave it there? And at a certain point, when all the dishes are overflowing in the sink and there's salmonella all over the countertops, then you're going to have a problem cooking something new. And if you're in a startup and you're trying to explore like what kind of kitchen you want to be, all those messes are going to make a big problem. And so you have to have enough discipline to keep things clean enough as you go. Okay. You're not trying to figure out what it was we did two weeks ago. We fully understand that and it's good. But at the same time, we're not trying to go too far on the other side of the spectrum where we're trying to design everything up front. Or even worse, we get completely out of the business mindset and we're just into what should the technical thing be? Because we want it to make things that we love technically with no regard to does it actually solve the business problem?

[00:47:09] Matt Baker: Yeah. He has a quote in the paper. What was it? It was something like our job isn't to write code. Our job is to compose computation to display a desired behavior. Do you remember that?

[00:47:20] Dave Adsit: Yeah.

[00:47:21] Matt Baker: I thought that was an elegant way of saying that, right? Like your job isn't to write good code. Your job isn't to write code that people understand. First and foremost, your job is to compose computation in a way that displays the desired behavior.

[00:47:34] Allan Stewart: Yeah.

[00:47:35] Matt Baker: I was thinking a little bit about ways that I do that. Like I maintain that discipline. Two for me are TDD and REPL-driven development. Surely there's others. Those are the two that come to mind most. And I don't see either of them as mandatory. But as I was thinking about those two things, the thing that I think is mandatory is that every time you commit code to a code base, it has to be stable. Like that's one of my disciplines. You can't write a commit into main that isn't functional or is partway functional. I think that's bad discipline. That's salmonella.

[00:48:39] Allan Stewart: Right now, the thing we usually do is we make a program and then we test it. It's an effective way to show presence of bugs, but hopelessly inadequate for showing their absence. The only effective way to raise the confidence level of a program significantly is to give a convincing proof of its correctness. Now, I did proofs in college when I was learning about computer science, and they were tough. And I did not understand a lot of the concepts behind it. I could go and write some code, but creating proofs for anything that was beyond a very small for loop was just incredibly difficult. I liked the concept that he was on. I think he was just on the wrong track. It's like, you're not going to be able to prove your program. There's too much complexity. There's too much, you know, real world messiness, right? Just IO. I went to talk to the database. I did everything perfectly. But at that time, somebody pulled. The network cord out at the data center, and they were plugging it back in. You have to deal with that temporary disconnect, right? We talked about the fallacies of distributed systems. There are these real life things that are happening that you have to account for. And I don't see how you can prove correctness. But when I look at the other things that he says in the same paragraph, like, you know, the same breath practically as he was, you know, giving this talk, the programmer should let correctness. Proof and program go hand in hand. I really liked the idea of what is a convincing proof. And now let's construct the program to meet the requirements of the proof. Instead of trying to test after that, to me, screams test driven development, right? Test first, figure out how do you want this to behave and then write the code that behaves that way. Ultimately, I just, I feel like it's one of those situations where TDD is the worst solution, except for all the other ones we've tried, because ultimately we just need it to work. And that gets you to this other part earlier in the paper that I liked. He says that those who really want reliable software will discover that they must find means of avoiding the majority of bugs to start with. And as a result, the programming process becomes cheaper. You should not waste your time debugging. You should just not introduce the bugs to start with. That resonates with me and my experiences around test driven practices. And which isn't always. Maybe shipping tests with the code. Sometimes I've noticed that there's a lot of value for TDD and just like that fast feedback loop of can I see this thing working? Sometimes a visual inspection is way better, especially if you're looking at UI code. It's like, okay, I'm going to build this HTML and is the Dom correct? That is very hard to test with automated tests, but my eyes can see it and that thing is blue. And so let's move on.

[00:51:35] Matt Baker: Yeah. I've been doing a lot of REPL driven development. Yeah. I've been doing a lot of REPL driven development lately and there's no tests in the code base, but don't let that mean that there's no testing. Every time I change the code, if you were to see some of the functions I'm working on, there's the non-test code. And then there's this little thing of code at the bottom. This code that produces test dependencies in order for me to be able to invoke the function and the REPL to test it. Right? So like I'll swap out the database driver, I'll swap out a web call, whatever, so I can test it locally. But every time I commit before it goes. I know that using test doubles or mocks or whatever, this code works great. So I have to ship it and then find out if the dependencies actually play along in the real world. And that's not perfect. Like you're saying, Allan, it's not the best, but it's better than anything we figured out so far. And I agree because I oftentimes there's a little wire I miss. There's a connection string that's screwed up, but like the meat, the meat of I get data from here, I munch it and then I do this with it test. That. All gets tested in my REPL. And I'm pretty confident when I ship that it's going to work aside from like, okay, like I said, you know, I missed the URL on that one and I've got to go change it. And so that's what, when I say I'm, I don't commit to main until I know that it works. That's what I mean. Like that code won't get checked in until I see it work. And I, you know, you just do that in little increments.

[00:52:58] Dave Adsit: Well, and I think that that's one of the things about a well-factored program is that as it grows and becomes more stable. The testing. Of that system becomes more stable and you can commit those REPL tests to automated regression tests so that you can follow Larry Wall's advice. Be a lazy programmer. Have one of the great virtues, right? Make the computer do things for you so that you don't have to do them yourself. But when you're in the initial phases, you actually create more work by trying to, you may. For certain classes of problem, but you. Create more work by starting with an automated test suite because the tests have to have to evolve very quickly. And as the code evolves very quickly. And so that's going to become a judgment call based on experience. Well said. Well, if you don't have the experience, I would still say, write the tests because that will give you the experience. But I think there's a, there's a lot of benefit to that. I was, I was thinking about that section where he's talking about proof. And if you replaced the word. Proof. With the word test or verification, automated verification, then everything about it rings true to me. I also attempted a CS degree and found that I was absolutely terrible at writing proofs. So I'm glad that that has not once ever come up in a professional setting.

[00:54:29] Allan Stewart: Another thing regarding professional coding. I think is interesting. It's just like a quote. I'm plucking. Out of the paper. He's talking about how the tools that we use matter. At one point, he says that people might conclude that once our tools are much more adequate, programming will no longer be a problem. He's like, no, no, no. Programming is going to remain very difficult for one important reason. As soon as we have gotten over our current hurdles, we're going to say, great. That's no longer a problem. We can do these things. We've got good tools to do a kind of thing. And then we're going to do something that is way harder that we couldn't even start to think about before. And we've seen that occurring over time. It seems like it just happens over and over in the industry. Oh, this thing is hard. It's impossible. You can't do this. And then people figure it out. They solve it. They figure out how to do it. And over time, eventually they bottle it up. You know, something that was really hard in the past, like having databases that have lots of redundancy and reliability. And their backups work. And like that used to be somebody's full-time job. And now it's Amazon's job or it's GCP's job. Hey, I said I wanted a database. Where's my database? And I wanted replication and backups and all these things working for me so that I can go and do something that is way more complicated than just doing database backups, which is hard enough on its own. And so programming always remains relevant. Because we're going to always be trying to do something that we just couldn't do before. Because that's where the monetary value comes in.

[00:56:14] Matt Baker: Almost like an abstraction. It's like an abstracted tool. Because you're right. Like, it's been years since I've had to fill out a ticket to get a new database created. You know, or create the database myself now. It's just like, if I can't get it in five seconds on AWS, I'm already impatient. Where's the thing? I need to keep going. You know? And then that changes what I can work on. That changes where I can spend my time. This point, I think, is probably my favorite one in the whole paper. I'm just going to read from here what I like. I absolutely fail to see how we can keep our growing programs firmly within our intellectual grip when by its sheer barokeness, the programming language, our basic tool, mind you, escapes our intellectual control. So I'm kind of pulling this a little bit to go just beyond programming languages to tools we use. But, oh, I guess I can't underscore how much this means to me right now. This might be my most pretentious reference that I'll ever do on this podcast. There's a philosopher, Wittgenstein, who wrote a kind of a small book on this point. And I'm not to read another quote, but I'm going to read his too. And he says, he's talking about the limits of thought and trying to discover the limits of thought and language in order to express what hasn't yet been expressed or in order to express the unexpressible. ! Which makes sense in philosophy, right? Like you're exploring boundaries. And he said, for an order to be able to draw a limit to thought, we should have to find both sides of the limit thinkable, i.e., we should have to be able to think what cannot be thought. And so I think about like, yeah, the language you use, staying on Wittgenstein for a minute, totally controls what you can express and the way you can express it. Right. And if you get a more succinct language, you can express more and more easily and more quickly. With our tools, I've been in this camp right now where I'm like on a tool. Minimalism kick. So like if you put a full ID in front of me, you'll spend the day listening to me, like make snarky remarks about it. Like, oh yeah, you needed that, right? Like, and I'm, I've been living in Vim on a Mac with OSX and I have installed no tools. I use the terminal is right out of the box. I just, sorry, I installed Docker and I use Docker. I use Vim for everything. And what I've been finding is that when your tools are simple and what you can do with them as simple, your product is simple. Yeah. But as your tool starts to exceed your intellectual grasp, so does your product, right? So if you're in a programming language that has 20 ways to do things, that's 20 different things you have to keep in your head when you're considering like the intellectual or the, like the conceptual integrity of the code, right? There's I'll just keep piling on here if that's okay. Rich Hickey, the creator of Clojure talks a lot about complecting. He says that, so first there's this predicate. The, the goal. And your program should be simplicity and what you build, you should strive for simplicity, both in like the code and then in the product, right? The way you get away from simplicity is to start doing this complecting or interleaving things together. And that starts to produce complexity. And the more you complect or the more you interleave or the more features you have in your language or your IDE or the way you ship code, the more complex it is. And the more you're prone to say things in a suboptimal way, right? Whereas if instead you're forced to. Do everything in only a few ways. Hopefully that's a, like a pinch or a forcing function that controls the complexity of your product, right? It might be harder. In the middle. I'm not going to say it's always easier to write code in Vim than it is in visual studio. Absolutely not. Especially like, depending on the type of code you're working on. But I do think that if you can constrain the number of tools you use, you will constrain the complexity of your product. You'll control it. You'll control how, how complex your code is, how complex, you know, your product is. And when I think about how to like, okay, that's cool. That's abstract. The Wittgenstein quote is fun. Makes me sound smart. Great. But like, how do you practically take from that to make more money for companies writing software for them? Right? Like how do you deliver code more quickly? But, but, but, and for that, I think about choosing tools lately, similar to the way a friend of mine taught me how to tune a guitar. I'm not a great guitar player and I'm sure I'm going to screw this up, but he showed me like when you're tuning the E string. I could never tell when I plucked it, that it was a E string. I just don't have that ear. Some people can say, yeah, that's E, some, I can't. But what he showed me is that you can play it, then you down, you take it out of tune. So you down tune it to where when you play, you're like, that's obviously not E, that's wrong. And then he says, then pluck again and just slowly start turning it. And then your brain will register, oh, now I'm back in the correct territory. And it's a pretty cool effect. Like now I can tune a guitar by ear and I can not hear an E standalone to save my life, but I can tune a guitar pretty close. And so I've been trying to do the same thing with my guitar. So with Vim, Vim is the down tune. I spent most of my career in bigger IDEs, but I saw a lot of problems that they brought both in like the end product and trying to write code and trying to set up someone's new dev machine. Like there was a lot of stuff that went wrong. Right. But as I've gone to Vim, now I'm out of tune in the guitar and I don't know that I'll stay in Vim forever. Like some people really like Neo Vim. Some people have said like, give it up, just use Visual Studio Code. That's a clear winner. And I don't know. I don't necessarily disagree with that. Right. But I'm going to get there by discovery like Dijkstra talked about. My tool selection and its correctness is going to grow with my problem and with my understanding as opposed to upfront saying I know exactly what I need here. I need this big old tool. And so this idea that he talked about like your tools, your programming language, what did he say in there? He said something like PL1 was the worst language he'd ever seen because the amount of features. And then he talked about a person giving a talk about PL2. And he likened them to an addict saying like this person is so addicted to features within the first. Like he stood up and said, I'm a fan of PL1. And then he spent the next 50 minutes or the next some minutes talking about 50 new features he wanted, like an addict, just saying, just give me more and more and more. And then he flipped to Lisp and he said Lisp has very few. If you know Lisp, you're probably laughing because if you've ever tried to write a program in Lisp, it's not fun, but it has very few primitives. And so they tend to say relatively simple. As long as we're not judging simple by how many parentheses you have to pull through. And so there's something here for me and I'm still in the process of figuring it out myself. But I think we've gone as an industry so far across so far overboard on our tools, on our programming languages. I think there's so much ground to gain back if we pull them all back and say instead of solving this problem with 500 features and this massive IDE, this sounds like a rocket. Ship spinning up my fans when I turn it on. Can I solve it with less tools? And in my experimenting with this, I have seen just like in TDD, when I write the test up front, it sets a benchmark for what my code needs to do. Right. And so the code is often more simple because I'm not sitting like, what should this thing do? I had to consider this case. No, I'm very focused on one problem. I've seen a similar effect when I use Vim. It's like, well, I can't hit F12 to go to definition. So I'm not going to put that token in another file. Right. But I can't like hit whatever keystroke it is to rename this variable. So I'm going to try and not pepper that variable everywhere. And so I'm seeing my code is getting smaller because it's a big pain to work with it if it starts to grow, especially if it starts to spread across multiple files and I'm kind of screwed.

[01:03:52] Allan Stewart: I love it. It's like Ian Malcolm in Jurassic Park. We get so caught up with whether we could. We don't stop to think if we should.

[01:04:01] Matt Baker: That's great. That's a great quote.

[01:04:03] Allan Stewart: Or maybe more even more to the point. There's this other quote in the movie where he says he stood on the shoulders of geniuses to accomplish something as fast as you could. And before you even knew what you had, you had patented it and packaged it and slapped it on a lunchbox. I think we do that in code too often, probably where we race to the complexity. What can we do? How can we slap this together? By the time that you've gotten done with the coding session, like you don't even know what it is that you've built.

[01:04:32] Matt Baker: Yeah. It's like, okay, run it. Did it work? Ship it. Right. And then that's what Dykstra was saying. Like, oh no, that didn't prove the absence of bugs that just proved along like that happy path at work. Like you're about to learn where all the bugs are when it starts to get used. I think he also had another quote in there. What did he say? Like how much of a time suck it is to debug things after the fact.

[01:04:53] Dave Adsit: Well, I'm reminded of several things. When I read that quote in the article, I was like, oh, this is a lot. I think it's a lot of what Matt was talking about in our last discussion around simplicity. I was also reminded of one of the things Bob Martin has said in a couple of presentations I've seen about how we make progress in software development by accepting restrictions around the types of things that we are willing to do with code. One of them was accepting a limitation around flow control. Where you just stop using go to. Because if you have a go to in there, that means you can't prove the program anymore. Also, it makes it really hard for humans to reason about as well. One of the other things is from Lisp like environments closure, specifically the restriction on mutation. You can't change the data. You can only append to it. And, you know, there's different types of restrictions that we accept as we move forward. As an industry. And I think it'll be interesting to see what the next ones are. I've definitely used big heavyweight IDEs. I'm currently coding go in VS code. And I certainly like coding go and VS code more than I like coding TypeScript and Webstorm. Even though Webstorm gives you a lot of really cool tools to make TypeScript bearable. I would still rather code go and VS code.

[01:06:30] Matt Baker: I think go is another case study. With generics. I think they've been introduced now, but for so long they resisted them for this reason. We don't want to have code that is able to use generics because of the sprawling complexity. Continuing along this topic of tools, there's a quote in the paper that says the sooner we can forget that Fortran has ever existed, the better. I don't necessarily take that as a negative. And prior to him saying that, he talks about how impressive it was. The Fortran has lasted for 10 years. There's a 10-year look ahead, I think he called it. Which there's some languages that exist today that fall into this category, right? Where it's like it has existed for 10 years and that's incredibly impressive. However, at this point, it's no longer adequate because it wastes so much brainpower having to use the tools. So he was talking about Fortran in the 70s. There's definitely some languages that we could talk about today that have been around a long time. Are very impressive. And we've learned a lot as an industry while growing and adapting the tool. But at some point, it just hits a ceiling where it's like this is no longer useful. So for me, what I take from that is always question your tools. Like the selection of a tool for a particular problem you're working on, it's a big decision, right? And don't fall into the habit of, well, I'm a so-and-so programmer, so I use this. I took that more as be willing to always step back and say, is this tool, is it adequate for the job anymore? Is it overly complex for what I need? Or has it just degraded, right? In its lifespan, has it become unmanageable because five features has turned into 50?

[01:08:05] Dave Adsit: So Matt, talking about lookaheads, there's one other quote that I'd really like to discuss. And that is, in a society in which the educational system is used as an instrument for the establishment of a homogenized culture in which the cream is prevented from rising to the top, the education of competent programmers could be politically impalatable. And I think that that will be a long discussion best saved for another podcast in the future.

[01:08:40] Allan Stewart: There's definitely some interesting things in there that we could talk about. But I think it's about time that we wrapped up. Coming to the summary of the paper and of our discussion, I think we're in agreement, I believe, with what Dijkstra says at the end of his paper. We need to do a better programming job, fully appreciate the tremendous difficulty, stick to modest and elegant programming languages, respect the intrinsic limitations of the human mind, and approach the task as very humble programmers. Music for our podcast is generously provided by Todd Fisher. And as always, we recommend that you join up with a community of professionals by attending a Software Crafters group or meetup near you. The Utah SC group at utasc.org has a virtual meeting the first Wednesday of each month. Maybe we will eat a piece of humble pie with you there.

~/podcast/episodes/020-the-humble-programmer $ cat ../../copyright.txt

Copyright © 2026 - Crafting Code Podcast