Crafting Code Podcast

~/podcast

$ cd episodes/018-searching-for-simple-tools

~/podcast/episodes/018-searching-for-simple-tools $ ls -1a
. .. episode-summary.txt references.txt themes.txt transcript.txt get-mp3.sh
~/podcast/episodes/018-searching-for-simple-tools $ cat episode-summary.txt

Will using simple tools help you keep your codebase simple? In this episode, your hosts try to draw a distinction between simplicity and ease, then examine how they apply to the tools we use to write code. Unfortunately, simplicity and complexity depend on what level of abstraction you're working at. Sometimes we choose to trade simplicity for easiness, or visa-versa. And maybe there are some simple tools that make your coding life both difficult and complex!

~/podcast/episodes/018-searching-for-simple-tools $ cat references.txt ~/podcast/episodes/018-searching-for-simple-tools $ cat themes.txt ~/podcast/episodes/018-searching-for-simple-tools
$ cat transcript.txt

[00:00:16] Allan Stewart: Welcome to episode 18 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 3D printing board game inserts for storing my games.

[00:00:35] Dave Adsit: I'm Dave Adsit, a technologist, and lately I've been thinking about the fragility and neediness of brand new humans.

[00:00:42] Matt Baker: My name's Matt. Lately I've been thinking about building actor systems.

[00:00:47] Allan Stewart: Our episode topic today is searching for simple tools. There's a lot of tooling that is needed in crafting code. But to give context to this discussion, we need to first start by answering the question, what is simple? What does simple mean when we're talking about searching for simple tools?

[00:01:05] Matt Baker: I don't have a definition myself, but I can borrow from one. A guy named Rich Hickey has a talk online called "Simple Made Easy." And he separates the two terms, simple versus easy, in order to, I think, highlight what a simple tool is and the quick summary of his talk is that easy tools are easy because they're familiar to you. For instance, if you speak German, you might think it's easier to speak German than someone who doesn't, right? Or if German's your native language, you might say, "yeah, speaking German is easy," and that's because it's, it's something you're familiar with, it's something you've grown accustomed to, and and it's easy to you, whereas simple, this is Rich's argument anyway. He says that simple is a measurement of how complected things are within the thing you're talking about. So like within the tool, if we're talking about simple tools, and he defines complecting as braiding things together or interweaving things. And so I take that as the amount of things your tool does, like the amount of responsibilities it has, and there's like a whole, like, degrees of scope here that it's hard to, it's almost hard to talk about and hard to figure out, like, when you say a simple tool, to what degree are you talking about? And so I think as we have this discussion, we're going to have to set our, our scopes pretty clearly, but anyway, uh, in summary, yeah, Rich Hickey's thing, he says there's a difference between simple and easy. Easy is something you're familiar with that comes naturally to you, might not be the case for someone else, but simple on the other hand is the amount of things that a thing does. And we should strive for simple things, not necessarily easy things. Yeah. I recently went back and

[00:02:47] Allan Stewart: rewatched that talk and I was surprised, first of all, that it was from 2012. So it was a little bit older than I remembered. And I was really interested to see how much I had changed since a previous watching of that talk. But I also think it's really interesting to think about that distinction between simple and easy because we get caught up with tooling a lot with our own familiarity. It's actually related to our recent episode about programming languages. Sometimes we think, oh, that programming language is hard. It's difficult. And we conflate that with being complex. That language, it's so complicated. I just can't get my head around it. But really, in a lot of cases, it's really speaking more towards our familiarity. Things that we have done before are familiar and they're easy. Things that you've never done before are hard. I'm working with a person right now who is very new to coding. And so placement of curly braces is difficult. It's something that they're still trying to understand. Whereas for a lot of seasoned developers, they don't even think about it. They just let the tooling take care of that for them, or they're just, you know, have muscle memory around it. So as we talk about simple tools, I think that that's important. The other thing that I think about a lot in terms of how easy or complex a tool is, is also just around how much you keep in your mind. I think the more complicated something is, the harder it is to keep all of the pieces juggled at the same time mentally. And that reminds me of the book, The Design of Everyday Things by Donald Norman. He talks in that book about how the average person can only really keep seven things in short-term memory at any given time. And so how complex your tool is, is going to matter. If it's too complicated, you can't actually use it effectively because you can't keep all of those things in mind all at once. Oh, okay. That's interesting.

[00:04:53] Dave Adsit: You know, I've spent the last three years trying to learn German and I have found it to be quite complex. It has not become easy for me yet. Initially, what you were saying about how many things you keep in mind and how many things your tool reinforces you to keep in mind, it got me thinking about some of the rules that we put in place, some of the guidance that comes down from previous developers around abstraction and encapsulation and the patterns that make things easier. Year. I know we want to focus primarily on tooling in this discussion, but I think that there's something there as well. Like you can use a complex tool in an easy way, or you can use an easy tool in a complicated way.

[00:05:43] Allan Stewart: I like that. As I've been thinking about this topic, one of the tools I've been considering to judge whether it's a simple tool or not is Git. Git is difficult to use, especially when you're first starting to use it. And there are things that you can do with it that will mess up your repository in ways that just feel completely unrecoverable. Rm-rf, this whole directory, I'm going to clone it again because what even happened, I don't know. But on the other hand, I think most developers only really use a subset of Git, especially if they're using Trunk-Based Development. For what it is accomplishing, it's not actually all that that difficult. There's only a handful of commands that you need to know to be able to do trunk-based development successfully. You can work together with a group of people, sharing code, having a store on GitHub or Bitbucket or wherever your favorite place is to store code. That can actually be done pretty easily. It's really focused in on one kind of work. I don't have to worry about you know, how many different extensions are installed into Git. All I really care about is like, let me clone the repository, do some work, commit some code, push it. In that case, it makes me think, well, maybe Git's not actually that complicated of a tool. It might be a difficult tool to use, but it really does focus in on the one thing that is source control, as opposed to trying to also have a bunch of other bells and whistles.

