Learning Library

← Back to Library

The New Software Crisis: AI-Generated Code

Key Points

  • The speaker admits that developers routinely ship generated code they can’t fully explain, a habit that’s become common across the industry.
  • He traces this “software crisis” back to recurring historical cycles—each leap in hardware or methodology (C, personal PCs, OOP, agile) expands demand faster than programmers can manage.
  • The recent surge in AI‑generated code speeds up delivery but creates a trap of conflating “easy” with “simple,” leaving engineers unable to keep up with understanding the resulting complexity.
  • To avoid costly failures like the recent Cloudflare outage, the fix is to stop outsourcing critical thinking to tools and ensure developers maintain deep, personal comprehension of the code they deploy.

Sections

Full Transcript

# The New Software Crisis: AI-Generated Code **Source:** [https://www.youtube.com/watch?v=eIoohUmYpGI](https://www.youtube.com/watch?v=eIoohUmYpGI) **Duration:** 00:18:57 ## Summary - The speaker admits that developers routinely ship generated code they can’t fully explain, a habit that’s become common across the industry. - He traces this “software crisis” back to recurring historical cycles—each leap in hardware or methodology (C, personal PCs, OOP, agile) expands demand faster than programmers can manage. - The recent surge in AI‑generated code speeds up delivery but creates a trap of conflating “easy” with “simple,” leaving engineers unable to keep up with understanding the resulting complexity. - To avoid costly failures like the recent Cloudflare outage, the fix is to stop outsourcing critical thinking to tools and ensure developers maintain deep, personal comprehension of the code they deploy. ## Sections - [00:00:00](https://www.youtube.com/watch?v=eIoohUmYpGI&t=0s) **Shipping Code You Don't Understand** - The speaker confesses to deploying AI‑generated code without fully grasping its behavior, warns that rapid, opaque coding leads to fragile systems, and urges developers to reclaim understanding over blind automation. - [00:03:12](https://www.youtube.com/watch?v=eIoohUmYpGI&t=192s) **AI Code Generation and the Silver Bullet** - The speaker argues that while AI tools automate coding mechanics, the real productivity barrier remains deep problem understanding, echoing Brooks' “No Silver Bullet” thesis and Rich Hickey’s distinction between simple and easy. - [00:06:35](https://www.youtube.com/watch?v=eIoohUmYpGI&t=395s) **AI‑Generated Code Fuels Complexity** - The speaker warns that AI code generators opt for the easiest fixes, indiscriminately preserving every existing pattern and thereby turning technical debt into tangled, accidental complexity that quickly overwhelms essential system logic. - [00:09:43](https://www.youtube.com/watch?v=eIoohUmYpGI&t=583s) **Distilling Massive Codebases into Specs** - The speaker explains how, when faced with a million‑line Java codebase exceeding an AI’s context window, they isolate essential patterns by crafting concise design specifications and precise step‑by‑step instructions for the model. - [00:12:51](https://www.youtube.com/watch?v=eIoohUmYpGI&t=771s) **Structured Planning for Rapid AI Code** - Emphasizes clean service boundaries, concise specifications, and fast validation to enable AI to implement code efficiently without excessive back-and-forth. - [00:15:56](https://www.youtube.com/watch?v=eIoohUmYpGI&t=956s) **Understanding Over AI Speed** - The speaker warns that depending on rapid AI‑generated code creates a knowledge gap and erodes the intuition needed to maintain and evolve systems, emphasizing that deep, manual understanding remains essential despite advances in models and prompts. ## Full Transcript
0:13[music] 0:20Hey everyone, good afternoon. Um, I'm 0:23going to start my talk with a bit of a 0:24confession. Uh, I've shipped code I 0:28didn't quite understand. Generated it, 0:30tested it, deployed it. Couldn't explain 0:32how it worked. And here's the thing, 0:34though. I'm willing to bet every one of 0:36you have, too. [applause] 0:40So, now I'm going to admit that we all 0:41ship code that we don't understand 0:42anymore. I want to take a bit of a 0:44journey, see how this kind of has come 0:45to be. First, look back in history. We 0:48see that history tends to repeat itself. 0:50Second, we've fallen into a bit of a 0:52trap. We've confused easy with simple. 0:55Lastly, there is a fix, but it requires 0:57us not to outsource our thinking. 1:01So, I spent the last few years at 1:03Netflix helping drive adoption of AI 1:04tools, and I have to say the 1:05acceleration is absolutely real. Backlog 1:08items that used to take days now take 1:09hours, and large refactors that have 1:12been on the books for years are finally 1:13being done. Here's the thing, though. 1:16Large production systems always fail in 1:18unexpected ways. Like, look what 1:19happened with CloudFare recently. When 1:21they do, you better understand the code 1:23you're debugging. And the problem is now 1:25we're generating code at such speed and 1:26such volume our understanding is having 1:28a hard time keeping up. 1:32Hell, I know I've done it myself. I've 1:34generated a bunch of code, looked at it, 1:36thought, I have no idea how this what 1:38this does. But, you know, the test pass, 1:40it works. So, I shipped it. The thing 1:43here is this isn't really new. Every 1:44generation of software engineers has 1:46eventually hit a wall where software 1:48complexity has exceeded their ability to 1:49manage it. We're not the fa first to 1:51face a software crisis. were the first 1:53to face it at this infinite scale of 1:54generation. So let's take a step back to 1:57see where this all started. 1:59In the late 60s, early '7s, a bunch of 2:02smart computer scientists at the time 2:03came together and said, "Hey, we're in a 2:05software crisis. We have this huge 2:08demand for software and yet we're not 2:10really able to keep up and like projects 2:12are taking too long and it's just really 2:14slow. We're not doing a good job." 2:16So Dystra Kano came up with a really 2:19great quote and he said when we had a 2:21few weak computers and I mean to 2:22paraphrase a longer quote when we had a 2:23few weak computers programming was a 2:26mild problem and now we have gigantic 2:27computers programming has become a 2:29gigantic problem. He was explaining as 2:31hardware power grew by a factor of a 2:33thousand society's wants of software 2:35grew in proportion and so it left us the 2:38programmers to figure out between the 2:40ways and the means how do we support 2:42this much more software. 2:44So this kind of keeps happening in a 2:46cycle. In the 70s we get the C 2:48programming language so we could write 2:49bigger systems. The 80s we have personal 2:51computers. Now everyone can write 2:52software. In the '9s we get 2:54object-oriented programming inheritance 2:57hierarchies from hell where you know 2:58thanks Java for that. In the 2000s we 3:01get agile and we sprints and scrum 3:03masters telling us what to do. There's 3:05no more waterfall. In the 2010s we had 3:07cloud mobile devops you know everything. 3:09Software truly ate the world. 3:12And today now we have AI. you know, 3:14co-pilot, cursor, claude, codeex, 3:16gemini, you name it. We could generate 3:17code as fast as we can describe it. The 3:20pattern continues, but the stale has 3:21really changed. It's it's infinite now. 3:25So, uh, Fred Brooks, you might know him 3:27from writing the mythical man month. He 3:29also wrote a paper in 1986 called No 3:31Silver Bullet. And in this, he argued 3:33that there'd be no single innovation 3:35that would give us an order of magnitude 3:37improvement in software productivity. 3:39Why? Because he said the hard part 3:41wasn't ever the mechanics of coding. the 3:44syntax, the typing, the boilerplate. It 3:46was about understanding the actual 3:47problem and designing the solution. And 3:49no tool can eliminate that fundamental 3:51difficulty. Every tool and technique 3:53we've created up to this point makes the 3:54mechanics easier. The core challenge 3:56though, understanding what to build, how 3:58it should work remains just as hard. 4:03So, if the problem isn't in the 4:04mechanics, why do we keep optimizing for 4:06it? How do experienced engineers end up 4:07with code they don't understand? Now, 4:09the answer, I think, comes down to two 4:11words we tend to confuse. simple and 4:13easy. We tend to use them 4:15interchangeably, but they really mean 4:16completely different things. Uh I was 4:19outed at the speaker dinner as being a 4:20closure guy, so this is kind of clear 4:22here. But Rich Hickey, the creator of 4:24the closure programming language, 4:25explained this in his talk from 2011 4:27called simple made easy. He defined 4:30simple meaning one fold, one braid, and 4:32no entanglement. Each piece does one 4:34thing and doesn't intertwine with 4:35others. He defines easy as meaning 4:38adjacent. What's within reach? What can 4:39you access without effort? Copy paste 4:42ship. Simple is about structure. Easy is 4:45about proximity. 4:48The thing is we can't make something 4:49simple by wishing it. So simplicity 4:51requires thought, design and untangling. 4:54But we can always make something easier. 4:56You just put it closer. Install a 4:58package, generate it with AI, you know, 5:00copy a solution off of Stack Overflow. 5:03It's it's human nature to take the easy 5:05path. We're wired for it. You know, as I 5:08said, copy something from Stack 5:09Overflow. It's right there. framework 5:10that handles everything for you with 5:12magic. Install and go. But easy doesn't 5:15mean simple. Easy means you can add to 5:16your system quickly. Simple means you 5:18can understand the work that you've 5:20done. Every time we choose easy, we're 5:22choosing speed now. Complexity later. 5:24And honestly, 5:25that trade-off really used to work. The 5:28complexity accumulated in our codebases 5:30slowly enough that we can refactor, 5:32rethink, and rebuild when needed. I 5:34think AI has destroyed that balance 5:36because it's the ultimate easy bun. And 5:37it makes the easy path so frictionless 5:39that we don't even consider the simple 5:40one anymore. Why think about 5:42architecture when code appears 5:43instantly. 5:46So let me show you how this happens. How 5:47a simple task evolves into a mess of 5:49complexity through a conversational 5:50interface that we've all come to love. 5:53You know this is a contrived example but 5:54you know say we have our app. We want to 5:56add uh some authentication to it. We say 5:58add o. So we get a nice clean o.js file. 6:01Iterate on a few times it gets a message 6:02file. You're like okay cool. We're going 6:03to add OOTH now too because and now 6:06we've got an OJS and OOTHJS. We keep 6:08iterating and then we find ourselves 6:09that sessions are broken and we got a 6:11bunch of conflicts and by the time you 6:12get to turn 20, you're not really having 6:14a discussion anymore. You're managing 6:15context that become so complex that even 6:18you don't remember all the constraints 6:19that you've added to it. Dead code from 6:21abandoned approaches. Uh tests that got 6:23fixed by just making them work. You 6:25know, fragments of three different 6:26solutions because you have saying wait 6:28actually each new instruction is 6:30overwriting architectural patterns. We 6:32said make the off work here. It did. 6:33When we said fix this error, it did. 6:35There's no resistance to bad 6:37architectural decisions. The code just 6:38morphs to satisfy your latest request. 6:40Each interaction is choosing easy over 6:42simple. And easy always means more 6:45complexity. We know better. But when the 6:48easy path is just this easy, we take it. 6:50And complexity is going to compound 6:51until it's too late. 6:55AI really takes easy to its logical 6:57extreme. Decide what you want. Get code 7:00instantly. But here's the danger in 7:02that. The generated code treats every 7:04pattern in your codebase the same. You 7:07know, when an agent analyzed your 7:08codebase, every line becomes a pattern 7:10to preserve. The authentication check on 7:11line 47, that's a pattern. That weird 7:14gRPC code that's acting like GraphQL 7:16that I may have had in 2019, that's also 7:18a pattern. Technical debt doesn't 7:20register as debt. It's just more code. 7:22The real problem here is complexity. I 7:25know I've been saying that word a bunch 7:27in this talk without really defining it, 7:29but the best way to think about it is 7:30it's the opposite of simplicity. It just 7:32means intertwined. And when things are 7:33complex, everything touches everything 7:35else. You can't change one thing without 7:37affecting 10 others. 7:41So, back to Fred Brooks's no bullet 7:43paper. In it, he identified that there's 7:45two main types of complexity in every 7:46system. There's the essential 7:48complexity, which is really the 7:50fundamental difficulty of the actual 7:52problem you're trying to solve. Users 7:53need to pay for things, orders must be 7:55fulfilled. This is the complexity of why 7:57your software system exists in the first 7:58place. And then second, there's this 8:01idea of accidental complexity. 8:03Everything else we've added along the 8:04way, workarounds, defensive code, 8:06frameworks, abstractions that made sense 8:08a while ago, it's all the stuff that we 8:10put together to make the code itself 8:11work. 8:13In a real codebase, these two types of 8:14complexity are everywhere and they get 8:17so tangled together that separating them 8:18requires context, history, and 8:19experience. 8:21the generated output makes no such 8:23distinction and so every pattern is 8:25keeps just getting preserved. 8:29So here's a real example from uh some 8:31work we're doing at Netflix. I have a 8:32system that has a abstraction layer 8:34sitting between our old authorization 8:36code we wrote say five or so years ago 8:38and a new centralized o system. We 8:41didn't have time to rebuild our whole 8:42app. So we just kind of put a shim in 8:44between. So now we have AI. This is a 8:46great opportunity to refactor our code 8:47to use the new system directly. Seems 8:49like a simple request, right? 8:52And no, it's like the old code was just 8:54so tightly coupled to its authorization 8:56patterns. Like we had permission checks 8:58woven through business logic, ro 8:59assumptions baked into data models and 9:01off calls scattered across hundreds of 9:03files. The agent would start 9:05refactoring, get a few files in and hit 9:07a dependency couldn't untangle and just 9:09spiral out of control and give up or 9:11worse it would try and preserve some 9:13existing logic that from the old system 9:16and recreating it using the new system 9:17which I think is not great too. 9:21The thing is it couldn't see the scenes. 9:23It couldn't identify where the business 9:24logic ended and the off logic began. 9:26Everything was so tangled together that 9:28even with perfect information, the AI 9:31couldn't find a clean path through. When 9:33your accidental complexity gets this 9:34tangled, AI is not the best help to 9:37actually make it any better. I found it 9:39only adds more layers on top. 9:42We can tell the difference, or at least 9:43we can when we slow down enough to 9:45think. We know which patterns are 9:47essential and which are just how someone 9:48solved it a few years ago. We carry the 9:51context that the AI can infer, but only 9:53if we time to make take time to make 9:54these distinctions before we start. 9:59So how do you actually do it? How do you 10:02separate the accidental and essential 10:04complexity when you're staring at a huge 10:05codebase? Codebase I work on Netflix has 10:08around a million lines of Java and the 10:10main service in it is about 5 million 10:11tokens last time I checked. no context 10:14window I have access to uh can hold it. 10:17So when I wanted to work with it, I 10:18first thought, hey, maybe I could just 10:19copy large swaths of this codebase into 10:21the into the context and see if the 10:23patterns were emerged, see if it would 10:24just be able to figure out what's 10:25happening. And just like the 10:27authorization refactor from previously, 10:28[clears throat] the output just got lost 10:30in its own complexity. So with this, I 10:32was forced to do something different. I 10:34had to select what to include. Design 10:36docs, architecture, diagrams, key 10:37interfaces, you name it, and take time 10:39writing out the requirements of how 10:40components should interact and what 10:42patterns to follow. 10:44See, I was writing a spec. Uh 5 million 10:47tokens became 2,000 words of 10:48specification. And then to take it even 10:50further, take that spec and create an 10:52exact step set of steps of code to 10:54execute. No vague instructions, just a 10:56precise sequence of operations. I found 10:58this produced much cleaner and more 11:00focused code that I could understand. As 11:02I defined it first and planned its own 11:04execution, 11:08this became the approach which I called 11:10context compression a while ago. But you 11:11call it context engineering or 11:12spectriven development, whatever you 11:14want. The name doesn't matter. What only 11:17matters here is that thinking and 11:18planning become a majority of the work. 11:20So let me walk you through that how this 11:22works in practice. 11:24So we have step one, phase one, 11:25research. You know, I go and feed 11:27everything to it up front. Architecture 11:29diagrams, documentation, Slack threads. 11:31I been over this a bunch, but really 11:33just bring as much context as you can 11:34that's going to be relevant to the 11:35changes you're making. And then use the 11:38agent to analyze the codebase and map 11:39out the components and dependencies. 11:42This shouldn't be a oneshot process. I 11:43like to probe say like what about the 11:45caching? How does this handle failures? 11:47And when it's analysis is wrong, I'll 11:48correct it. And if it's missing context, 11:50I provide it. Each iteration refineses 11:53its analysis. 11:55The output here is a single research 11:56document. Here's what exists. Here's 11:58what connects to what. And here's what 12:00your change will affect. Hours of 12:01exploration are compressed into minutes 12:03of reading. 12:05[snorts] I know Dex mentioned it this 12:07morning, but the human checkpoint here 12:08is critical. This is where you validate 12:10the analysis against reality. The 12:12highest leverage moment in the entire 12:14process. Catch errors here. Prevent 12:16disasters later. 12:19Onto phase two. Now that you have some 12:21valid research in hand, we create a 12:22detailed imple implementation plan. Real 12:24code structure, function signatures, 12:26type definitions, data flow. You want 12:28this to be so any developer can follow 12:30it. I I kind of liken it to paint by 12:32numbers. You should be able to hand it 12:33to your most junior engineer and say, 12:34"Go do this." And if they copy it line 12:37by line, it should just work. 12:40This step is where we make a lot of the 12:41important architectural decisions. You 12:43know, make sure complex logic is 12:45correct. Make sure business requirements 12:47are, you know, following good practice. 12:50Make sure there's good service 12:51boundaries, clean separation, and 12:52preventing any unnecessary coupling. We 12:54spot the problems before they happen 12:56because we've lived through them. AI 12:58doesn't have that option. It treats 12:59every pattern as a requirement. 13:02The real magic in this step is the 13:04review speed. We can validate this plan 13:06in minutes and know exactly what's going 13:08to be built. And in order to keep up 13:10with the speed at which we want to 13:12generate code, we need to be able to 13:13comprehend what we're doing just as 13:15fast. 13:17Lastly, we have implementation. And now 13:20that we have a clear plan and like 13:22backed by a clear research, this phase 13:24should be pretty simple. And that's the 13:27point. You know, when AI has a clear 13:29specification to follow, the context 13:31remains clean and focused. We've 13:33prevented the complexity spiral of long 13:34conversations. And instead of 50 13:36messages of evolutionary code, we have 13:38three focused outputs, each validated 13:40before proceeding. No abandoned 13:42approaches, no conflicting patterns, no 13:44wait actually moments that leave dead 13:46code everywhere. 13:48To me, what I see is the real payoff of 13:50this is that you can use a background 13:51agent to do a lot of this work because 13:53you've done all the thinking and hard 13:55work ahead of time. It can just start 13:57the implementation. You can go work on 13:59something else and come back to review 14:01and you can review this quickly because 14:03you're just verifying it's conforming to 14:04your plan, not trying to understand if 14:06anything got invented. 14:10The thing here is we're not using AI to 14:12think for us. We're using it to 14:13accelerate the mechanical parts while 14:15maintaining our ability to understand 14:16it. Research is faster, planning is more 14:19thorough, and the implementation is 14:20cleaner. The thinking, the synthesis, 14:23and the judgment though that remains 14:25with us. 14:29So remember that uh authorization 14:32refactor I said that AI couldn't handle. 14:34The thing is now we're actually, you 14:36know, working on it now starting to make 14:37some good progress on it. The thing is 14:40it's not because we found better 14:41prompts. We found we couldn't even jump 14:43into doing any sort of research, 14:45planning, implementation. We actually 14:46had to go make this change ourself by 14:48hand. No AI, just reading the code, 14:51understanding dependencies, and making 14:52changes to see what broke. That manual 14:55migration was, I'll be honest, it was a 14:57pain, but it was crucial. It revealed 14:59all the hidden constraints, which 15:01invariants had to hold true, and which 15:02services would break if the off changed. 15:05things no amount of code an analysis 15:07would have surfaced for us. And then we 15:09fed that pull request of the actual 15:12manual migration into our research 15:14process and had it use that as the seed 15:16for any sort of research going forward. 15:18The AI could then see what a clean 15:22migration looks like. The thing is each 15:24of these entities are slightly 15:26different. So we have to go and 15:27interrogate it and say hey what do we 15:28about do about this? Some things are 15:30encrypted some things are not. We had to 15:32provide that extra context each time uh 15:34through a bunch of iteration. 15:37Then and only then we could generate a 15:39plan that might work in one shot. And 15:42the key and might's the key word here is 15:44we're still validating, still adjusting, 15:46and still discovering edge cases. 15:52The three-phase approach is not magic. 15:54It only works because we did this one 15:56migration by hand. We had to earn the 15:58understanding before we can code into 15:59our process. I still think there's no 16:02silver bullet. I don't think there's 16:03better prompts, better models, or even 16:04writing better specs, just the work of 16:07understanding your system deeply enough 16:08that you can make changes to it safely. 16:14So why go through with all this? Like 16:16why not just iterate with AI until it 16:17works? Like eventually won't models get 16:19strong enough and it just works. The 16:21thing to me is it works isn't enough. 16:24There's a difference between code that 16:26passes test and code that survives in 16:27production. between systems that 16:30function today and systems that that can 16:32be changed by someone else in the 16:34future. The real problem here is a 16:37knowledge gap. When AI can generate 16:39thousands of lines of code in seconds, 16:41understanding it could take you hours, 16:43maybe days if it's complex. Who knows, 16:46maybe never if it's really that tangled. 16:50And here's something that I don't think 16:51many people are even talking about this 16:52point. Every time we skip thinking to 16:54keep up with generation speed, we're not 16:56just adding code that we don't 16:57understand. We're losing our ability to 16:59recognize problems. That instinct that 17:01says, "Hey, this is getting complex." It 17:04atrophies when you don't understand your 17:05own system. 17:07[snorts] 17:09Pattern recognition comes from 17:10experience. When I spot a dangerous 17:12architecture, it's because I'm the one 17:13up at 3:00 in the morning dealing with 17:15it. When I push for simpler solutions, 17:17it's because I've had to maintain the 17:19alternative from someone else. AI 17:22generates what you ask it for. It 17:23doesn't encode lessons from past 17:25failures. 17:27The three-phase approach bridges this 17:28gap. It compresses understanding into 17:30artifacts we can review at the speed of 17:32generation. Without it, we're just 17:34accumulating complexity faster than we 17:36can comprehend it. 17:40AI changes everything about how we write 17:42code. But honestly, I don't think it 17:44changes anything about why software 17:46itself fails. Every generation has faced 17:48their own software crisis. Dystra's 17:51generation faced it by creating the 17:52discipline of software engineering. And 17:54now we face ours with infinite code 17:56generation. 17:58I don't think the solution is another 17:59tool or methodology. It's remembering 18:01what we've always known. That software 18:03is a human endeavor. The hard part was 18:06never typing the code. It was knowing 18:07what to type in the first place. The 18:09developers who thrive won't just be the 18:11ones who generate the most code, but 18:13they'll be the ones who understand what 18:15they're building, who can still see the 18:16seams, who can recognize that they're 18:18solving the wrong problem. That's still 18:20us. That will only be us. 18:23I want to leave on a question and I 18:24don't think the question is whether or 18:25not we will use AI. That's a foregone 18:27conclusion. The ship has already sailed. 18:30To me, the question is going to be 18:31whether we will still understand our own 18:33systems when AI is writing most of our 18:34code. 18:37Thank you. [applause] 18:40[music] 18:50[music] 18:55>> [music]