Where Business Rules Belong in the AI Era
AI coding makes tech debt cheap to create and hard to see. The fix is to separate what matters: keep business rules in a deterministic Nodora rule engine, and let Claude Code scaffold the code around them.
Building with an AI coding agent is fast in a way that's genuinely new. You describe a feature and a working version of it appears: routes, handlers, types, validation, the wiring between them. Most of that is boilerplate, and boilerplate is exactly what these tools are best at. They have read more Express handlers and React forms than any human ever will.
But there's a failure mode that sometimes shows up. You ask the agent to change something that sounds small, "make pro users get the discount too," and it produces a diff that touches nine files. Some of those edits are right. One of them subtly isn't. The discount now applies in checkout but not in the renewal job, because the agent found two of the three places that price gets computed and silently missed the third. Nothing crashes. The tests, which the agent also wrote, still pass. You find out from a support ticket.
The AI is perfectly good at coding. The problem is location: your most important logic is living in the part of the stack the AI rewrites most freely.
Two kinds of code
It helps to split what you're building into two piles.
The first pile is scaffolding: the route that parses a request, the handler that calls a service, the form, the serializer, the glue. This code is mechanical. There are a hundred correct ways to write it and they're all roughly equivalent. If the agent regenerates it slightly differently next week, you don't care, as long as it works. This pile is most of your codebase by line count, and handing it to an AI is a clear win.
The second pile is policy: what an action costs, who is allowed to do it, when the discount applies, which plan unlocks which feature. This is small by line count but it's the part that actually encodes your business. It's edited in response to a sales call or a pricing experiment rather than a refactor, and it has to be the same answer everywhere it's consulted.
These two piles want opposite things from an AI agent. The scaffolding wants the agent to have a free hand. The policy wants the agent kept on a very short leash. The mistake is letting them sit in the same files, so the agent treats them the same way, and your refund policy gets rewritten with the same casual confidence as a logging statement.
Why "in code" is the wrong place for policy
Two arguments, and they compound.
The blast radius. When a business rule is embedded in application code, changing it is a search problem. The agent has to find every place the rule leaks to, understand how those places interact, edit all of them consistently, and not disturb the logic next door. Your review then has to verify all of that across a multi-file diff. That's a large, distributed surface, and distributed surfaces are exactly where AI-generated bugs hide, not in the file you're looking at, but in the one you assumed was fine.
The non-determinism. An LLM is a stochastic function. The code it emits is a sample from a distribution, not a guarantee. For the scaffolding pile that's "fine"; a route handler that came out a little different but works is no problem. But your business invariants cannot be sampled. "Refunds are allowed within 30 days" is not a thing you want re-derived, slightly, every time an agent passes through that file during an unrelated refactor. Every regeneration of code that contains policy is a fresh chance to quietly alter the policy.
Put those together and the conclusion is uncomfortable: the more productive your AI agent is, the more often it touches your code, and the more exposure your business rules have to drift. Speed makes the problem worse, not better.
The fix: move policy to a deterministic engine
A rule engine is, in a precise sense, the opposite of an LLM. Where the model is stochastic, stateful in context, and free-form, a Nodora rule is a pure function: same input, same output, every time, no hidden state, no I/O, checked at compile time. When you move a decision out of application code and into a ruleset, you are physically relocating it from the stochastic part of your stack to the deterministic part.
Here's a discount policy as a rule instead of as if statements sprinkled through a pricing service:
rule Checkout {
// who qualifies for a discount, each reason on its own line
has_coupon = input.coupon == "SAVE25"
is_pro = input.plan == "pro"
loyal = fallback(input.months_active, 0) >= 12
out discount_pct = match true {
_ when has_coupon => 25,
_ when is_pro => 20,
_ when loyal => 10,
_ => 0,
}
out final_price = input.subtotal - (input.subtotal * discount_pct / 100)
}Now go back to that "make pro users get the discount too" request. The change is one line: a new arm in the match. The diff is one declarative block you can read on one screen. And critically, none of the scaffolding moved, because the price is computed in exactly one place now, the rule, and checkout, renewals, and the analytics pipeline all ask that one place. There is no third spot for the agent to miss, because there is only one spot.
Your review collapses from "did the agent correctly modify a distributed system" to "is this rule correct." That second question is one a human can actually answer with confidence.
How this looks as a Claude Code workflow
The strategy is only practical if the agent can author rules as fluently as it writes TypeScript. That's what the Nodora skill is for. Its SKILL.md teaches Claude Code the whole language in one document: the syntax, the type rules, the compile and eval CLI, the constraints, and the common errors with their fixes. With the skill installed, "put the discount logic in a Nodora rule" is an instruction the agent can carry out and verify on its own, by compiling the ruleset and running nodora eval against sample inputs, rather than guessing at syntax it half-remembers.
A workflow that holds up over a long project looks like this.
1. Make the convention explicit. The most useful single thing you can do is write the rule down where the agent reads it every session. Run /init to create a CLAUDE.md, and put the policy split in it in plain language:
## Where business logic goes
Decisions that are *policy* (pricing, limits, eligibility, feature
access, anything that changes for business reasons rather than
technical ones) live in Nodora rulesets, not in application code.
Use the `nodora` skill to author them. Application code reads the
rule's output; it does not re-implement the decision.Now every time the agent starts a feature, it has already been told which pile each piece of logic belongs in. You're shaping the default, not policing every diff after the fact.
2. Let the agent build the scaffolding around the rule. This is the part it's good at. Give it the rule's shape, inputs in, outputs out, and let it generate the route, the service call, the types, etc. Where the ruleset lives is up to you: a .ruleset file checked into the repo and evaluated locally with @nodora/js, or hosted on the platform and fetched with @nodora/client. Either way the agent builds against the same seam, an evaluate call:
const { outputs } = await checkout.evaluate("Checkout", {
subtotal: cart.subtotal,
plan: user.plan,
coupon,
months_active: user.monthsActive,
});
// the agent writes all the code around this line;
// the line itself is the boundary it doesn't cross
return Response.json({ price: outputs.final_price });The boundary is doing real work here. On one side, code the agent owns and you barely review. On the other, a rule the agent proposes but you actually read.
3. Review the rule, not the refactor. When policy changes, the diff that matters is the ruleset diff, and it's small enough that you can give it real scrutiny instead of skimming. You spend your limited review attention on the 8 lines that encode a business decision, and let the agent's boilerplate go by with a glance. That's the right allocation. It was never sustainable to deeply review every file an AI touches; this makes it unnecessary.
4. (Optional) Move the ruleset to the platform. Keeping rulesets in the repo is perfectly fine, and for plenty of projects it's all you ever need: the agent edits a .ruleset file, you review the diff, it ships with the next deploy. The optional upgrade is hosting the ruleset on the Nodora platform, which starts to pay off as the project grows. At that point editing a rule stops touching your application at all. Bumping the loyalty threshold becomes a one-line change that propagates in seconds: no redeploy, no agent involved, no application diff to audit.
Why the whole system gets more stable
The stability here doesn't come from the AI making fewer mistakes. It comes from where the mistakes can land. A bug in the scaffolding is usually loud and local: something breaks, a test fails, you notice. A bug in policy is quiet and expensive, the kind that quietly bills the wrong price for a week before anyone catches it. Moving policy into a rule puts your highest-stakes logic somewhere a single reviewer can hold the whole thing in their head, while the AI agent keeps its speed on everything around it.
That's the trade worth making while building this way. The agent writes the boilerplate, which is most of the app and mostly interchangeable. You keep the decisions that are actually yours in rules you can still read in one sitting.