[00:07:16] Dave Adsit: I do think that Git is a response to more complicated tools that had been available before it. Tools like ClearCase or even Team Foundation Server, any of these tools that are trying to be multiple things. And I think that that's one of the advantages of simple. You're not bringing in extra complexity, unnecessary complexity. You're not entangling source control and how essential source control is with your build system and your ticketing system and all these other types of tools that you end up getting when you've got a ecosystem of source control. That's not to say that every feature of Git is easy. I have certainly had to Google some fairly arcane things that you only do once in a career. And luckily, someone on Stack Exchange can tell you what to do and you try it and it may or may not work. But it's OK because it's all local and you don't have to push to somebody else until you fixed it.

[00:08:18] Matt Baker: So now that we have this framework separating simple versus easy things and we want to talk about simple tools. So why does it matter? I think that your code reflects your tools. So the more complex tools you select, the more complex your code will be. I also think you should prefer less complex code. So like if you just turn that around, then it says, okay, if you want less complex code, you have to use less complex tools. And like on the surface, it's like, yeah, okay, that makes sense. Maybe that's not even what we're saying. And I was at a talk, conference talk at some point, and the speaker said, "the complexity of your tools is a comment on the complexity of your code." And I kind of clicked with me. I thought, man, I really agree with that right now. At the time I was working in a big IDE. So yeah, I think that you should strive for more simple tools. And I think that because simple tools will help you produce less complex code, but then the follow-up question I would have is, well, how do you define the complexity of code? So if we're talking about, we want simple tools because we'll produce less complex code, how do we measure the current degree of our code complexity? Do either of you have any ideas for how we might measure that?

[00:09:28] Allan Stewart: Bringing it back to like your hypothesis there, it sounds like you're saying that if you're using a big IDE that has a lot of bells and whistles, that essentially you're more likely to utilize a lot of these tools, a lot of the things that are in your tool that have complexity and it leads towards code complexity. So like if my IDE has mechanisms for doing database integrations that I will make my code more complex because I'm kind of chasing after these other bells and whistles. It's like, "oh, the IDE will let me do this." And so I'm more likely to actually invoke that. Is that the hypothesis here?

[00:10:11] Matt Baker: It's squishy, right? I think so. So for me, this might be saying the same thing you just said, Allan. I think your tool will shape the way you look at the problem you're working on and in doing so, like drive you towards certain solutions. Like if you take a big IDE and maybe you set up an app and let's say, I don't know, you create some database migrations in the IDE. And then maybe the IDE layers on top some sugar that ensures those database migration scripts run only if they need to and kind of manages that. And so you just build an API with database migrations that way. And you ship that. On one hand, it's great. On the other hand, you just shipped a lot while having to do a little. And I wonder if there are times when that kind of example can go in a not good way. Like surely there are times where having that database migration script management and the IDE is nice. But are there situations that are similar to that where it's no longer nice? Like, does your IDE make it easy or even encourage you to do things that don't fit your problem, you know, but you think they do because of the way the IDE shapes your thinking?

[00:11:22] Dave Adsit: I think one of the things that comes to mind here is the difference between essential complexity and accidental complexity. When you asked how to measure code, the first thing that came to mind for me is like, well, the basic metrics of cyclomatic complexity, how many loops and branches do you have, the number of lines of code that you have, the number of structures that you have, classes, functions, whatever those structures are in that language. And one of the things, like, there's the rules, the four rules of simple design. One of them is, you know, minimize the the number of things. After you've expressed the problem or the solution to the problem, you want to minimize the total number of things necessary. And so I'm reminded a lot of the discussions around these concepts that happened around the time when Ruby on Rails was getting very popular. And it was all about, "you don't have to do so much to get things going." And that felt really good for a while because you're like, you just create a database model or you create a model and you use the model in your code. And that model is using Active Record and it's tied to the database, but you don't really think about that or worry about that that much because you're just dealing with the simple parts of your domain and keeping your domain simple and pushing all of that complexity out. And that's, you know, you're pushing it behind an abstraction, but you're also relying on a framework to do a lot of the work for you. And so it's like, where is the complexity? And how much of the complexity is essential to solving this problem versus incidental because you're doing things? I used to go to a lot of code retreats and you're coding in different languages all the time, right? We talked about this in our programming languages episode. There's a constraint that they give you during some of the sessions of code retreat where it's like, "do not write any function more than two lines of code long." And there's always the caveat, unless you're in a C-based language or C-derived language, then you can have three. And part of that is because you've always got those extra braces. You know, a Python developer looks at the braces in C# and like, "that's unnecessary." And a C# developer looks at the white space, the significant white space in a Python code base and and says, "I will lose my mind. I have to turn on visual white space to even know what my code is doing." Both of these are attempts at reducing the complexity and making the ideas flow, I think, but from different perspectives.

[00:14:00] Matt Baker: You mean they want you to get going quicker?

[00:14:03] Dave Adsit: I think that most programming languages, most toolkits, most frameworks are built around the the idea of you should be able to get started and get going fast.

[00:14:14] Matt Baker: Yeah. It's interesting. Like, my first thought is that sounds more like easy. And so I, I start to try and get back to simple and I say, okay, like if I use this tool, if I use a framework and I get going fast, and then I stop and I look at what I built and let's say maybe it's scaffolded some code for me. And then there's a runtime that it needs in order to execute. How do I measure that complexity in a meaningful way? Like how, you know, like how do you get out? Like, I'm not talking about like, well, like it's definitely three degrees complex and we all feel good about that measurement. Like I don't, not just for the sake of it. I want to say like, when you look at what you've built and you start to think, is this thing complex? And is it so complex that it's going to get in my way? Like, how do you actually start peeling those layers back? Right. And so one of my first questions is, okay, well, if I want to understand how complex this thing is, like, what the hell does that mean? How am I defining complexity? Is it like, let's take lines of code, for instance, like just for the sake of conversation, like if you scaffolded something in a framework like Ruby, and you're measuring complexity on lines of code, you're immediately complex, right? I don't know how complex you are. But like when you scaffold a new, or I could use a different one, like I'm using AWS CDK a lot lately, and you create these little things called like apps and then stacks and you deploy those into AWS. When I create a new one, I scaffold it. So I immediately have a few hundred lines of code. It's like, okay, well, is this, is that good? Is that bad? I don't know. Like I haven't even written any code yet and I already have like 300 lines of code. And so I think, okay, well, what are the limits of lines of code? Just as one example. So like the problem I'm trying to tee up is like, how do you actually ascertain complexity of something you've built? And then how do you take that far enough to say, and this is is why it matters, right? Like if it's, if it's just to like, like I said, just come up with some arbitrary measurement, then fine. But like, how, why does it matter to what you're trying to build? Why does it matter to the company you're trying to build or the team you're trying to build or, you know, feature or whatever?

