The Work Shifted Left, Not Away: Building an Event-Driven Order Saga with SCRUB — Solo
Or: why one architect could stand up five services and a compensating saga, and why that is emphatically not the headcount story you think it is
TL;DR — the code lives at https://github.com/architect4hire/orderflow2026. It's an event-driven order-fulfillment system I built for a fictional client, using the SCRUB framework end to end: an Angular front end, an order saga, four reacting services, and a compensation path on every failure. This post is less about the topology and more about what the build taught me — including the part where I keep reading that AI is going to thin out engineering teams, and the part where, having just delivered a system solo that used to need a squad, I'm here to tell you the developer never left the room.
Let me start with the fact that's going to be misread, so I can un-misread it before we go anywhere.
I built OrderFlow alone. One architect, no development squad. Five .NET services — an order saga plus four reactors — talking over a service bus, backed by four different data stores, fronted by an Angular operations view, with a compensating transaction on every failure path in the system. The kind of build that a few years ago would have been a team and a quarter. Solo, in about six weeks, most of it not spent typing.
And I know exactly how that reads on a nervous Monday: see, one senior person plus AI replaced the team. So let me be the guy who actually did it and tell you that's the wrong lesson.
Done right, AI-assisted development does not shrink your headcount. It relocates your work — out of the muddy middle where we used to hand-crank implementation, and to the two ends where it was always hardest.
One architect could stand up this system not because the AI did the engineering, but because the engineering moved: forward into specifying it precisely, and backward into verifying it ruthlessly. Both are still done by a human, and both are more senior, not less. The keystrokes went away. The judgment did not. That's the whole post — everything below is me showing my work.
Why OrderFlow Exists, and What It Actually Is
OrderFlow is a proof-of-concept I stand up in the open, for a client who doesn't exist, so that "I do disciplined AI-assisted development" stops being a slide and starts being a repository you can read. This one models order fulfillment — the genuinely hard kind, where money moves and inventory is finite.
Here's the landscape. An Angular 20 app with two views — customer order-status and an operations view — calls an Order service over REST. From there it's events all the way down: the Order service runs a saga that sends commands over a service bus, and four services react and reply — Inventory (reserve stock), Payment (charge), Fulfillment (dispatch), and Notification (tell the customer). Cosmos holds the event log, Redis the status read model the UI reads from, SQL the inventory and payment records, Blob the rest — all on Aspire-local emulators against the real Azure SDKs, so it boots on a laptop with no cloud spend.
Now — regular readers know my default template is the opposite of this. On PlannerPro I argued hard for one API, one database, and no microservices, because right-sizing usually means resisting the urge to distribute. So why the saga-and-reactors sprawl here?
Because right-sizing is bidirectional, and this domain sizes up. An order that reserves stock, charges a card, and dispatches a package is a multi-step transaction across systems that fail independently, on a bus that delivers at least once. You cannot right-size that into a clever monolith and a database transaction — the distribution isn't resume-driven; it's the shape the problem actually is. This deliberately departs from my house template, and inside the build, I worked to a fictional client's code conventions rather than my own standards (which matters later). The template is a starting bias, not a religion. When the domain moves money, you follow the domain.
SCRUB, For Those Just Joining Us
If you've read the PlannerPro post, skip ahead. Everyone else, ninety seconds.
Generative AI has a failure mode I call the Plausibility Trap: the output isn't wrong in a way that crashes. It's wrong in a way that compiles, passes its tests, looks idiomatic, and quietly encodes a domain decision you never made. The model knows how to write code. It does not know your business. Leave a gap in the prompt and it fills that gap with the most statistically average answer in its training data — which is, occasionally and silently, the wrong answer for you.
SCRUB is the checklist that closes those gaps before you submit. Five elements, every prompt: Scope (exactly what you're asking for), Constraints (your framework, your architecture, your naming), Restrictions (what you explicitly do not want, and why — the element everyone skips and the one that matters most), Usage (who calls this, in what world), and Behavior (what must not change). Hold onto Restrictions. On a system like this, it's the whole ballgame.
Four Places Where the Plausible Answer Is the Wrong Answer
Here's the part of OrderFlow that made me want to write this at all. A CRUD app has maybe one spot where the training-data idiom and your business reality point in opposite directions. An order saga has four — and every one is the difference between a demo that dazzles and a production system that double-charges a customer. Each is a Restriction I had to write by hand, because I understood the domain and the model could not.
Compensation on every failure path. (CRITICAL.) Ask an AI to build a saga step and it will hand you a beautiful happy path — reserve, charge, dispatch, done. What it won't do, unprompted, is compensate when step three fails after step two succeeded: a multi-step order failing mid-flow, leaving stock reserved or a charge captured with nothing shipped. It compiles, it demos, it passes the happy-path test — and it quietly rots your inventory and your customer trust in production. The restriction is CRITICAL: every failure path compensates — a declined payment releases the reservation, a failed dispatch refunds and releases, no order is ever left partial. That sentence isn't documentation. It's the product.
Idempotent payment callbacks. (CRITICAL.) At-least-once delivery means the payment callback will arrive twice — not sometimes, will. The naive handler the model writes processes both. Two identical callbacks, two charges. The restriction: a duplicate payment callback is a no-op; poison messages dead-letter, they do not loop. Idempotency is not a nice-to-have here; it's the line between "correct" and "class-action."
Concurrency-correct inventory reservation. (IMPORTANT.) Two buyers race for the last unit. The obvious reservation code — read stock, check it's positive, decrement — lets both of them win, because the check and the decrement aren't atomic. The AI writes the obvious code; the obvious code oversells. The restriction pins the reservation to be concurrency-correct so oversell incidents go to zero, and the status read model converges in a second so the UI doesn't lie about it.
A durable baseline. (IMPORTANT.) Without ADRs, C4 diagrams, and a committed design doc, the architecture and the code drift apart the moment the build ships — and six months on, nobody remembers why the payment handler is idempotent, so someone "simplifies" it. The restriction is the baseline itself: design doc, ADRs, and the SCRUB prompt library, committed as a durable source of truth.
Sit with those four, because they're the argument. Not one is a thing the AI can decide for you; every one is a senior human saying the plausible version is wrong here, and here's why. Take the human out of that loop and the AI ships four confident, compiling, demo-ready bugs that each cost real money. The developer isn't the bottleneck in that picture. The developer is the immune system.
Lessons From the Trenches
With the stakes clear, here's what the build actually cost me — in the order I hit it.
The first decision isn't code — it's documentation, and it's a coin-flip you can't afford to fumble. You can architect and scaffold the whole thing from a committed SCRUB prompt library — prompts as plan, spec, and record — or you can build it by hand and document it after. For the initial stand-up, those two roads cost about the same hours. All roads lead to Rome. But they diverge violently by next Tuesday: hand-built-then-documented gives you a system and a description that start drifting apart the moment you close the editor, while the prompt-library road gives you an executable, versioned, reviewable spec you can reuse on the next engagement. My recommendation, and what I did: build a default scaffolding prompt in your library and start from it every time. The juice is genuinely worth the squeeze — but I won't pretend the first pressing is free. It pays out on the second glass.
My toolchain moved. Since the GitHub Copilot price change I've shifted essentially all of this to Claude Code, with a cleaner division of labor: agentic Edit mode almost exclusively for the initial stand-up and the plan — the big multi-file scaffolding push where an autonomous build-and-verify loop earns its keep — and Chat mode for surgical edits, the one-file "change this and nothing else" work where I approve every character. The mode mapping evolved since PlannerPro, and that's the point: SCRUB is what survives a pricing email that reshuffles your whole toolchain overnight. The discipline is portable even when the buttons aren't.
Your prompt library is code, and code has bugs. I write a big slice of the library up front, and on OrderFlow that bit me exactly the way real code bites you. I'd named a project OrderFlow.Order.Api, and the singular Order collided with types and folders all over a system that already has an Orders saga. The fix wasn't in the generated C#. It was in the prompt: rename to OrderFlow.Orders.Api, regenerate, move on. Read that again — I debugged the specification, not the output. That is shift-left in one rename. The bug existed before the code did, which is exactly where I caught and killed it.
The AI finds the holes you left — and when it's right, you feed the fix back into the prompts. Throughout the build, Claude surfaced gaps in my prompts: places I'd underspecified, ambiguities it flagged, fixes it proposed. Often it was right. But here's the discipline that separates a tool that helps from a tool that quietly rewrites your intentions — when its fix is correct, you don't just accept the diff. You update the prompt so the library reflects the system you actually built. Otherwise, the record lies to you in six months. Keeping the spec true is your job. Still yours.
And sometimes you fix it by hand, on purpose. My favorite small one: Claude cheerfully generated multiple classes in a single file. Compiles, runs, ships — and violates a standard I've held for years, one class per file, and it grated every time I opened the offender. Fork in the road: fix the prompt so the generator never does it again, or fix the files by hand this once? For a PoC that isn't going to grow, I did it manually — pure annoyance-driven housekeeping, zero functional change, in service of a standard I refuse to drop. On a production system with a long road ahead, I'd have fixed the generator, because there you pay once and it never reoffends. Both calls are legitimate. The point is that a human weighed lifespan against effort, because no prompt on earth was going to make that judgment for me.
Every one of those is a developer doing developer work — naming things right, catching a collision, keeping the spec honest, enforcing a standard the machine doesn't care about. The tool didn't delete any of it. It relocated it to the two ends that were always the hard part: saying precisely what you want, and verifying precisely what you got.
So, About That Squad
Back to the fact I opened with. Yes, I delivered this solo. No, that is not the layoff memo.
Here's the honest reconciliation. One architect could ship this saga because the irreducibly human parts — the four restrictions above, the event contracts, the compensation semantics, the failure-injection validation — got done by a senior person, and the mechanical middle got generated against them. That doesn't scale to "fire the team." It scales the opposite way: the harder and more valuable the system, the more of that senior specification-and-verification work there is, because the Plausibility Trap grows with complexity, not with team size. A CRUD form has one silent-wrong bug waiting for you. A saga that moves money has four before breakfast, and everyone needs someone who understands the domain to pin it down.
The work shifted left, not away. On a reference build, you see that as "one architect, no squad." On a real product with a real backlog, you see it as a team whose job stopped being typing the implementation and became authoring the restrictions and verifying an ever-growing surface — a job that gets bigger as the system does. If your headcount drops after adopting this, you didn't automate the work. You stopped doing part of it, and OrderFlow's four restrictions are the part you stopped doing. Production will send you the invoice.
The Practical Version
Running OrderFlow's playbook on your own next build:
Decide the documentation fork first, out loud. SCRUB-library-first or hand-built-then-documented — pick before you generate. Equal effort on day one, wildly unequal on day thirty. Build a default scaffolding prompt and start every project from it.
Right-size in both directions. Sometimes right-sizing means one API; sometimes it means a saga and four reactors. Let the domain decide, and bake "match this architecture" into Constraints on every prompt.
Find all your no-project-filters. Every domain has the spots where the training-data idiom and your reality diverge. A saga has several: compensation on every failure path, idempotent callbacks, concurrency-correct reservation. Find them before the AI does and pin each with a tiered Restriction that carries its reason.
Treat the prompt library as code. It has bugs. You'll rename an Order to Orders to kill a collision. Fix the spec, not just the output — that is the debugging now.
Feed the AI's good catches back into the prompts. When the model finds a real hole, update the library so it matches what you shipped. A drifted record is worse than none.
Know when to fix by hand vs. fix the generator. Manual for one-off annoyances on a short-lived PoC. Generator for anything that will live and reoffend. A human weighs lifespan against effort — that call isn't going anywhere.
The repo is at github.com/architect4hire/orderflow2026. Read the prompt library alongside the code and watch how much of the real work happened before any C# existed and after it was generated — at the two ends, where the developer still very much lives.
Because that's the headline I'd tattoo on the industry if I could: nobody got removed from this build. The work moved to where it was always hardest. If you were good at the hard part, you're about to be worth more, not less.
Robert Felkins is the principal architect at Architect4Hire and the author of the upcoming book Practical AI Assisted Development, which covers the full SCRUB system — tiered restrictions, layered generation, Edit Mode patterns, prompt chaining, and applying the framework to codebases you inherited rather than started.
And if your team is somewhere in the middle of this transition — a distributed system that has to be correct under failure, tooling that changed under you, or a nagging worry about what all this means for the people on your team — that's the work I do. I take fractional architecture engagements: a few days a month of senior architectural judgment without the full-time price tag, covering .NET and Azure platform work, modernization, team enablement, and AI-assisted development that survives contact with production. Reach out at robert@architect4hire.com.





