- Software fundamentals are more critical than ever in the age of AI, as good code bases allow AI to perform effectively, while bad code becomes prohibitively expensive.
- The "specs to code" approach, which repeatedly generates code from specifications without human design oversight, frequently leads to deteriorating code quality and increased complexity.
- Effective AI-assisted development requires a strategic human role focused on establishing shared understanding, using precise language, implementing TDD, and designing robust, deep modules.
"Software Fundamentals Matter More Than Ever" — Matt Pocock
- Prioritize software fundamentals: Good code bases and strong software design principles are essential for leveraging AI effectively, as AI struggles with poorly structured code and amplifies existing code debt.
- Avoid naive "specs to code" generation: Repeatedly generating code from high-level specifications without human intervention often produces unmaintainable "garbage" code.
- Establish a shared design concept: Use a "Grill Me" approach to have the AI relentlessly question you, similar to requirements gathering, until a mutual understanding of the project's design concept is achieved.
- Develop a ubiquitous language: Create a shared vocabulary of domain-specific terms (e.g., in a Markdown file) with the AI, aligning conversations, code expressions, and domain expert interactions to reduce verbosity and improve clarity.
- Enforce Test-Driven Development (TDD): Implement TDD to force the AI to take small, deliberate steps, create tests first, and refactor, which helps leverage feedback loops and prevents the generation of large, untestable code chunks.
- Design for deep modules: Structure your codebase with "deep modules" – large modules with extensive functionality hidden behind simple interfaces – to improve testability, maintainability, and the AI's ability to understand the system.
- Delegate implementation within deep modules: Once a module's simple interface is well-designed and tested, you can delegate the complex internal implementation to the AI, reducing cognitive load for human developers.
- Invest in system design daily: Continuously think about module boundaries and interfaces in all planning and code changes, as this strategic oversight is crucial for a healthy, evolving codebase.
Specs to Code — A development approach where high-level specifications are directly fed into an AI to generate code, often without manual intervention or focus on architectural quality.
Complexity — In software design, any aspect of a system's structure that makes it hard to understand and modify, often leading to increased bugs and development time.
Software Entropy — The tendency of software systems to degrade over time, becoming more chaotic and difficult to maintain with each change, similar to the physical concept of entropy.
Design Concept — The invisible, ephemeral, shared understanding or theory of what is being built between collaborators (e.g., human and AI) during the design process.
Grill Me — A technique or prompt where the AI is instructed to relentlessly interview the user about a plan until a shared, deep understanding is achieved, acting as an adversary to refine requirements.
Ubiquitous Language — From Domain-Driven Design (DDD), a shared, unambiguous language used by all team members (developers, domain experts, AI) derived from the domain model, used in code, documentation, and communication.
Domain-Driven Design (DDD) — A software development approach that focuses on connecting the implementation to an evolving model of the core business domain, emphasizing a ubiquitous language.
Feedback Loops — Mechanisms in software development (e.g., static types, automated tests) that provide rapid information about the quality and correctness of code changes.
Test-Driven Development (TDD) — A software development process where tests are written before the code itself, forcing small, incremental steps and ensuring code is testable and correct.
Deep Modules — Code modules that encapsulate a lot of functionality behind a relatively simple and narrow interface, hiding internal complexity and making them easier to understand, test, and modify from the outside.
So everyone, you have a good conference so far? Are you having a good conference so far? Good, wonderful. I have a message for you that I hope will be a comforting message for folks who believe that their skill set is no longer worth anything in this new age, which is I believe that self-worth fundamentals matter now more than they actually ever have. And I'm a teacher, and I have been recently teaching a course called Claude Code for Real Engineers, nice and provocative. And in the process of kind of working on this course, I had to come up with a curriculum about AI coding, which is a bit of a nightmare because things are changing all the time, right? So we have a whole new paradigm. We need to chuck out all of the old rules, surely, so that we can bring in the new stuff. And there's a kind of movement that has come up around this, which is the specs to code movements. And the specs to code movement says that, okay, you can write a specification about how an application is supposed to work. Then you can use AI to turn it into code. If there's a problem with the application, you then go back to the spec. And you can actually look at the code, just change the spec, you run the compiler again, and you end up with more code. Raise your hand if you've heard of that. Keep your hand raised if you've tried it. Okay, I've tried it too. You can put your hands down. What I noticed was I would run it, and I would try not to look at the code, but I would look at the code. And I realized I would get code out, first of all, and then I would run it, I would get worse code. And I got it again, I kept running the compiler, kept running the compiler, and I would just end up with garbage. Raise your hand if that's happened to you. Yes, I don't think this works. The idea that we can just ignore the code and just have the code let it manage itself is just sort of by coding by another name. And I didn't believe that back then. I thought, okay, how do I fix the compiler? How do I make it so that it doesn't produce bad code each time? Or worse code? And so I thought, okay, I need to explain to the LLM in English what a good code base looks like. Let me dig out one of my old favorite books, which is A Philosophy of Software Design by John Asterhouds. Go on Amazon, get it. And he has a definition for what bad code looks like. He calls it complex code. Complexity is anything related to the structure of a software system that makes it hard to understand and modify the system. So a bad code base is a code base that's hard to change. If you can't change a code base without causing bugs, then it's a bad code base. Good code bases aren't easy to change. So I thought, that was good. Let's try another book. Let's try the Parachematic Programmer. Go on Amazon, get it. They have a whole chapter on something called software entropy. And this is exactly what I was seeing. Entropy is the idea that things tend towards disaster and floating away from each other and collapse. And this is exactly how most software systems behave, too, is that every time you make a change to a code base, if you're only thinking about that change, not thinking about the design of the whole system, your code base is going to get worse and worse and worse. And that's what I was seeing. Everything inside the Spexta code idea that you just run, the compiler again and again, was making worse code. Now, there's an idea that sort of drives the Spexta code movement, which is that code is cheap. Raise your hand if you've heard that phrase before, that code is cheap. Well, I don't think this is right. I think code is not cheap. In fact, bad code is the most expensive it's ever been. Because if you have a code base that's hard to change, you're not able to take all of the bounty that AI can offer, because AI in a good code base actually does really, really well. And this means good code bases matter more than ever, which means software fundamentals matter more than ever. That's the thesis of this talk. So let's actually get into practical stuff. I want to talk about different failure modes that you may have experienced or you may not have experienced the ads with AI and how you can avoid them by just going back to old books and looking at software practices. So I'm good. So the first one is that the AI didn't do what I wanted. I thought I had a good idea in my head and the AI just did something totally different. Or it did some like specs that I, you know, it just made something I didn't want. Raise your hand if you've hit this mode. Cool. Okay. Well, this is what they say in the pragmatic programmer is that no one knows exactly what they want. It's that you and the AI there is a communication barrier there, right? And so when you're talking to the AI, that's kind of like the AI doing its requirements gathering. It's basically working out from you what it is that you need. And I realize that there was another book for a book, the design of design. And it talks about this idea called the design concept is that when you have more than one person designing something together, you have this idea sort of floating between you, this ephemeral idea of the thing that you're building. And that thing that you're building or the idea of it is called the design concept. It's not an asset. It's not something you can put in a markdown file. It is the invisible sort of theory of what you're building. And so I thought, okay, that's what's going on me and the AI don't share a design concept. So I came up with a skill. The skill is very, very simple. It's called Grill Me. And it looks like this. Interview me relentlessly about every aspect of this plan until we reach a shared understanding. Walk down each branch of the design tree, which is another thing from Frederick P. Brooks, resolving dependencies between decisions one by one. This skill is like the repo containing this skill has like 13,000 stars or something like it just went nuts, went viral, people love this thing. These couple of lines means the AI asks you like 40 questions, 60 questions. I've had people a hundred questions before it's satisfied they've reached a shared understanding. And it means it turns the AI into a kind of adversary where it's just continually pinging you ideas and trying to reach a shared understanding. And that means that the conversation that you then generates, you can take that and turn it into product requirements documents or something. Or if it's a small change, you can just do, turn it directly into issues. And then your AFK agent will then pick it up. And don't add me on this, but I personally believe this is better than the default plan mode in the tool that I use, which is called code. Plan mode is extremely eager to create an asset. It really wants to just create a plan and start working. Whereas I think it's a lot nicer to reach a shared design concept first. So that's tip number one. Now, failure mode number two is that the AI is just way too verbose. It's like you're almost talking across purposes with the AI. Raise your hand if you feel this. If you have experience that failure mode. Yeah. It's kind of like the AI is like talking just using too many words to try to communicate what it's doing. It's not like you're talking using the same language. And this to me felt very, very familiar. Right. If you've ever been a developer for a long time and you've worked with, let's say, domain experts, someone building an application. Let's say the domain expert wants you to build something on microchips. You have no idea what microchips are. You need to establish some kind of shared language. Right. Because otherwise they're going to be using terms you don't understand. You're going to be translating that into code that maybe you don't even understand. And certainly the domain expert won't. And so there's this kind of language gap between you and the domain expert. And so I went back to domain driven design. DDD. This is something I'm still kind of on the edge of exploring. But everything I'm reading about DDD is just music to my ears. I freaking love it. And DDD has a concept of a ubiquitous language. Whether ubiquitous language, conversations among developers and expressions of the code and conversations with domain experts are all derived from the same domain model. It's essentially a marked out file full of a list of terms that you and the AI have in common. And you really focus on those terms and you really make sure that they're aligned with what it actually means. And you use them all the time in the code when you're talking about the code when you're talking to domain experts. Or in our case when you're talking with AI. So I made a skill. This skill is the ubiquitous language skill basically just scans your code base looks for terminology and then creates a marked out file creates the ubiquitous language marked out file a bunch of marked down tables with all of the terminology. And this then I pass it to the AI and I'm able to read it to and I actually have it open all the time when I'm grilling with the AI and planning and that. What I noticed by reading the thinking traces of the AI not only improves the planning but it allows the AI to think in a less of a both way and actually means that the implementation is more aligned with what you actually plant. So this has absolutely been a powerhouse. It's unbelievably good. So that's tip number two create a shared language with the AI. So okay, let's imagine that you've aligned with the AI. You know what it is you're supposed to be building. The AI has built the right thing but it doesn't work. Raise your hands if that's happened to you. Yep, just done work. Well there's an obvious thing that we can do to make that better which is we can use feedback loops we can use static types you know if you're not using tax scripts. That's crazy. If you're not using if you're building a front end app and you're not giving it the alarm access to the browser so it can look around absolutely needs that. And you obviously also need automated tests. And one sort of thing I notice here is that even with these feedback loops the LLM doesn't use them very well. It doesn't kind of like get the most out of its feedback loops in the way that a veteran developer would. And so it does what it tends to do is just does way too much at once it will produce the huge amounts of code and then think oh I should probably type track that actually or I should maybe check a test on that. And this in the pragmatic programmer that describes as out running your headlights as essentially driving too fast because the rate of feedback is your speed limit. Which means that you should be testing as you go taking small deliberate steps and the AI by default is really not very good at that. So skill number three is TDD you should be using test driven developments because TDD forces the LLM to really take small steps you create a test first you make that test pass and then you refactor the code to make it nicer and consider the design. The issue here is that testing is really hard testing has always been hard and the reason for that is there a ton of different decisions you need to make when you write a test. You need to figure out how big a unit do you want to test. You need to figure out what to mock you need to figure out what behaviors do you even want to test in the first place. And all of these decisions are dependent so if you are testing a really big units like an entire massive application then it might be quite flaky you might not want to test that many behaviors. If you only test this unit you need to mock this unit you know it's all into linked and I've been thinking about this for years my entire development career. And what we notice is that good code bases are easy code bases to test right. So here we're starting to get back to the idea of code being important is that the better your code bases the better your feedback loops are because you're able to give better feedback to the LLM it produces better code. And so I thought what does a good code base what does a testable code base look like again we go to join out to how he talks about having deep modules in your code base not shallow modules not lots of modules that expose like kind of lots of functions they should be relatively few large deep modules with simple interfaces. Let's compare them quickly deep modules lots of functionality hidden behind a simple interface hiding the complexity. You can look inside the deep module if you want to but you don't need to you can just use the interface shallow modules not much functionality complex interface. And I just wait for you to take the photos. So shallow modules in a code base kind of look like this we have a ton of different tiny little blobs that the AI has to walk through and navigate. And this is really hard for the AI to explore actually. And so often what you'll see is if you have a code base like this which AI is really good at creating code bases like this is that you'll have a situation where AI doesn't understand what your code is doing. It will attempt to explore the code but because it's poorly laid out filled with shallow modules it doesn't maybe get to the right module in time or doesn't understand all the dependencies all that stuff it doesn't understand your code. And so what does code base full of deep modules look like? Well it looks like this where it's the same code but it's just structured inside boundaries where you have these interfaces on the top. And these interfaces you should probably have a lot of control over them and design them really well otherwise you know AI might mess up the design. But the implementation you can kind of leave that to the AI bit. So how do you turn a code base that looks like this into a code base that looks like that? Well I've got a skill for that improve code base architecture turns out this is not it's quite complicated to do this but it's just like a set of steps. Like a set of steps that you can reusably do again and again you just sort of explore the code base look for opportunities where there's code that's kind of related and wrap all of that in a deep module. And this is a testable code base because the boundaries around this code are so so simple you test at the interface you verify using that interface and you go to go. So this is a code base that rewards to you. But how about failure mode number six which is your okay let's say your feedback loops are working let's say that things are kicking into gear you're able to ship more code than you ever have before but your brain can't keep up. Right raise your hand if you felt more tired than you have ever before in your development career. Yeah me too it's not caring and I think this is a code base that actually makes it harder for your brain because you as well as the AI need to keep all of that information in your head. Whereas this not only is it simpler for you to read and understand it also means you can kind of treat these modules or these deep modules as gray boxes. You can kind of say okay I'm going to just design the interface but I'm not going to worry too much or not review the implementation too much. You can do this obviously with things that are less critical in your application. Come to this with various things like finance or whatever but in many many modules in your app you don't need to think about the implementation too much as long as you have a testable boundary outside the module and as long as you understand its purpose and can design it from the outside. I have found this has really saved my brain because I can just go okay the AI I'll let you handle what's inside the big blob. I'm just going to test from the outside and verify it. So that's tip number five. Design the interface, delegate the implementation. But this means that whenever we're touching the code whenever we're planning stuff we need to think about and be aware of the modules in our application. We need to know that map really well. It needs to be part of our ubiquitous language. We need to build it into our planning skills as well. So my right to PRD inside the PRD I'm specific about the module changes and the interfaces inside those modules. How they're being modified I'm thinking about them all the time and this comes from Kent Beck. Invest in the design of the system every day. And this is the core of it right because it's the code we are not investing in the design of the system. We are divesting from it. We're getting rid of that. Whereas this I think is absolutely key. And so code is not cheap. That's the message I want you to take away. Code is important. And if we think about AI as a really great on the ground programmer, a kind of tactical programmer, a sergeant on the ground making the code changes. You need someone above that. You need someone thinking on the strategic level. And that's you. And that requires software fundamental skills that we've been using for 20 years from longer. Now if you were interested in any of the skills I put up here, it's in the GitHub repo, MacPokec skills. And if you're interested in the training that I do or any free stuff, I'm on YouTube, I'm on Twitter, but I'm also an AI hero.dev where I have a newsletter that you can check out. Thank you so much. I hope that this gives you confidence in this new AI age. You can actually make a good impact. Thank you.
TL;DR
- Software fundamentals are more critical than ever in the age of AI, as good code bases allow AI to perform effectively, while bad code becomes prohibitively expensive.
- The "specs to code" approach, which repeatedly generates code from specifications without human design oversight, frequently leads to deteriorating code quality and increased complexity.
- Effective AI-assisted development requires a strategic human role focused on establishing shared understanding, using precise language, implementing TDD, and designing robust, deep modules.
Takeaways
- Prioritize software fundamentals: Good code bases and strong software design principles are essential for leveraging AI effectively, as AI struggles with poorly structured code and amplifies existing code debt.
- Avoid naive "specs to code" generation: Repeatedly generating code from high-level specifications without human intervention often produces unmaintainable "garbage" code.
- Establish a shared design concept: Use a "Grill Me" approach to have the AI relentlessly question you, similar to requirements gathering, until a mutual understanding of the project's design concept is achieved.
- Develop a ubiquitous language: Create a shared vocabulary of domain-specific terms (e.g., in a Markdown file) with the AI, aligning conversations, code expressions, and domain expert interactions to reduce verbosity and improve clarity.
- Enforce Test-Driven Development (TDD): Implement TDD to force the AI to take small, deliberate steps, create tests first, and refactor, which helps leverage feedback loops and prevents the generation of large, untestable code chunks.
- Design for deep modules: Structure your codebase with "deep modules" – large modules with extensive functionality hidden behind simple interfaces – to improve testability, maintainability, and the AI's ability to understand the system.
- Delegate implementation within deep modules: Once a module's simple interface is well-designed and tested, you can delegate the complex internal implementation to the AI, reducing cognitive load for human developers.
- Invest in system design daily: Continuously think about module boundaries and interfaces in all planning and code changes, as this strategic oversight is crucial for a healthy, evolving codebase.
Vocabulary
Specs to Code — A development approach where high-level specifications are directly fed into an AI to generate code, often without manual intervention or focus on architectural quality.
Complexity — In software design, any aspect of a system's structure that makes it hard to understand and modify, often leading to increased bugs and development time.
Software Entropy — The tendency of software systems to degrade over time, becoming more chaotic and difficult to maintain with each change, similar to the physical concept of entropy.
Design Concept — The invisible, ephemeral, shared understanding or theory of what is being built between collaborators (e.g., human and AI) during the design process.
Grill Me — A technique or prompt where the AI is instructed to relentlessly interview the user about a plan until a shared, deep understanding is achieved, acting as an adversary to refine requirements.
Ubiquitous Language — From Domain-Driven Design (DDD), a shared, unambiguous language used by all team members (developers, domain experts, AI) derived from the domain model, used in code, documentation, and communication.
Domain-Driven Design (DDD) — A software development approach that focuses on connecting the implementation to an evolving model of the core business domain, emphasizing a ubiquitous language.
Feedback Loops — Mechanisms in software development (e.g., static types, automated tests) that provide rapid information about the quality and correctness of code changes.
Test-Driven Development (TDD) — A software development process where tests are written before the code itself, forcing small, incremental steps and ensuring code is testable and correct.
Deep Modules — Code modules that encapsulate a lot of functionality behind a relatively simple and narrow interface, hiding internal complexity and making them easier to understand, test, and modify from the outside.
Transcript
So everyone, you have a good conference so far? Are you having a good conference so far? Good, wonderful. I have a message for you that I hope will be a comforting message for folks who believe that their skill set is no longer worth anything in this new age, which is I believe that self-worth fundamentals matter now more than they actually ever have. And I'm a teacher, and I have been recently teaching a course called Claude Code for Real Engineers, nice and provocative. And in the process of kind of working on this course, I had to come up with a curriculum about AI coding, which is a bit of a nightmare because things are changing all the time, right? So we have a whole new paradigm. We need to chuck out all of the old rules, surely, so that we can bring in the new stuff. And there's a kind of movement that has come up around this, which is the specs to code movements. And the specs to code movement says that, okay, you can write a specification about how an application is supposed to work. Then you can use AI to turn it into code. If there's a problem with the application, you then go back to the spec. And you can actually look at the code, just change the spec, you run the compiler again, and you end up with more code. Raise your hand if you've heard of that. Keep your hand raised if you've tried it. Okay, I've tried it too. You can put your hands down. What I noticed was I would run it, and I would try not to look at the code, but I would look at the code. And I realized I would get code out, first of all, and then I would run it, I would get worse code. And I got it again, I kept running the compiler, kept running the compiler, and I would just end up with garbage. Raise your hand if that's happened to you. Yes, I don't think this works. The idea that we can just ignore the code and just have the code let it manage itself is just sort of by coding by another name. And I didn't believe that back then. I thought, okay, how do I fix the compiler? How do I make it so that it doesn't produce bad code each time? Or worse code? And so I thought, okay, I need to explain to the LLM in English what a good code base looks like. Let me dig out one of my old favorite books, which is A Philosophy of Software Design by John Asterhouds. Go on Amazon, get it. And he has a definition for what bad code looks like. He calls it complex code. Complexity is anything related to the structure of a software system that makes it hard to understand and modify the system. So a bad code base is a code base that's hard to change. If you can't change a code base without causing bugs, then it's a bad code base. Good code bases aren't easy to change. So I thought, that was good. Let's try another book. Let's try the Parachematic Programmer. Go on Amazon, get it. They have a whole chapter on something called software entropy. And this is exactly what I was seeing. Entropy is the idea that things tend towards disaster and floating away from each other and collapse. And this is exactly how most software systems behave, too, is that every time you make a change to a code base, if you're only thinking about that change, not thinking about the design of the whole system, your code base is going to get worse and worse and worse. And that's what I was seeing. Everything inside the Spexta code idea that you just run, the compiler again and again, was making worse code. Now, there's an idea that sort of drives the Spexta code movement, which is that code is cheap. Raise your hand if you've heard that phrase before, that code is cheap. Well, I don't think this is right. I think code is not cheap. In fact, bad code is the most expensive it's ever been. Because if you have a code base that's hard to change, you're not able to take all of the bounty that AI can offer, because AI in a good code base actually does really, really well. And this means good code bases matter more than ever, which means software fundamentals matter more than ever. That's the thesis of this talk. So let's actually get into practical stuff. I want to talk about different failure modes that you may have experienced or you may not have experienced the ads with AI and how you can avoid them by just going back to old books and looking at software practices. So I'm good. So the first one is that the AI didn't do what I wanted. I thought I had a good idea in my head and the AI just did something totally different. Or it did some like specs that I, you know, it just made something I didn't want. Raise your hand if you've hit this mode. Cool. Okay. Well, this is what they say in the pragmatic programmer is that no one knows exactly what they want. It's that you and the AI there is a communication barrier there, right? And so when you're talking to the AI, that's kind of like the AI doing its requirements gathering. It's basically working out from you what it is that you need. And I realize that there was another book for a book, the design of design. And it talks about this idea called the design concept is that when you have more than one person designing something together, you have this idea sort of floating between you, this ephemeral idea of the thing that you're building. And that thing that you're building or the idea of it is called the design concept. It's not an asset. It's not something you can put in a markdown file. It is the invisible sort of theory of what you're building. And so I thought, okay, that's what's going on me and the AI don't share a design concept. So I came up with a skill. The skill is very, very simple. It's called Grill Me. And it looks like this. Interview me relentlessly about every aspect of this plan until we reach a shared understanding. Walk down each branch of the design tree, which is another thing from Frederick P. Brooks, resolving dependencies between decisions one by one. This skill is like the repo containing this skill has like 13,000 stars or something like it just went nuts, went viral, people love this thing. These couple of lines means the AI asks you like 40 questions, 60 questions. I've had people a hundred questions before it's satisfied they've reached a shared understanding. And it means it turns the AI into a kind of adversary where it's just continually pinging you ideas and trying to reach a shared understanding. And that means that the conversation that you then generates, you can take that and turn it into product requirements documents or something. Or if it's a small change, you can just do, turn it directly into issues. And then your AFK agent will then pick it up. And don't add me on this, but I personally believe this is better than the default plan mode in the tool that I use, which is called code. Plan mode is extremely eager to create an asset. It really wants to just create a plan and start working. Whereas I think it's a lot nicer to reach a shared design concept first. So that's tip number one. Now, failure mode number two is that the AI is just way too verbose. It's like you're almost talking across purposes with the AI. Raise your hand if you feel this. If you have experience that failure mode. Yeah. It's kind of like the AI is like talking just using too many words to try to communicate what it's doing. It's not like you're talking using the same language. And this to me felt very, very familiar. Right. If you've ever been a developer for a long time and you've worked with, let's say, domain experts, someone building an application. Let's say the domain expert wants you to build something on microchips. You have no idea what microchips are. You need to establish some kind of shared language. Right. Because otherwise they're going to be using terms you don't understand. You're going to be translating that into code that maybe you don't even understand. And certainly the domain expert won't. And so there's this kind of language gap between you and the domain expert. And so I went back to domain driven design. DDD. This is something I'm still kind of on the edge of exploring. But everything I'm reading about DDD is just music to my ears. I freaking love it. And DDD has a concept of a ubiquitous language. Whether ubiquitous language, conversations among developers and expressions of the code and conversations with domain experts are all derived from the same domain model. It's essentially a marked out file full of a list of terms that you and the AI have in common. And you really focus on those terms and you really make sure that they're aligned with what it actually means. And you use them all the time in the code when you're talking about the code when you're talking to domain experts. Or in our case when you're talking with AI. So I made a skill. This skill is the ubiquitous language skill basically just scans your code base looks for terminology and then creates a marked out file creates the ubiquitous language marked out file a bunch of marked down tables with all of the terminology. And this then I pass it to the AI and I'm able to read it to and I actually have it open all the time when I'm grilling with the AI and planning and that. What I noticed by reading the thinking traces of the AI not only improves the planning but it allows the AI to think in a less of a both way and actually means that the implementation is more aligned with what you actually plant. So this has absolutely been a powerhouse. It's unbelievably good. So that's tip number two create a shared language with the AI. So okay, let's imagine that you've aligned with the AI. You know what it is you're supposed to be building. The AI has built the right thing but it doesn't work. Raise your hands if that's happened to you. Yep, just done work. Well there's an obvious thing that we can do to make that better which is we can use feedback loops we can use static types you know if you're not using tax scripts. That's crazy. If you're not using if you're building a front end app and you're not giving it the alarm access to the browser so it can look around absolutely needs that. And you obviously also need automated tests. And one sort of thing I notice here is that even with these feedback loops the LLM doesn't use them very well. It doesn't kind of like get the most out of its feedback loops in the way that a veteran developer would. And so it does what it tends to do is just does way too much at once it will produce the huge amounts of code and then think oh I should probably type track that actually or I should maybe check a test on that. And this in the pragmatic programmer that describes as out running your headlights as essentially driving too fast because the rate of feedback is your speed limit. Which means that you should be testing as you go taking small deliberate steps and the AI by default is really not very good at that. So skill number three is TDD you should be using test driven developments because TDD forces the LLM to really take small steps you create a test first you make that test pass and then you refactor the code to make it nicer and consider the design. The issue here is that testing is really hard testing has always been hard and the reason for that is there a ton of different decisions you need to make when you write a test. You need to figure out how big a unit do you want to test. You need to figure out what to mock you need to figure out what behaviors do you even want to test in the first place. And all of these decisions are dependent so if you are testing a really big units like an entire massive application then it might be quite flaky you might not want to test that many behaviors. If you only test this unit you need to mock this unit you know it's all into linked and I've been thinking about this for years my entire development career. And what we notice is that good code bases are easy code bases to test right. So here we're starting to get back to the idea of code being important is that the better your code bases the better your feedback loops are because you're able to give better feedback to the LLM it produces better code. And so I thought what does a good code base what does a testable code base look like again we go to join out to how he talks about having deep modules in your code base not shallow modules not lots of modules that expose like kind of lots of functions they should be relatively few large deep modules with simple interfaces. Let's compare them quickly deep modules lots of functionality hidden behind a simple interface hiding the complexity. You can look inside the deep module if you want to but you don't need to you can just use the interface shallow modules not much functionality complex interface. And I just wait for you to take the photos. So shallow modules in a code base kind of look like this we have a ton of different tiny little blobs that the AI has to walk through and navigate. And this is really hard for the AI to explore actually. And so often what you'll see is if you have a code base like this which AI is really good at creating code bases like this is that you'll have a situation where AI doesn't understand what your code is doing. It will attempt to explore the code but because it's poorly laid out filled with shallow modules it doesn't maybe get to the right module in time or doesn't understand all the dependencies all that stuff it doesn't understand your code. And so what does code base full of deep modules look like? Well it looks like this where it's the same code but it's just structured inside boundaries where you have these interfaces on the top. And these interfaces you should probably have a lot of control over them and design them really well otherwise you know AI might mess up the design. But the implementation you can kind of leave that to the AI bit. So how do you turn a code base that looks like this into a code base that looks like that? Well I've got a skill for that improve code base architecture turns out this is not it's quite complicated to do this but it's just like a set of steps. Like a set of steps that you can reusably do again and again you just sort of explore the code base look for opportunities where there's code that's kind of related and wrap all of that in a deep module. And this is a testable code base because the boundaries around this code are so so simple you test at the interface you verify using that interface and you go to go. So this is a code base that rewards to you. But how about failure mode number six which is your okay let's say your feedback loops are working let's say that things are kicking into gear you're able to ship more code than you ever have before but your brain can't keep up. Right raise your hand if you felt more tired than you have ever before in your development career. Yeah me too it's not caring and I think this is a code base that actually makes it harder for your brain because you as well as the AI need to keep all of that information in your head. Whereas this not only is it simpler for you to read and understand it also means you can kind of treat these modules or these deep modules as gray boxes. You can kind of say okay I'm going to just design the interface but I'm not going to worry too much or not review the implementation too much. You can do this obviously with things that are less critical in your application. Come to this with various things like finance or whatever but in many many modules in your app you don't need to think about the implementation too much as long as you have a testable boundary outside the module and as long as you understand its purpose and can design it from the outside. I have found this has really saved my brain because I can just go okay the AI I'll let you handle what's inside the big blob. I'm just going to test from the outside and verify it. So that's tip number five. Design the interface, delegate the implementation. But this means that whenever we're touching the code whenever we're planning stuff we need to think about and be aware of the modules in our application. We need to know that map really well. It needs to be part of our ubiquitous language. We need to build it into our planning skills as well. So my right to PRD inside the PRD I'm specific about the module changes and the interfaces inside those modules. How they're being modified I'm thinking about them all the time and this comes from Kent Beck. Invest in the design of the system every day. And this is the core of it right because it's the code we are not investing in the design of the system. We are divesting from it. We're getting rid of that. Whereas this I think is absolutely key. And so code is not cheap. That's the message I want you to take away. Code is important. And if we think about AI as a really great on the ground programmer, a kind of tactical programmer, a sergeant on the ground making the code changes. You need someone above that. You need someone thinking on the strategic level. And that's you. And that requires software fundamental skills that we've been using for 20 years from longer. Now if you were interested in any of the skills I put up here, it's in the GitHub repo, MacPokec skills. And if you're interested in the training that I do or any free stuff, I'm on YouTube, I'm on Twitter, but I'm also an AI hero.dev where I have a newsletter that you can check out. Thank you so much. I hope that this gives you confidence in this new AI age. You can actually make a good impact. Thank you.