[00:16:15] Dave Adsit: I think it has to do with the ratio of essential complexity to incidental complexity. And now you just have to figure out how to measure those two things. Right, like how much of this code is expressing a domain concept and how much of this code is necessary because of the language, framework, or toolkit that I'm using? One extreme example from my past, because I like to go to examples because it helps me to get concrete, we were building a product that used the CSLA, which I don't even remember what that stands for. It's like common and something garbage tire fire. Anyway, so we had a template for new classes and the template was 10,000 lines long and you copy the 10,000 line template or you'd like CSLA new template or whatever. And then you get the 10,000 line class file and then you would add like maybe 20 lines of code total to that file. And that's partially because the tools didn't support things like partial classes. And it's partially because the person who built that framework was trying to make it so that you could at runtime decide whether you were running on a single server or multiple servers doing the same job and distributing the work. Like it was neither simple nor easy.

[00:17:42] Matt Baker: And did it matter? Like at the end of the day, did it have like an impact in your life or what you're trying to do?

[00:17:48] Dave Adsit: Absolutely not.

[00:17:49] Matt Baker: Interesting.

[00:17:50] Dave Adsit: We never ever ran that application in a way that would take advantage of the feature set of that toolkit. It was a hedge against a future that never happened.

[00:17:59] Matt Baker: Oh, that's interesting. Would you say it was quicker to use it than not use it to get to where you wanted to go?

[00:18:06] Dave Adsit: No.

[00:18:06] Matt Baker: It would have been quicker to not use it?

[00:18:08] Dave Adsit: In that case, it would have been quicker to not use it.

[00:18:11] Matt Baker: And just so I understand clearly, you decided to use it because you thought you were going to scale and it would set you up to scale better?

[00:18:18] Dave Adsit: Yeah, it was a decision made by the architect on the team at the time who wanted to basically provide extensibility for an interior environment. And that was back in the day when everybody was like, "well, you have to have like your presentation layer servers and then your middleware servers and then your backend servers and then your database servers." And by using the CSLA, you could say, "I'm going to write one class file and define this idea once, and then I'm going to deploy it in different ways at different times." And it is like a relic of the, I don't know, it's definitely not the way I think about problems now. I think that, you know, a good software engineer has an idea how the code is going to be deployed in the production environment, at least partially because they're working with the ops specialist on their team to deploy the code into the production environment on a regular basis.

[00:19:18] Allan Stewart: Going back to what you were asking Matt about, why does this matter? It makes me think that it matters because of the architectural principle of separating things that change for different reasons. Because when we have things that are complex or literally complected, to go back to that definition from "Simple Made Easy," if everything is like all braided together and complected, it's hard to separate things out. And so like, it makes me think about your example, maybe your, your IDE has a bunch of database tools. And so you're using the migration tool and because you're using the migration tool, you're also using their testing framework and deployment framework. That's all like baked into the IDE. And that might, that might be great for a while. You're able to move fast, get started quickly. And in certain contexts, that might be all that you need. There may be a context where you're in a very like early pioneer exploration kind of stage, and you just need to like be able to throw stuff at the wall really quick to see what sticks. But over time, a year or two later, they've put out a new version of the IDE and the old version is not supported anymore. And by the way, you bought some new hardware and it won't even run on the old stuff. So now you're stuck because the complexity of like the migration path is difficult. Or maybe you skipped a version because it was difficult and it only got more difficult as you went on to more and more versions. So the IDE changing or something else changing around you changes that whole experience. Like maybe what worked really well for deploying Deploying was fine when you only were deploying just this one monolithic thing out to some servers that you owned. But now you want to go serverless and you want to have a whole bunch of things that work together in tandem and it becomes difficult again. It's the same problem with the frameworks. Frameworks don't have a lot to say about what do you do to separate your business logic from their framework. Work. They don't have a lot to say about what is going to change and why you might want to do something different in the future. And so if we find simple tools that solve a particular problem, it might be more work upfront or to transition from a single fast, easy tool to a multitude of simple tools. But the simple tools have that advantage that each one of of them can be independently swapped out. You don't like your deployment tool? Fine, change it. You don't like your database migrations tool? Change it. But if you're using everything all mushed together, they are complected and you're going to have to do a lot more work to loosen up that weave enough that you can pull out the cord that you want to replace.

[00:22:08] Matt Baker: It's interesting that you arrived at coupling there because it makes me think, okay, so like I'm still on this horn about about how do you define complexity. If you have six things that are loosely coupled, that feels less complex based on the definition of like the simple versus easy than it is one thing that does those six things. And I think that's probably because of the way change ripples through it.

[00:22:29] Dave Adsit: So one of the things that I'm hearing, whether you're saying it or not, is that single responsibility applies to the tools we use, not just the classes that we write or the functions that we write. Right. That concept of single responsibility and composability, those are critical to creating the ecosystem for the code. And by building something that's small and focused and simple, we then take upon us the responsibility for ensuring that it can be integrated and used in a bigger system that has more going on. Rather than trying to be the big system, we use that small focus tool with a single responsibility to do each of the pieces. I used to be a big fan of the Visual Studio IDE, and eventually I was like, "what is this thing doing?" It's got all of these tools in it. And it's doing things that I don't want my IDE to ever do. I'm not convinced I want my IDE to even know about source control, let alone my database, etc, etc. Certainly the IDE that I use these days has all of those plugins available because it's convenient for some people. I just choose not to use them.

