AI-Assisted Programming Through Subtraction: From One-Shot Generation to Agentic Collaboration
AI-assisted programming has evolved from standalone chat-based code generation to editor-integrated suggestions and, more recently, agentic workflows that iteratively plan, execute, observe, and verify. This article explains how these stages differ, breaks down the mechanisms behind agentic coding, and presents practical principles for controlling context, tools, permissions, validation, and recoverability.
Introduction
When discussing AI-assisted programming, people often focus on model capability. However, the interaction model also has a major impact on results. The same model can produce significantly different outcomes and development efficiency depending on how it is integrated into the workflow.
This article examines three stages of technological evolution, with a focus on how agentic programming works. It also presents design principles that teams can use when adopting these workflows.
1. Stage One: Standalone Conversational Generation
In the first stage, developers describe their requirements in a chat window. The model returns code, and the developer manually copies it into the project.
The main source of friction is missing context. The model cannot see the project structure, dependency versions, or team conventions. It can only infer them from the developer’s description. The generated code must be reviewed line by line. After making changes, the developer returns to the chat window to ask another question, creating an inefficient cycle of back-and-forth interactions.
2. Stage Two: One-Shot Generation Inside the Editor
As AI tools moved into development environments, code generation became integrated with the cursor position. A developer can select a block of code or issue an instruction at a specific location. The model uses the current file and nearby code to suggest a change, which is written into the file after approval.
This removes the copy-and-paste step, but the interaction model is still essentially “one request, one output.” The model does not run the code, know whether the build succeeds, or see the test results. When an error occurs, the developer still has to determine how to fix it.
This stage solves the problem of operating the tool, but not the problem of receiving feedback.
3. Stage Three: The Agentic Loop
Agentic programming places code generation inside a continuous loop:
- Understand the goal.
- Read the relevant context.
- Create a plan.
- Call tools.
- Observe the results.
- Make corrections and continue until the completion criteria are met.
The key difference is that the output from each iteration becomes the input for the next one. A typical loop can be represented by the following pseudocode:
while task is not complete and step or cost budget has not been exceeded:state = collect_context(task_description, relevant_project_files, execution_history)action = model_decides_next_step(state, currently_available_tools)if action is a high-risk operation:wait for human approvalresult = execute_tool(action)record(action, result)task_complete = run_acceptance_checks(acceptance_criteria)
Every step in the loop can be observed, recorded, and interrupted. This makes the automation process auditable and creates clear points for human intervention.
4. Four Directions of “Subtraction”
The more capable an agent becomes, the more important it is to remove unnecessary elements. The goal is not to reduce capability, but to reduce noise, redundancy, and unconstrained freedom.
4.1 Reduce Context Noise
A model’s context window is limited. Irrelevant information dilutes attention, increases cost, and may introduce incorrect assumptions. In practice, teams can take three measures:
- Retrieve relevant files on demand instead of injecting the entire codebase into the context.
- When a long task reaches the end of a phase, create a concise summary instead of carrying the full history forward.
- Organize project conventions in a fixed instruction file that is loaded automatically for each task.
4.2 Reduce the Number of Tools
The more tools an agent has, the higher the cost of choosing among them and the greater the chance of an incorrect call. Only expose the minimum toolset required for the current task, such as reading files, searching code, editing files, and running tests.
Clear capability boundaries make behavior more predictable.
4.3 Reduce Freedom
Once an agent can execute actions, its permissions must be restricted. Limit writes to the working directory, enable network access only when necessary, and require human confirmation for actions such as deleting files, changing permission settings, or publishing a release.
Authorization should be granted by risk level rather than enabled all at once.
4.4 Reduce Ineffective Steps
Excessive planning can slow down execution and accumulate incorrect assumptions. A more reliable approach is to take small actions, validate them immediately, and adjust the plan according to real feedback.
Each step should perform one action that can be tested or verified.
5. Key Mechanisms
5.1 Layered Context Management
Context can be divided into three layers.
The first layer consists of persistent rules, including coding standards, directory structure, and prohibited actions. This information is relatively stable and should be loaded for every task.
The second layer is task context, including the current requirement, relevant code, and acceptance criteria. This layer changes from task to task.
The third layer is temporary observation, including tool output, error messages, and intermediate results. It has the shortest lifecycle and should be compressed or discarded after use.
The purpose of layering is to control the context budget. Persistent rules should remain concise because they create a fixed cost for every call. Task context should be selected and ordered according to relevance.
5.2 Tool Calls and Result Injection
Tool results should be structured and length-limited. If verbose logs are inserted into the context without processing, they consume space that could otherwise hold useful information.
A better approach is to preserve the error type, failure location, and key values while truncating repeated content. For example:
Tool: Run test suiteStatus: FailedFailed tests: 1 / 42Test name: test_empty_emailExpected: Raise ValueErrorActual: Returned FalseRelated file: user_service.py, line 18
Well-structured result data allows the model to locate the problem directly and reduces unproductive guessing.
5.3 The Verification Loop
Verification is the foundation of agent reliability. Common verification methods include compilation or builds, type checking, static analysis, unit tests, and API smoke tests.
Each method provides an objective pass or fail signal. These signals give the agent the information it needs to decide what to do next.
When verification fails, the error information should be passed into the next context window in full enough detail to support diagnosis. The agent should not simply be told to “try again.” A typical correction process looks like this:
Action: Modify the null-handling branch in validate_emailResult: Build passedAction: Run the test suiteResult: 1 test failed; test_empty_email expected an exception but received FalseAction: Change the empty-string branch to raise an exception and update the callersResult: All tests passed
This example demonstrates the value of a closed loop: every change is tested against reality instead of relying on the model’s own judgment.
5.4 State Tracking and Recoverability
Long-running tasks may span multiple sessions or be interrupted by network and permission issues. An agent should record progress checkpoints, including completed steps, modified files, and pending work.
Before making changes, create a backup or use a version control system to save a snapshot. This ensures that a failure at any step can be rolled back to a known state.
6. Practical Recommendations for Adoption
Define acceptance criteria before allowing the agent to act. Acceptance criteria should be machine-verifiable, such as passing tests, returning an expected API value, or introducing no new static-analysis warnings.
Create a concise project-conventions file. Include the directory structure, naming rules, dependency-management approach, test commands, and prohibited actions. The more specific the conventions are, the less room there is for agent errors.
Grant permissions according to risk. Reading can be automated. Write operations should be restricted to an isolated branch or workspace. Releases, deletions, and permission changes must require human confirmation.
Review the agent’s output, with particular attention to edge cases. Null values, concurrency, exception paths, and dependency changes are common sources of errors and should receive extra scrutiny.
Use data to determine the authorization scope. Track first-pass success rates, rework frequency, and verification time for each task category. Expand permissions gradually after the results become stable instead of granting access based on intuition.
7. Risks and Boundaries
Agentic programming introduces four major categories of risk.
Fabricated interfaces. A model may call a function or use a parameter that does not exist. Compilation and testing are the primary ways to expose these problems; code review alone is not enough.
Unauthorized changes. An agent may modify files unrelated to the task. Restricting the write scope and reviewing the change list can keep the impact within the expected boundaries.
Sensitive information leakage. Secrets, tokens, and personal data should not enter the context or logs. Credentials should be provided through environment variables or a dedicated secrets-management service, and sensitive values should be redacted from logs.
Overconfidence in automation. Passing tests only shows that the code satisfies the existing checks. It does not prove that the business logic is completely correct. Human acceptance is still required for critical paths.
8. Conclusion
The value of agentic programming comes from its feedback loop, and that loop depends on constraints.
Reducing context noise, reducing the number of tools, and reducing unconstrained freedom together form the foundation of controllable automation. The purpose of subtraction is to keep the agent within clear boundaries while allowing it to continuously correct itself according to real feedback.
Tool interfaces will continue to evolve, but the underlying framework can remain useful: define explicit acceptance criteria, manage context in layers, grant permissions according to risk, and use verification instead of guesswork.
