Who Decides the Next Step?
In traditional programming, we write the steps a system follows.
When a customer submits a refund request, the code might check the order, confirm the purchase date, apply a refund policy, and send the result. Every branch has to be designed in advance.
In agentic programming, we give a large language model, or LLM, a goal, a set of tools, and boundaries. The model decides which tool to use, reads the result, and decides what to do next.
The difference is not simply that one uses AI. It is where the decisions about the next step happen.
Traditional programming puts those decisions in code. Agentic programming lets an LLM make some of those decisions at runtime.
Traditional Programming: We Define the Route
Think of a traditional application as a train running on tracks. It can take different routes, but someone has to build the tracks and switches first.
A refund workflow might look like this:
Receive refund request
↓
Find the order
↓
Is the order within the refund period?
├── Yes → Process the refund
└── No → Reject the requestWe define the conditions and the actions. The system follows them.
This is what we generally mean by deterministic behaviour: given the same inputs and system state, the same rules produce the same result.
That predictability is useful. When calculating tax, checking account permissions, or recording a payment, we want explicit rules. These are not places where a model should improvise.
The Limitation: Someone Must Anticipate the Branches
Real requests are not always tidy.
A customer might write:
“Two items were ordered. One arrived broken, and the other has not arrived. Can you help?”
A traditional system can handle this, but only if we build the relevant paths:
- Identify the two items.
- Separate the damaged-item issue from the delivery issue.
- Check the return policy.
- Check the shipment status.
- Decide whether to refund, replace, wait, or escalate.
There is nothing inherently wrong with this approach. It is how we build reliable software.
The challenge is that every new variation can require more rules, more interfaces, and more code. A situation we did not anticipate might end up in a fallback path or require human support.
Agentic Programming: We Build the Capabilities
With an agentic architecture, we still write code. But instead of defining every possible sequence, we define capabilities the model can use.
For the same customer support system, those capabilities might be:
- Find an order.
- Read the return policy.
- Check a shipment.
- Check replacement stock.
- Request a refund.
- Create a support ticket.
These capabilities are exposed as tools.
A tool is usually an ordinary function or API with a defined purpose, accepted inputs, and a structured result. The LLM does not need direct access to the whole database or application. It needs a controlled way to ask for specific information or actions.
We then attach an LLM as the decision-making component, or the “brain”.
The architecture becomes:
User request
↓
LLM chooses a next step
↓
Application executes an allowed tool
↓
Tool returns a result
↓
LLM evaluates the result
├── Use another tool
├── Ask the user a question
└── Return a final answerThat repeated cycle is the agent loop.
The model might check the order first, discover two separate shipments, check both delivery statuses, and then ask which item arrived damaged.
We did not write that exact sequence. We provided the tools and constraints that made it possible.
The Robot Analogy: A Brain Needs Something to Control
We can think of an LLM as the brain of a robot.
A brain without arms, sensors, or wheels cannot do much in the physical world. Similarly, an LLM without tools mostly produces text.
Tools give it ways to observe and act:
| Robot capability | Software agent equivalent |
|---|---|
| Eyes and sensors | Search, document retrieval, monitoring APIs |
| Hands | APIs that create or update records |
| Wheels | Navigation through pages or application workflows |
| Control panel | Tools for operating another system |
Give an agent a calendar tool, and it can help arrange a meeting. Give it a code execution tool, and it can run a calculation. Give it access to a deployment workflow, and it can request a deployment.
But having a tool does not guarantee that it will use it correctly.
A robot with arms can still pick up the wrong object. An LLM with a refund tool can still misunderstand which order the customer meant.
Tools make actions possible. Permissions, validation, and approval make those actions controllable.
The model should also never claim that it completed an action just because it generated a sentence about it. “Your order has been refunded” must be backed by a successful tool result.
What Actually Changes?
| Aspect | Traditional programming | Agentic programming |
|---|---|---|
| Workflow | We define the sequence in code | The model selects steps within a controlled loop |
| Decisions | Explicit conditions and rules | Model judgement based on instructions and available context |
| Capabilities | Functions called through predefined paths | Tools the model can choose to request |
| Predictability | Usually high for the same inputs and state | The chosen path and wording can vary |
| Unexpected requests | Need an existing branch or fallback | May be handled by combining existing tools |
| Testing | Check known inputs against expected outputs | Also evaluate tool choices, completed tasks, and constraint compliance |
| Main design challenge | Cover the required workflows | Design reliable tools and constrain model behaviour |
The important distinction is not “old code versus smart code”.
It is a predefined workflow versus a dynamically selected workflow.
Both still depend on ordinary software doing reliable work underneath.
The Control Flow Moves, but Not All of It
It is tempting to say that the control flow is no longer in our code and is now in the model’s context window.
That describes part of the change, but it is not the whole picture.
The model uses its context, meaning the instructions, conversation, and tool results it currently sees, to choose its next action. Our application still controls:
- Which tools are available.
- Which user permissions apply.
- Whether a tool request is valid.
- Which actions need approval.
- How many steps the agent can take.
- When the process must stop.
For example, the model can propose a refund. The refund service should still enforce the policy and check authorisation.
We should not rely on a prompt saying “never refund more than the purchase price”. That restriction belongs in code.
The LLM chooses among permitted actions. It does not get to redefine what is permitted.
The Hard Part Is Not Just Choosing a Model
Getting a model to call a tool is relatively straightforward. Building an agent that behaves reliably takes more work.
Most of our effort goes into making the agent's failures understandable and recoverable. A model that picks the wrong tool is not a mystery; it is a design problem. We log the context, tool arguments, and result for every step. We add explicit retry logic when a tool returns a validation error instead of letting the model guess again with no new information. We also keep the number of tools per agent small. Five well-defined tools beat twenty overlapping ones because the model has less room to choose incorrectly.
The other half is setting boundaries that are enforced outside the prompt. If a workflow absolutely must not update a production database without a human approving the SQL, the approval gate sits in the tool executor, not in the system message. The model can request the action, the application can block it, and the model can then explain why the request was blocked. That separation lets us test the policy with normal unit tests and keeps the agent's reasoning limited to what it is actually good at: selecting and sequencing actions, not enforcing business rules.
Finally, we treat agent workflows as state machines with known terminal states. There is a maximum step count, a timeout, and a defined success or failure return value. If the model loops or asks for a tool that does not exist, the application terminates the run and returns a structured error. That is the only way to make an agent safe to call from another service.