[00:23:41] Allan Stewart: It reminds me of the Unix philosophy of making a whole bunch of little tools that can be chained together and that you can pipeline these things. I like what you said about responsibility, Dave. Where are you deferring responsibility and what do you need to be responsible for? So in the Unix philosophy, each tool just does its one thing, hopefully extremely well. And it's up to the person who is piping all the commands together so that they can grep and word count and said and whatever else that they're going to do. That's their complexity, and it's their responsibility to know whether or not it works or not. And so when we defer responsibility to another party, that can sometimes be useful. That can sometimes make us go fast, whether it's a framework, whether it's a IDE, whether it's some other tool. Like I know for React, there's Create React App. And one of the things that it gives you is the ability to eject. At some point, you may be done with this. And so you can eject and be done. And I think that's kind of for this reason. It's like an acknowledgement that you might have to take responsibility for the things that the framework was doing. The developers of IDEs and frameworks, they don't care what you're trying to accomplish so much. They're trying to enable you, but they don't want to be responsible for whether your website is down or not.

[00:25:09] Dave Adsit: Well, it's whatever your framework does for you, it does to you. So if it helps you do X, Y, and Z, it makes you do X, Y, and Z in the framework's way. And if you want to do it a different way, you have to find a way to decouple and deconstruct that framework.

[00:25:25] Matt Baker: work. I keep coming back to this question of what do you gain by reducing your complexity? Because I think that's where the conversation is headed. More complex is bad than less complex. And I think the less complex your code base, the easier it is to add new features or update current ones. And I also think the more composable it is. So going back to what you were just talking about, Allan, the Unix philosophy, being able to compose simple things together. I think the ability to do that is somewhat predicated by those things you're composing being simple. Like the more complected those things are, the often in practice, the less composable they are. So maybe there's this aspect of building software where we favor like compositions of lots of small, discrete things over the big thing, like Dave was saying in the big IDE, because I feel the same way. I've switched to a more bare bones way of writing code lately and I don't miss the IDE. And I kind of look back with like disdain a little bit. It. But I think it matters. And so like, and I think that matters to your bottom line, I guess is what I'm trying to say. When your code's complex, it's harder to add new features and maintain or update current ones. And it's harder to extend it. And then I also think it's harder to, it's rather easier to hire an onboard engineers, the less and less your complex your code gets.

[00:26:45] Allan Stewart: There's an interesting balance there too. And I think it also plays into the purpose of a tool. We talk about IDEs, but there's also just text editors. And there's a question in my mind about how simple do you want to go? Because to get more simple, you have to be focused on less things. And so a text editor, generally speaking, is more simple than an IDE, but it also has a more narrow purpose. So like for me personally, I would say I want intermediate level of complexity in some of the tools that I use for writing code, because yeah, having a really big bloated IDE, it has problems and there's the potential, right? There's the temptation that I might reach in and use a bunch of stuff that I shouldn't, or maybe I'm just, I'm relying on it like a crutch. But on the other hand, why are there so many different editors? And some of them are serving different purposes and they're They're not necessarily interchangeable. I could just use Notepad that ships with Windows to write code. It is able to write at least some form of ASCII, maybe not UTF-8. Potentially, you could use it, but you're going to lose all of these other features. It's not going to help you with matching up your braces. It's not going to give you syntax highlighting. It's not going to give you any code completion. Are those things that you're willing to trade off? I can open up something like Nano or Pico on a Linux box or a Mac, and I could write code using that. It's super bare bones. Like it's about as simple as you could get. You are able to type characters in, and there's like 10 commands in total. You can save the file, you can exit, you can page up and down, you can cut a line. Those are like the only things that even exist in the tool, but that might be going so simplistic that it's no longer useful. I don't know that I want to keep in my head all of the exact syntax that a bigger IDE will help me do, let alone some of the other tooling that are nice conveniences like refactoring tools where I want to say, "hey, I've selected this text. Please extract a variable or extract this out into a separate method." I can do that by hand, but should I?

[00:29:05] Matt Baker: Yeah, it's really hard. So I think we're agreed that the tools you choose, I feel silly saying this, but the tools you choose definitely impact the end result and complex tools can make for more complex code. But there's in situations, Allan, what you were just saying, the call for it. In certain scenarios, you absolutely want those refactoring capabilities. Abilities. Like I'm thinking like a big old crafty code base, you know, that was maybe a few hundred thousand lines or millions of lines of.NET or Java or something that you're having to sift through. And like, you'll, you'll want a more powerful IDE for that. And I think it's warranted, you know, so in that case, you might reach for it. And so it brings me back to this question. Okay. So if complexity is bad because it slows me down, essentially, you know, in feature development and hiring and onboarding people and just maintaining the system, it slows me down. But there's also a certain degree of called for complexity. For example, if I'm in a legacy 2 million lines of code Java system or .NET system, I'm going to want the IDE of the time, most likely to try and wrangle that thing. So how do you know? Like when you go to make a decision, how do you know if you're dealing with too much or too little complexity? My answer to this lately lately has been start with the simplest thing that could possibly work and then iterate. And if that means doing something manually at first and do it manually a few times until you get a sense for what it, if it's really a big time suck and it's going to drain you day to day, then maybe you should solve it. You know? And I think when you take the simple approach, it helps you check your own assumptions at the door and say, "you know, I'm going to give it a a week coding this way, and then I'm going to prioritize a fix for the worst part about the week." So as a concrete example, if you're shipping, let's say you're writing some code and shipping it into the cloud, maybe your deployment time into your cloud provider is 10 minutes and you deploy a lot and you can make that better. So maybe you should adopt a tool that makes your deployment model better, right? And I'm sure that there were other things throughout the week, but you went through this exercise of saying, "I worked this in the simplest way I possibly could for a week. And the thing that sucked the most was waiting around 10 minutes every time I had to ship something to production. So that's the thing I'm going to fix. That's the spot where I'm going to accept the additional complexity."

[00:31:27] Dave Adsit: I think that there's definitely something there with the idea that different tools and frameworks, et cetera, are more appropriate for different contexts. There have been a lot of tools that have come and gone that have been optimized for getting a quick start. I'm reminded that Microsoft Access was intended to be used by, you know, your secretary or your admin or your department secretary or admin to create reporting for the manager. And every tool that every, every project that I ever took over that was originally Microsoft Access basically was a disaster because there was too much accidental complexity. I'm not even going to say incidental because it may not have just been because of the tool. It may have just been because of the lack of knowledge and experience of the people using the tool. There's definitely a lot of value in taking whatever the complex or time-consuming or hard thing is and making it simple.

