Beyond the Prompt: Architect Your Code to Embed Business Logic for AI
Most AI coding failures do not begin with a weak prompt. They begin with an application that has hidden rules, ambiguous ownership, and business decisions scattered across controllers, database queries, UI conditions, and someone’s memory.
A language model can generate code, classify requests, summarize documents, or operate tools. But it cannot reliably act on business logic that has never been made explicit. If an agent needs to decide whether to issue a refund, route a support case, approve an expense, or update a customer record, it needs more than a prompt describing the company’s intentions. It needs software boundaries that make those intentions executable, testable, and observable.
The practical shift is simple: stop treating prompts as the primary location for business logic. Treat them as an interface to a system whose rules live in code, data, and controlled workflows.
Prompts are not a durable rules engine
Prompts are useful for expressing goals, context, tone, and task-specific instructions. They are not ideal for enforcing rules that must be precise and repeatable.
Consider an AI support agent asked to process cancellation requests. A prompt may say that customers can receive a refund within 30 days, unless they have consumed a premium service or their account is under review. That sounds clear until the agent must answer practical questions: Which timestamp defines purchase date? What counts as consumption? Who places an account under review? Can a manager override the result?
Those are policy questions, but they should not be resolved afresh in model output. They belong in a domain layer with named concepts, structured inputs, and deterministic outcomes.
type RefundDecision =
| { eligible: true; reason: "within_window" }
| { eligible: false; reason: "outside_window" | "service_consumed" | "account_review" };
function assessRefund(order: Order, account: Account, now: Date): RefundDecision {
if (account.isUnderReview) {
return { eligible: false, reason: "account_review" };
}
if (order.premiumServiceConsumed) {
return { eligible: false, reason: "service_consumed" };
}
const ageInDays = (now.getTime() - order.paidAt.getTime()) / 86_400_000;
return ageInDays <= 30
? { eligible: true, reason: "within_window" }
: { eligible: false, reason: "outside_window" };
}
The model can collect missing information, explain the decision in plain language, and initiate the approved workflow. The application should determine eligibility.
Design AI as a client of your domain
A useful architectural rule is to give an AI agent the same respect and constraints as any other external client. It should not reach directly into production tables, infer permissions from prose, or compose arbitrary write operations.
Instead, expose carefully designed capabilities. A capability should represent a business action rather than a low-level database operation. Prefer createRefundRequest over updateOrderStatus. Prefer assignCaseToQueue over setOwnerId.
This distinction matters because a business action can validate authorization, enforce state transitions, create an audit record, and return a meaningful result. A raw update cannot express those safeguards on its own.
Make tool contracts narrow and explicit
Every tool available to an agent is an operational promise. Keep its input schema small, validate it on the server, and return structured outcomes that the model can use without guessing.
{
"caseId": "CS-1042",
"targetQueue": "billing",
"reason": "refund_question"
}
A tool should reject an invalid queue, a case the caller cannot access, or a transition that violates workflow rules. The error should be actionable, such as “case is already closed,” rather than a vague server failure. That lets the agent recover by explaining the limitation or asking a human to intervene.
Do not rely on a prompt instruction such as “only call this tool for authorized users.” Enforce authorization inside the tool. Prompt guidance shapes behavior; application controls establish guarantees.
Turn tribal knowledge into executable policy
AI initiatives often expose a useful discomfort: teams know their business logic is real, but cannot point to where it lives. A rule may be split between a sales playbook, a support macro, a spreadsheet, and a conditional buried in an old service.
Before connecting an agent to consequential actions, identify the decisions it will influence. For each one, define:
- the inputs required to make the decision;
- the authoritative source for each input;
- the allowed outcomes and state transitions;
- the person or system accountable for exceptions;
- the audit data required to explain what happened later.
This work is not bureaucracy around AI. It is the engineering needed to make automation trustworthy. If a policy cannot be represented clearly enough to test, it is too vague to delegate safely to a model.
Separate judgment from execution
Models are valuable when language, ambiguity, and incomplete information are central to the task. They can extract intent from an email, summarize a long case history, detect that a request needs escalation, or draft a response tailored to a customer’s question.
Those strengths do not require giving the model final control over irreversible actions. A robust workflow separates interpretation from execution:
- The model interprets the request and proposes a structured action.
- The application validates the action against business rules and permissions.
- The workflow either executes, requests approval, or returns a clear constraint.
- The model communicates the verified result to the user.
For example, an agent may recognize that an invoice appears duplicated. It can gather invoice identifiers and prepare a recommendation. A billing service should decide whether the duplicate condition is met and whether a credit can be issued. For high-impact cases, the service can create an approval task instead of applying the credit automatically.
This pattern also improves user experience. Rather than inventing certainty, the agent can say what it found, what rule applied, and what will happen next.
Build for retries, failure, and review
Real systems are not single-turn demonstrations. Networks fail, users change their minds, tools time out, and a model may repeat a request after receiving an unclear response. Design business operations to tolerate this reality.
Use idempotency for write actions so a retried request does not create two refunds or two support tickets. Record the request identity, actor, inputs, validation result, and resulting state. Make long-running work asynchronous when necessary, returning a status the agent can check rather than pretending completion is immediate.
Human review should be a first-class outcome, not an embarrassing fallback. Good escalation includes the relevant context, the proposed action, the rule that blocked automation, and the exact decision a reviewer must make. That reduces handoff friction while preserving accountability.
Test the system, not just the prompt
Prompt tests still matter, especially for intent extraction and response quality. But the higher-value tests verify the domain behavior around the model. Test invalid tool arguments, unauthorized requests, stale records, duplicate submissions, partial failures, and conflicting policy states.
Keep model output constrained where possible. Ask for structured fields, validate them, and treat malformed output as a recoverable input error. A model can be probabilistic; the surrounding system should not be careless.
The enduring lesson is that AI becomes more useful as your software becomes more explicit about what it believes, permits, and records. Prompts can make an agent sound capable. Well-architected business logic makes it dependable. When the model is connected to clear domain capabilities instead of vague instructions and broad access, you gain something more valuable than an impressive demo: automation that deserves a place in real work.