In late 1999, I found myself inside Extreme Programming movement before most people had heard of it. Kent Beck's white "extreme programming explained" book had just come out. Ward Cunningham's wiki was where the real conversations happened. The Agile Manifesto wouldn't exist for another couple of years.
From the outside, what we were doing looked reckless.
We threw away long-range plans. We rejected heavyweight processes. We stopped pretending we could predict the shape of a system months in advance. We paired constantly, which looked inefficient. We wrote tests before code, which looked backward. We released continuously, which looked dangerous.
To many observers, this was a removal of constraints. The opposite was true.
XP compressed feedback loops until truth became unavoidable. Tests replaced promises. Continuous integration replaced status reports. Working software replaced narrative. You could no longer hide behind process because the system itself reported your progress, loudly and continuously.
The practices that looked like chaos were actually mechanisms for enforcing honesty. Pair programming meant every line of code had a witness. Test-first meant you couldn't ship wishes. Short iterations meant you couldn't hide. The discipline was more demanding than what came before, not less. It just didn't look like the discipline people were used to seeing.
That experience permanently changed how I think about software.
It also explains why I lost interest once Extreme Programming got absorbed into the broader "Agile" movement and solidified into branding and ceremony. When the name took over, the rigor drained out. The feedback softened. The theater returned. Consultants taught the artifacts without the discipline. I wrote about that years ago in The Curse of a Name.
I'm revisiting this history now because we're watching the same pattern repeat with generative AI, and it's being misunderstood in exactly the same way.
The Pattern
Certain shifts in software history feel like freedom because they remove familiar signals of control. In reality, they relocate rigor closer to where truth lives. They make it harder to fake progress.
This pattern has repeated at least three times in my career.
Dynamic languages displaced static type systems. When Ruby and Python started spreading into production systems, they were widely criticized as undisciplined. No compile-time guarantees. No rigid type constraints. Too easy to write sloppy code.
What actually happened was a shift in where rigor lived. Static promises gave way to runtime truth. Type declarations gave way to executable behavior. Compiler appeasement gave way to test-enforced correctness. In practice, the teams that succeeded doubled down on executable specifications: tests that described behavior precisely enough to function as a de facto type system.
The discipline didn't vanish. It moved into tests, contracts, and feedback loops that reflected how the system actually ran. The type system was still there; you just had to earn it through behavior rather than declaration.
(yes, I know and appreciate that there are some great and very popular languages that have started to displace the dynamic with amazing static type systems)
Extreme Programming displaced phase-gate development. XP removed plans, design documents, and phase gates. These were the artifacts that made organizations feel safe. In their place it installed mechanisms that were far less forgiving: test-first development, continuous integration, constant peer review, real customer feedback.
It looked chaotic because it removed the appearance of control. What replaced it was operational truth. You knew where you stood because the code told you, not because a project manager updated a Gantt chart.
Continuous deployment displaced release management. No release windows. No stabilization phases. No heroic integration efforts. Another apparent loss of discipline.
In reality, continuous deployment demands far stricter engineering than quarterly releases ever did. You need reversibility. Observability. Automated verification. Fast rollback paths. Continuous deployment isn't about speed; it's about never being surprised. You can't ship continuously without knowing exactly what your system is doing at all times. The rigor becomes continuous as well.
Why Regenerative Software Fits This Pattern
Generative AI appears to remove the ultimate constraint: hand-written code. That makes people nervous, and it should. But the danger isn't probabilistic generation. The danger is quiet failure.
Here's what I mean. When you generate code instead of writing it, you lose the incidental knowledge that comes from typing every character. You lose the friction that forces you to understand. You can produce systems that work without ever knowing why they work.
That's the legitimate fear, and it's a real failure mode. I've seen teams drowning in generated code they don't understand, systems that function but can't be debugged, abstractions that exist because an LLM suggested them rather than because they serve a purpose.
But the answer isn't to reject generation. The answer is to relocate the discipline.
Generative systems only work if invariants are explicit rather than implicit. Interfaces must be real contracts, not incidental boundaries. Evaluation must be ruthless. Failures must be loud and immediate. The engineer's job shifts from typing code to specifying intent and verifying outcomes.
What does this look like in practice? One possibility: You write the tests and the LLM generates implementations. If the tests don't pass, the code doesn't ship.
This is test-first development with a different author for the implementation. The discipline I learned in 1999 turns out to be exactly the discipline that makes AI-assisted development work. The rigor relocated from who writes the code to what the code must satisfy. The tests don't care whether a human or a machine produced the implementation. They care whether it behaves correctly.
The pattern is: probabilistic inside, deterministic at the edges.
This is harder than it sounds. Specifying intent precisely enough that a machine can generate correct implementations is not easier than writing code. It's a different skill, and in some ways a more demanding one. You have to know what you actually want. You have to be able to recognize when you've gotten it. You can't hide behind activity.
Cheap generation without strict judgment isn't a new paradigm. It's abdication.
I've been experimenting with frameworks that treat evaluation as a first-class system component, not an afterthought. Generation can be flexible. It can even be probabilistic. But evaluation must be rigid. Systems must fail visibly when they drift from intent. The comfort of working code that you don't understand is precisely the comfort you have to refuse.
What This Means for Practice
If you're working with generative AI now, the question to ask yourself is: where did the rigor go?
If you removed hand-written code but didn't add explicit invariants, you lost rigor. If you're generating implementations without rigorous evaluation, you lost rigor. If you're accepting code because it runs rather than because you understand it, you lost rigor.
The engineers who thrive in this environment will be the ones who relocate discipline rather than abandon it. They'll treat generation as a capability that demands more precision in specification, not less. They'll build evaluation systems that are harder to fool than the ones they replaced. They'll refuse the temptation to mistake velocity for progress.
The Throughline
Across decades of software evolution, the same misunderstanding keeps recurring. Constraint removal is mistaken for loss of rigor.
But what actually happens, when things go well, is rigor relocation.
Control doesn't disappear. It moves closer to reality.
XP taught me this. Dynamic languages reinforced it. Continuous deployment reinforced it again. Now generative systems are teaching it to a new generation of engineers, whether they realize it or not.
The lesson is always the same. When something looks like recklessness, look for where the discipline moved. If you can't find it, that's when you should worry. If you can find it, you're probably looking at the future.
If generation gets easier, judgment must get stricter. Otherwise, you're not engineering anymore.
That was the real lesson of Extreme Programming before it got diluted into a brand. It's the same lesson now. And this time, the velocity of change means we don't have years to figure it out.