[00:32:27] Matt Baker: What I would add to that is there's value in giving the hard thing time to present itself. The reason I like the simple, like start out doing everything just in the most simple way possible is that you can still be delivering value to what you're doing, right? You're not sitting there like in a room whiteboarding, like solutions for all the risks you could possibly face. You're saying like, "no, I'm going to move forward in this week. I'm going to do it all manually." And then that gives your solution, it gives the problem you're dealing with rather time to emerge and time to kind of present itself because that stuff it's not always is obvious up front. I think we all know what it's like to pre-optimize the first week or two into a project and then find out not only did it not help, but your pre-optimization now actually got in your way and made it more difficult for you to move forward.

[00:33:14] Dave Adsit: I mean, I've seen that with attempts at going to a microservices architecture before it was necessary. We're just going to make this one microservice and we've doubled our complexity in deployment because now we have two things to deploy every time we deploy and they have to be done together because we didn't do a good enough job of untangling them and making them simple rather than complex. It strikes me how much

[00:33:41] Allan Stewart: time plays into this equation. If it was just something that is happening for a short period of time and then it's done and it's over, then a lot of these decisions don't matter. You could do something simple or you could do something complex, and using a complex tool might actually be preferable because you are able to get more, a bigger set of things done more quickly, which may be part of the reason for the preponderance of low code solutions that are emerging onto the scene, where it makes it really easy to do certain kinds of work quickly. And if you're only going to do it but for some kind of limited scope, maybe the limited scope is in the amount of time that this thing is going to live for. Maybe the scope is how often you're going to allow it to change. Maybe the scope is for how many features are there. Then those solutions may be perfectly fine. You find the one that does what you need and you're golden and you can just be done. But if it doesn't do what you need over time, if some of these constraints fail, it's no longer true that we're not going to add new features and we want, you know, 17 new features now, or it's no longer true that we're going to throw this away. We're going to keep using it indefinitely. We're going to keep developers on and continue to work on it. All of those constraints mean that there's change over time and that makes it harder for complex solutions to work. And so it makes me think that there's, you know, some kind of middle ground that you You just have to strike. It's hard because everything in software is that way. We're constantly saying, well, that depends. And you have to look at the context of a problem, but there's just no way around it because code is such a complex endeavor, right? It's one of the most complicated things that we've ever tried to do in all of humanity. And so we really have to be careful about how we take it on. I like what you're saying about starting off simple, Matt. I wonder if you can go too simple to start off though. Like, I wonder if there's like some amount of like, you're, you're trying to stay somewhat simple, but there's some level of accepted complexity that you're willing to take on. You like things like your IDE, right? I'm not willing to write all my code in Nano. And so therefore I accept that I'm going to have Git integration and syntax highlighting. And there's probably an extension sitting off there just tempting for doing code deploys or database migrations or something out there, but it may be worth it to have that initial level so that you're not starting at ground zero every single time.

[00:36:27] Dave Adsit: I think that there is a context there about who is doing the thing. If Matt is saying, "I'm going to write the simplest code that could possibly work. And when I find a case where it doesn't work, I'm going to change it." Great. I have full confidence in that. Even if he has to deal with things like database protocols and HTTP, I'm like, "he's got it. I'm comfortable with Matt doing that." I know he's been doing this for more than 20 years and he knows his way around a code file or two. If I'm working in a different environment where I've got a bunch of novice developers, I want to provide them with more support because I have less confidence that they can deliver on the promise of simple and just change it as you run into problems. I think that that is something that we have to take into account when we determine which tools do we want to adopt or embrace for this system. There's a lot of tools tools in an experienced developer's head that are not available to a less experienced developer and do we need to keep things simple so that the less, uh, less experienced developer can work on them? Yes. What does that mean for them? It's going to mean something different than what it means to keep it simple for somebody who could write their own text editor and their own programming language and their own compiler or interpreter.

[00:38:00] Matt Baker: Easy, right? Not simple. Simple is the degree of complexion. Easy is relative per our model at the beginning.

[00:38:07] Allan Stewart: It's a difficult judgment between simple and easy there because a lot of the things that you need to do to make it easier, which requires less familiarity, which is more broadly inclusive of people of different backgrounds and skill sets, also has this tendency to have complexity baked into them because they're handling more things. They've, they've complected more kinds of things because not everybody has all of that knowledge in their head. And so they're trying to help you out. And so if you reach for something that is easy to use, you might be adding complexity to that development environment, which may or may not help you to get simple code out. There's some interesting trade-offs going on there.

[00:38:53] Dave Adsit: Well, at the end of the day, it's all complex. There is no such thing as writing simple software, simple code in a software environment. Because at the end of the day, it all relies on being turned into binary instructions that can be interpreted by the processor. Kevlin Henney pointed this out when he's like, "Oh, you're hiring for full stack, which which compiler or which, which, which processors, are you targeting with your compiler that your home growing in house type of a thing, right, is like nobody actually is full stack, because we all sit on top of compilers and operating systems someone else wrote, or even which beach are you going to, to get a bucket of sand,"

[00:39:39] Allan Stewart: to make the silicon that you're going to run this software on.

