SDLC after AI

Requirements, design, code, test, and review are still the lifecycle. After AI, each handoff has to be something a machine can reject. Deploy stays with a person.

A tech lead asked me if we could skip the spec. The model would write the code. I had a diff open that compiled. It also invented a field nobody had asked for. That's the whole argument, honestly. People keep saying AI collapsed the software lifecycle. One prompt. One diff. Ship it. I don't buy it. The stages are still there. What died is the slack between them. A paragraph plus a hallway conversation used to move the work. A model does not raise its hand in standup. It fills the gap. Vague gap, confident wrong fill. The names were never the bottleneck I've shipped through the same seven words for a long time. Requirements. Design. Code. Test. Review. Deploy. Operate. Hospital scheduling. Lending. Document pipelines. A high-traffic replatform. The calendar changed. The nouns did not. What actually carried the work was slack. A ticket could say "add export" because someone would catch the missing format in refinement. A design could live in a slide because the tech lead was in the room on Thursday. Tests slipped to the next sprint because a human still remembered the risky branch. Review was "LGTM" because the author and the reviewer shared a hallway. That slack was not free. It was prepaid attention. Standups, design reviews, and the person who had been on the codebase for three years. AI spent that attention in a week. The model has no hallway. It has the artifact you handed it, and then it has a guess. So the lifecycle did not get shorter. The artifacts got stricter. Strict is the only kind of context a new session can use. What "collapsed" usually means When a team tells me the SDLC collapsed, I ask for the last change that skipped a stage on purpose. What I get is a demo. A prompt produced a file. The file compiled. Someone merged it. The stages were still there. They were just invisible, and nobody kept the receipt. Fine for a spike. A bad way to run a Tuesday. The failure is specific, and I've watched it in the same order. Requirements become a chat log. Design becomes whatever structure the model picked on the first try. Test becomes "the model said it ran them." Review becomes the same model scoring its own diff. Deploy becomes a button the loop is allowed to press. None of that is a faster lifecycle. It's the old one with the checker removed. An unverified loop is a tap, not engineering. Same rule, one floor up. A lifecycle with no handoff you can reject is a status meeting that happens to emit code. Each arrow has to be a receipt Here's the change I actually made. Stage by stage. Not a new religion. A stricter artifact. Stage What used to be enough What has to exist now Requirements A paragraph and a refinement conversation A feature brief: numbered Given/When/Then criteria, scope, and an owner Design A diagram someone can narrate A spec that traces every criterion to a planned test. If one id is missing, the spec is not done. Code A developer, a branch, and taste File tools inside a path jail. One change. The role that writes does not grade itself. Test A phase after the code, if the sprint allowed it A deterministic gate on an allowlisted test command. Red means go back. The model does not get to declare green. Review A comment thread and an LGTM A verdict over the git diff. Approve is legal only when the blocking count is zero. Deploy A human, a pipeline, a rollback note Still a human. The loop I run commits locally and never pushes. Operate On-call, a runbook, a person who can roll back Still a person. A recurring failure becomes a case in the harness, not a longer prompt. flowchart LR subgraph talk [Before: a conversation could carry the gap] direction LR R1[Requirements] --> D1[Design] --> C1[Code] --> T1[Test] --> V1[Review] end subgraph receipt [After: the arrow is an artifact] direction LR R2[Feature brief] --> D2[Spec covers every criterion] --> C2[Change in a jail] --> T2[Build result] --> V2[Review verdict] end Read the bottom row left to right. Those are not new phase names. They're the proof the old phase happened. Test is the one people shrug at. It stopped being the phase after coding. It became the gate inside coding. In the lab, the implementation loop is the developer, then the project's real test command, then back to the developer if the build is red, until a cap. Hitting the cap does not mean "try a friendlier prompt." The run escalates. Done. Review stayed a different role on purpose. The person (or the agent) who wrote the diff does not get to approve it. That rule is older than language models. AI just made the conflict of interest cheap, so it has to be a type, not a hallway norm. The new work is the harness If the stages stayed, what's actually new? The stuff around them. I drew it as six duties, not an eighth phase: contract, context, tools, loop limits, evidence, and evaluation. Contract is the brief. Context is the map ( AGENTS.md , the decisions, a start you can reproduce). Tools are git, the terminal, and tests, with the smallest permission that still works, and an allowlist. Limits are time, cost, and retries, plus an exit. Evidence is the diff, the build, and a review matched to the risk. Evaluation is a handful of representative tasks you re-run when the model, the rules, or the tools change. flowchart TB H[Harness: contract, context, tools, caps, evidence, evals] H --> Req[Requirements] Req --> Des[Design] Des --> Code[Code] Code --> Test[Test] Test --> Rev[Review] Rev --> Human[Human owns deploy and operate] The six duties sit above the stages. They are not an eighth phase. The harness is not a brand you adopt. It's the prepaid attention, moved out of people's heads and into the repo. A new model session can read a file. It cannot attend last quarter's design review. And that's why "just use a better model" keeps disappointing teams who skipped this. A stronger model fills a vague ticket more fluently. Fluency is not a receipt. What I refuse to let the loop own I run this as a lab, not a slogan. Six roles. Team and stack in YAML. A real local git repo. A real build. Java does not name Gradle. The project YAML does. The developer writes files. A build gate runs an allowlisted test command. Git commits on the branch. Git never pushes. The outer stop is a stakeholder decision. That role can be a human. The constraint I care about is the same either way: the cycle ends on an approval, on an explicit rejection, or when the cap is hit. It does not end because a prompt felt finished. Deploy and operate sit past that line on purpose. I'll let a loop retry a red test. I will not let it decide a production cutover, an auth boundary, or a rollback at 2 a.m. Those are judgment. A machine can reject "the allowlisted command exited 0." It cannot reject "this is the right consistency boundary" with the same honesty. Start there if you're tired of framework arguments. One repo. One task. One CI command. Grow the loop only after that command has failed a bad change where other people can see it. When the old slack was the right tool I'm not arguing that every hour needs a typed verdict. Okay, I almost did, and that's too neat. A one-off spike can live in a chat. Building the harness for a task you'll do once costs more than doing the task. I've watched the policy file outlive the spike. That's the signal to stop. A phase with no check you can say in one sentence should stay a conversation. "Looks good" is not a check. "Every acceptance criterion id appears once in the spec" is a check. "The allowlisted test command exited 0" is a check. "We should split this module" is a conversation. Keep it a conversation. And a high-blast-radius change still wants a human who can say no after the receipts are green. Green tests are necessary. They are not a release decision. The teams that get hurt delete the conversation and forget the receipt. The teams that stall keep both: a heavy ceremony, and a model writing ceremony-shaped prose. Pick one carrier per arrow. A conversation, or a receipt....