[00:39:47] Matt Baker: So at my current role right now, I'm, it's a greenfield project. It's me and two other people, and we're each working on our own little piece right now, still on like the POC stage. We're writing stuff and shipping it up to a cloud and then having it execute in like a ephemeral compute model, you know, like a service model, like GCPs functions or Azure's. They might also call functions, right? AWS is Lambda. So to test some of these ideas out that we've been talking out, I decided to start coding in Vim, and I don't use any of the plugins other than one or two. I use command line Git for my source control. And then I push, excuse me, a ship code with the cloud vendors command line tool. So I never leave the terminal. I don't have any IntelliSense. I don't have any like refactoring capabilities. Like I honestly, I miss renames. Sometimes I've only been doing this for like maybe about a month, but I miss a few already, but I'm also moving the needle on the company goals like consistently because I'm just focused on like, okay, what's the next thing the company needs. And so when I go to do something like as as an example, I have to start a, one of these ephemeral compute instances by putting a message onto a log and then the compute instance is subscribed to it. And so I actually just open up the cloud vendors CLI tool and push the message JSON, just right in the terminal. I just write the JSON right there and I push it. And one of the commands says like, "do this thing." And then it goes and invokes the process that I'm working on. I see if it worked. And then I go on, like I could write some code to change that, right? Like have a little script that says, "start the thing, start the engine." Um, but that's, I think that's an interesting comment on our simple and easy conversation. So is it easy to know the schema, the of the message that needs to be put onto the log and started in order to start the ephemeral compute instance for what I'm working on? No, like you're gonna have have to go read the code or the schema or have have it documented somewhere in order to know that. But is it simple, like per our definition? I think yes. It's one thing, it's, there's one way to do it, this message onto this log. And it might not be easy for you to do that, but it's still simple. And when you figure out how to, when you learn how to do it, and like me have it at the top of your daily notes so you can just go copy paste, and it's very easy. Um, and it's also simple and And so I'm in the spot where I'm able to bring it. I'm able to start this, like the, when you ask the question, you know, "what's the simplest thing that could possibly work?" I'm able to turn that dial way down and say, "well, let's see what that actually is." And like I said, there's certain things that I've been experiencing over the past month that I don't like, like not being able to rename symbols and the text file is, it sucks. But there's also, I've noticed the way I write code is changing because I don't have those things. I'm not willing to proliferate a bunch of code. And because I'm in Vim, if it starts to get over like 50, 60 lines, I start to get kind of icky or not. I start to not feel good about the file. I start thinking there must be something wrong with it. And I shouldn't say because I'm in Vim, but I've started doing that more as I've been coding in Vim. I pay a lot more attention to the size of the code file than I ever did before. And I have some theories for why, but I will say that that's causing me to think about all the things I'm putting into a particular file. And this code base is in Python, so groups of functions. And so I'm noticing that my choice of those tools is in fact changing the way I'm writing code to solve the problem. But at the same time, like there's certain things that suck again, like refactoring. And so I suspect at some point there'll be an addition to the workflow, right? Like maybe I'll get a better Vim plugin or a more complex one that's warranted given the situation. But I like that I'm taking the time to figure out what's warranted. And then what we ultimately choose, you know, like, I think it's on, I think you're in this constant carousel of simplest thing that could possibly work. Where does it suck? Okay. Like take on a little bit more complexity if you need to, to make that thing that sucked a little bit better and then go again and go again. I don't know. We'll see where this takes me at some point. I might just say like, "the hell with all this, give me my more robust IDE back and whatever." But so far it's, it's been a fun experiment and I am being productive day to day. I would say it's no more or less. And I'm continuing. One thing that's nice is I'm not in the phase of what's all the risk that is coming my way and let me hedge against it and introduce all this complexity up front. We're not doing that. We're saying, "well, like we see our goal and we know there's a ton of crap we don't know, but we're going to try and go there and then we'll iterate on our approach." And so that, that to me is like a way, an actual concrete way that that you can start to answer the question, "how much complexity do I need to take on?" Because I don't know that you can get it like just by looking up front. I think there's a big portion of reflecting on how your day-to-day is going. That's going to help you, you know, get to this, maybe get to some semblance of an answer for "how much complexity do I need to take on for the given situation?" Because you both highlighted, there's so many variables that could be involved when you say, "well, you know, what do you mean, too complex for this situation, based on what?" Like, because again, if you're working in an old IDE, excuse me, if you're working in a code base that was written 15 years ago, and it's .NET or Java, like any corporation, and let's say it's a few million lines of code, it would definitely be more complex to try and make, I think, something like Vim work in that environment, and it would, to make something like the Java IDE or the .NET IDE they used to write the code in the

[00:45:28] Allan Stewart: the first place. And so I'll be real interested to hear how your system shakes out over time, because it's making me think a lot about different abstractions and where we try to simplify things. We can make things simpler by making a lot of functions, a lot of small functions instead of big functions, right? Or like, what do you put inside a function or how many functions should be in a class or how many classes should be in a module? And like, there's all of these like different layers. And so you're starting out real small with each deployment into this serverless environment. But eventually you're going to have to complex those serverless pieces together in order to get them all working together. And will the overall complexity of that be worse than having more complexity in each individual instance? I don't know that I can say because this is like a fundamental problem that we've wrestled with. I think we have learned that lots of small things is better than few really big complected things, but we'll have to see what happens with those abstractions. And I think abstraction becomes key in facilitating that work. Because at some point you have to complex things to make interesting code. And so you have to figure out, well, where are we going to complex it? And how are we going to deal with that abstraction? If you can create an abstraction that hides the complexity, then you can treat that thing as if if it were just one simple idea under the hood, right? Like at some point you're looking down at what all the assembly instructions are, right? Like the code that actually flows through the processor, that's gonna be always complicated, but we can hide a lot of that complexity behind abstractions that makes the layer that we're working on easier. And so it's really important to make the right abstractions because if you have a leaky abstraction, then that's going to start polluting your space that you're trying to protect with additional complexity. And that's why I don't like magic in tools and frameworks, because they hide from me what's really going on. If the concept was sufficiently abstract, then it's not magic. It's just doing something for me. I've thought about it in the past in terms of tying your shoes. Is that the interaction between billions of atoms? Or is that the one thing that you do before you go out the door? Or is it just wrapped up within the idea of, "I need to go out and get some milk?" Depending on the context that you're in and what level of abstraction you're working at, it's all of them.

[00:48:09] Matt Baker: Yeah, it just continues to spiral as we pull on it. And one of the things that's coming up in my mind again and again, as we're talking, is the importance of moving in the the small and reflecting on the effects of those movements. Again, just like everything else we've talked about, that has degrees of scope, right? What it means to move in the small at a tiny company means something different than what it means to move in the small at a big company. But I think it's more like, maybe it doesn't. Let me restate that. I think it has to do about minimizing the amount of things you change at one time and then observing the results of that change before moving on to the next. That's one of the ways that I think large or small, you can try and wrestle some sense out of the madness that is all the complexity you're dealing with. And it affords you that time to say, like to learn, right? It affords you the time to poke the thing and see how it responds. And you do that enough times, and then you'll be able to better understand the solution that you need to bring to the table. You'll be able to better say, "I know where it it makes sense to take on the complexity in this problem." Cause going back to what we said earlier, you don't get away from it. It's not like, "well, it's perfectly complex, free thing." Like Dave said, everything has to complex like in order to become useful, but that does not give way to everything should be complex. Like there's, there's an art to what's complex together, which I think is what we're talking about. What, going to the Unix philosophy, what simple, not easy, but what simple tools can I pipe together here? Can I compose in order to get this job done? You know, like, what's the simple way to go about it?

[00:49:48] Dave Adsit: One of the other Kevlin Henney talks that I really liked, he talks about the rule for calculating leap years, which is super easy. It's every fourth year is a leap year, except every hundredth year is not, except there's like three levels of exception in the definition. And so that becomes your essential complexity, right? And that's, it's just, it is complex. It's like at the end of the day, if you're accurately calculating a leap year, it's not as trivial as it seems like it would be considering that we only encounter them about every four years. I'm probably misremembering this because it's been years since I watched the "Simple Made Easy" talk, but it was kind of a sales

[00:50:35] Matt Baker: pitch for the Clojure language, right? Subtly. It was like, I think it was a sales pitch for the philosophy. Okay, like, and, and yes. So since he writes Clojure, he would say like, "and this is the philosophy that I bring to Clojure." So I can see what you mean, but it felt more like, let's talk about, like, the design of programming languages, that particular talk. I gotta say

[00:50:58] Dave Adsit: every time I tried to learn Clojure, I ran into a fundamental complexity of not being a Java programmer and not knowing the Java base class library, which was effectively essential for doing anything useful in Clojure anytime I tried in the far distant past. And so I think this is just another reiteration of what is complex is a function of what is familiar. Are you sure about that? I think so.

[00:51:33] Matt Baker: Interesting.

[00:51:35] Dave Adsit: I think that what feels complex is a function of what we are familiar with and what we have been trained to do.

[00:51:42] Matt Baker: Yes, I agree with that.

[00:51:44] Dave Adsit: because anytime I try to show somebody some of the C# code that I'm working on, anybody who doesn't know programming, they're like, "what are, this is nonsense." For me, it goes back to the idea, like we have an assumption that anybody who is going to speak publicly in our society is literate, but we also have an assumption that mathematical literacy is hard. And so a lot of people are mathematically illiterate. And even those who are mathematically literate, are illiterate of statistics. Before we started recording, we were talking a bit about the statistical likelihood of certain things happening. And it's really hard for our brains. So it feels really complex. And it's especially hard if we haven't had any training in it. And so that's where I'm kind of landing. And I'd love to be shown what I'm missing. But to me, I'm starting to feel like complexity is related to familiarity.

[00:52:40] Matt Baker: I think that's what Rich Hickey was taking issue with and saying that simple is a measurement of how much complexion has taken place. And then from that, you get to complexity. Whereas easy is a measurement of how familiar it is to you personally, as opposed to the number of complexions. I don't know what the word is.

[00:53:00] Dave Adsit: I get that. And I agree with it, but also I think that a lot of those happen outside of the system in front of us and they happen in our own heads and our own experience and our own training. What do you mean? Tell me more. Well, I mean, all software code looks complex to a novice, and professional software looks complex to an amateur. One example from my history, I think I may have brought it up before, is I took over a code base that was, I was assured that it was weird and hard to work in and had all kinds of unnecessary indirection. And when I opened the code base, I was like, "oh, this code was written in such a way that it could be easily tested given the framework at hand." And it had a test suite. None of the tests passed. They didn't even compile. They'd been removed from the compilation target and they had been excluded, but they were still in source control because even removing them from source control was too complex for the people who were working on the code.

[00:54:02] Matt Baker: I just imagine some engineer like right-clicking on the project and being like, "exclude test, problem solved."

[00:54:10] Dave Adsit: That's exactly what it was. There was a project for the software, for the website. It was actually a web application. And there was a project for the tests. And somebody was just like, "I can't even with these tests right now." And they excluded the project. It was still in source control. It was still on disk. I checked out the code. I joined the team and checked out the code probably a year after they had stopped doing tests on the code, and I was like, "well, this code actually does not feel complex to me. In fact, every single class in this system has a single responsibility, except for the ones where the people who had no idea why it was the way it was started writing code." And there were a lot of responsibilities all in a big ball. And to me, that was the complected code. But to the other developers on the team, all these unnecessary abstractions and interfaces and tests, that was the complexity.

[00:55:08] Allan Stewart: It's interesting and makes me wonder if there is some hidden link between these two concepts, that they're not completely orthogonal. And we see this is in some other areas with software too, right? Like when we talk about socio-technical systems, we want to say that the technology is one thing and the people is a different thing and they're completely orthogonal and you can vary them independently, but it just isn't true. There's a level of coupling between your people and your code that you can't get away from. And can't completely decouple them. You can't just pull on the one lever without also doing something with the other level.

[00:55:56] Matt Baker: I think it highlights for me that you got to get clear on what complexity means for you and your environment. Like if you're actually going to make decisions based on this stuff, like you, like if you're going to say, "I, I, this complexity is worth it, or this complexity isn't worth it," whatever I define complexity. I think that's one of the things that sticks out to me that we, even the three of us, I think have divergent definitions. And why wouldn't we? It's just such a broad ambiguous thing. But for a specific situation, I would say define what it means. Like if you, you can define complexity as number of responsibilities per file, that's fine, you know, and then work within those bounds, or lines of code or whatever. But get clear on that. No, know what you mean, all right? Because if you don't, like, you stick in this, I don't know, you You're in the space where we're all using the same word, but you mean different things. It's hard to talk.

[00:56:51] Dave Adsit: I'm thinking about programming languages. I had to look it up really quickly because I only know it's derivative language, not the original. But there's a programming language, P double prime, which only has four operators. And it's Turing complete because it expresses a finite state machine. And so you can write, presumably, any program can be expressed in that language. But I wouldn't call that easy because it's not familiar. I also don't think I would call it simple because you have to create all of the structures yourself. It's not complex, so maybe it's simple.

[00:57:33] Matt Baker: If you agree to the model of simple and easy, simple is number of things complected and easy is point of relativity, how easy it is for you to understand something. How familiar it is, yeah. Do you still say it's not simple, even in that context?

[00:57:47] Dave Adsit: Well, I don't think it's simple because I think that you have to complex those four operators millions of times before you've produced anything of use.

[00:57:55] Allan Stewart: Ah, interesting. Interesting. Going back to the search for simple tools concept, I'm okay with adopting more complex tools if it makes my code simple, because the code space is where I'm going to be living and having to deal with things on a day-to-day basis. Same thing goes with a framework. I might be willing to adopt a more complex framework if it means that I don't have to complex as much code to get the thing up and running and doing what it's supposed to be doing.

[00:58:27] Dave Adsit: I've been thinking about, recently I started at a new startup as well, Matt, and there was a code base. And it was arguably more simple than the code base we have now. Or maybe not even arguably. It was more simple than the code base we have now. It was Python Django, but that's unfamiliar to me. And so I was unable to do work in it. So I rewrote it in .NET. And one of the very first things I did is hook up a CICD pipeline because I have done many, many, many deploys to the target environment. And I knew that I didn't want to do that manually more than maybe once. In fact, I don't think I deployed manually even once. I think that the very first deploy of the environment was through the pipeline. And so was that jumping to complexity too quickly? Maybe.

[00:59:22] Matt Baker: How would you, if you actually tried to answer that question, like if you tried to like quantify the amount of complexity you took on and then the impact it had, how would you, you know what I mean? Like, is it just a squishy feely, or is it like, do you measure it in terms of complexity or in

[00:59:39] Dave Adsit: terms of time spent or how do you measure it?

[00:59:41] Matt Baker: I don't know. That's why I'm asking.

[00:59:44] Dave Adsit: So for me, it took me probably two hours to set up a CICD pipeline to a new environment. I don't do immutable infrastructure. I don't have infrastructure as code because that's, that to me, deploying over and over onto the same system feels simpler than spinning up new systems and tearing down old ones. Interesting. But I know that other people feel the exact opposite about that, the changing, mutating state on an existing system is, is more complex. Yeah, it's hard to

[01:00:18] Matt Baker: So it's hard to figure out what degree, like what, going to what Allan was saying, what abstraction you should consider this from when you say like, "well, this thing has more things or less things than that other thing." And something that you're saying, Dave, that's sticking out, the way you're talking makes it sound like there's this continuum, and on, or whatever you would call it, a spectrum, I guess. And then on one end, the simple, and the other one's easy. And like, we're dancing in the middle a lot of the time. Because I, and Allan has like hinted at this subtle connection too, right? As the more you make something easy, maybe the more complexity you take on. Like, like you were saying, if you make it easier to ship code to production, one command, bloop, you know, then like doing all the requisite, yeah

[01:01:03] Dave Adsit: I learned to Git so that I could do Git push so that I could deploy to production.

[01:01:09] Matt Baker: Yeah, like, that's, that's easier than, than not having a deployment pipeline when it comes to deploying to production, right? And it might, it's not simpler, I don't think. Like, I think it's dead simple to FTP into a server, SSH into server and, like, drop a file, but granted, it's like, it has less complecting. It's not what you want to do, so you, so you take on that complexity, and you know that. So I, I see now, like, the point I think that both of you have been highlighting, like, there's this dance back and forth, and maybe as you make things easier, you, you, you sacrifice complexity. And I still don't understand though, like, how to, how to not do this in a gut feely kind of way, you know? Like, and this is why I was harping on, like, how you define complexity at the beginning of the podcast. And I think Allan blew it out of the water when he said that, "well, there, it depends on the degree, the level of abstraction you're standing from." Like, once you add that to the stew, my whole, like, mental model model falls apart of like, how do I actually measure complexity in a meaningful way?

[01:02:12] Dave Adsit: Well, and I think that I'm, I'm relying on some complexity out there in the ether that I, if it broke and I had to fix it, it would be a problem because right now I get pushed to GitHub. There's a hook in Git that triggers an event on a server in Azure that then pulls my code and builds it and tests it and all of those things inside of a giant general purpose CI engine that creates an artifact that then gets deployed to a server also running in the cloud. And if I had to build all those things myself, you know what I would do is I would have a little one-use server in a rack, and I'd probably just connect up to it and drop my code directly on it. In fact, I've done that in the past. I have worked in environments where we would basically to debug a production problem, you would just remote desktop into that computer and change the code on the computer directly and see what happens because it was an interpreted language and you could just do that. That was substantially worse in a lot of ways, but also simpler than relying on Git and GitHub and Git hooks and open connections and TCP sockets and whatnot between Azure and GitHub and all of the different things that I have to rely on, but it's a behind an abstraction that I don't care about because I created a YAML file and then it worked, even though I don't love YAML because I don't love significant white space. In fact, I anti-love I love significant white space.

[01:03:53] Matt Baker: I've been coming around to it. I've been coding in Python for a while now.

[01:03:58] Dave Adsit: I will believe people when they turn off visual white space.

[01:04:03] Matt Baker: What's that? What do you mean visual white space?

[01:04:05] Dave Adsit: Do you see, can you see the difference between a space and a tab in your editor visually? No. Oh, and then you are coming around to it.

[01:04:19] Allan Stewart: I think the only thing that we can say for sure as we're wrapping up this topic is that we're in agreement that we should avoid things that are both difficult and complex and strive for things that are simple and easy. But where in that quadrant diagram you land is a much more murky question. One thing that is less murky is that the music for our podcast was generously provided by Todd Fisher, whom we thank. And as always, we recommend that you join a community of professionals by attending a Software Crafters group or meetup near you. The Utah SC group at utahsc.org has a virtual meeting the first Wednesday of each month. Maybe we will argue about simplicity and ease with you there.

~/podcast/episodes/018-searching-for-simple-tools $ cat ../../copyright.txt

Copyright © 2026 - Crafting Code Podcast