AI coding assistants such as Codex and Claude can dramatically reduce the time required to analyze a codebase, propose a refactoring, generate tests, or implement a feature. But that speed introduces a risk: confusing the ability to produce code quickly with the ability to make the right technical decisions for a real project.
In my work I try to avoid the most immediate workflow: requirement → prompt → code. Before asking an assistant to modify the project, I ask it to analyze the problem and formulate a proposal. Only after a discussion and refinement phase do I move to implementation.
The principle I try to preserve
AI can compress execution time dramatically, but analysis, technical decisions, and responsibility for the result must remain under the developer's control.
The problem is not how quickly AI can write code
A coding assistant can produce in a few minutes changes that would otherwise require hours or days of manual work. That is a real advantage and it would be a mistake to ignore it.
The key question is what we are measuring. If we only measure the time required to generate a patch, the fastest approach is almost always to ask the AI to implement the solution directly. If instead we measure the time required to obtain a change that is correct, consistent with the architecture, understandable by the team, and safe enough to reach production, the picture changes.
Business software contains constraints that are often not fully represented in the source code: internal conventions, architectural decisions, compatibility with legacy systems, operational requirements, deployment practices, customer expectations, and decisions inherited from the product's history.
A model can detect many of these elements, but it cannot automatically know which ones are negotiable and which ones reflect deliberate company choices.
Before code: ask for a technical proposal
When I approach a change, I do not normally start with a prompt such as "implement this feature". I prefer to separate analysis from execution.
An initial request may look more like this:
Analyze the requirement and the existing code.
Do not modify any files yet.
Identify:
- components involved;
- dependencies;
- possible regressions;
- technical constraints;
- implementation alternatives;
- the approach you recommend and its trade-offs.I do not treat this first response as a specification to accept automatically. It is a proposal to discuss.
If the assistant introduces a dependency that the project does not want, proposes a pattern that does not fit the rest of the solution, or assumes that a legacy component can be changed when compatibility must be preserved, I add that constraint and ask it to reconsider the proposal.
Brainstorming with AI is part of the work, not wasted time
The phase before writing code is a form of technical brainstorming. The assistant can act as a sparring partner that helps stress-test an idea before it becomes a concrete change.
At this stage it can be useful to ask:
- what alternatives exist;
- which failure modes have not been considered;
- which parts of the existing code could be affected;
- whether the solution increases coupling;
- whether a more incremental approach exists;
- which tests would verify the behavior;
- which assumptions the assistant is making.
The final analysis must be compatible not only with the requirement, but also with the technical philosophy of the company or client and with the way the project is maintained.
A particularly useful question
Which assumptions are you making that you cannot verify directly from the code you analyzed?
Refinement: progressively narrowing the scope
A good initial proposal is rarely the final one. Additional constraints may emerge, or the impact of the change may need to be reduced.
For example, after the first analysis I may add instructions such as:
Reconsider the proposal with these constraints:
- do not introduce new external dependencies;
- preserve compatibility with legacy SQL Server;
- do not change the public API contract;
- prefer an incremental change;
- reuse patterns already present in the solution;
- keep rollback simple.Only when the approach is sufficiently clear do I authorize implementation.
This changes the role of the assistant: I am no longer asking it to decide freely how to solve the problem, but to implement a solution whose boundaries have already been discussed.
A four-stage workflow
| Stage | Goal | Main responsibility |
|---|---|---|
| 1. Analysis | Understand the requirement, codebase, dependencies, and risks | AI + developer |
| 2. Brainstorming and refinement | Compare alternatives and adapt the solution to real constraints | Developer |
| 3. Implementation | Apply the agreed solution within a controlled scope | AI under supervision |
| 4. Review and testing | Verify code, behavior, regressions, and quality | Developer |
The workflow is therefore not prompt → code, but analysis → discussion → implementation → verification.
Code generation comes after a technical decision
Once the analysis phase is complete, AI can express its speed advantage much more effectively. It can modify several files consistently, generate boilerplate, update tests, propagate repetitive changes, or implement a refactoring with a speed that is difficult to match manually.
Even at this stage I prefer to keep the work divided into understandable blocks. A very large change makes it harder to see where unexpected behavior was introduced and makes code review more expensive.
Whenever possible, I therefore ask for small, verifiable changes, keeping the diff readable and rollback simple.
Git remains one of the most important safety nets
Using a coding assistant does not change good version-control practices. If anything, it makes them more important.
Dedicated branches, coherent commits, and reviewable diffs make it possible to clearly distinguish the initial code from what was generated or modified during the session.
Before accepting a change I want to be able to answer three questions:
- which files changed?
- why is each change necessary?
- can I revert the intervention without reconstructing the previous state manually?
After code: review cannot be delegated
The second stage where I deliberately slow the process down is after generation.
I read the diff, verify that the implementation matches the approved analysis, and check that no unrelated changes have been introduced. Compilation and automated tests are important, but they do not replace technical review.
I pay particular attention to:
- consistency with patterns already used in the project;
- error handling and edge cases;
- compatibility with the existing framework and infrastructure;
- security and data handling;
- performance and the number of database or external-service calls;
- readability and maintainability;
- possible regressions in behavior not directly related to the requirement.
A simple rule is: if I could not explain a change during a code review, I should not ship it to production simply because the tests pass.
AI can write tests, but it should not be its own only judge
Assistants are very useful for generating unit tests, identifying edge cases, and building a first verification suite quickly. However, when the same model produces both the implementation and the tests, there is a risk that both share the same incorrect interpretation of the requirement.
Generated tests therefore need to be compared against the actual expected behavior, against characterization tests for existing systems when available, and against edge cases derived from domain knowledge.
The fact that code and tests agree with each other does not automatically mean that they agree with the business requirement.
Legacy software requires even more care
In a new codebase many decisions are explicit and the scope is relatively easy to control. In a legacy application, actual behavior may depend on stored procedures, scheduled jobs, configuration, UI events, external integrations, and conventions that were never formally documented.
In these systems an apparently elegant change can be technically correct while still being incompatible with an existing dependency.
This is one of the reasons why, even when I use AI to accelerate analysis or refactoring, I prefer incremental and verifiable interventions. The same principle I apply to modernizing .NET Framework applications applies to AI: understand the system first, then decide how much to change.
Data, secrets, and proprietary code
A development assistant may gain access to a significant amount of information: source code, configuration files, documentation, logs, database schemas, and sometimes sample data.
Before using it in a company environment, developers need to understand the client's policies and how the tool handles data. Credentials, connection strings, tokens, personal data, and confidential information should not be shared indiscriminately.
The fact that a tool is technically available does not automatically mean it is authorized for every repository.
"You are giving up part of the speed advantage"
This is an objection I have heard, and it contains some truth.
I have seen an assistant complete in around ten minutes an activity that, if developed entirely by hand, would have required one or two days. But when I include the initial brainstorming, refinement of the proposal, and the final review, I may still spend several hours on the same task.
I do not consider those hours wasted. They are the cost of technical governance.
Productivity is not about maximizing the number of lines generated per minute. It is about reducing the total time required to reach software that is correct, understandable, verifiable, and consistent with the system in which it has to live.
The speed I care about
I do not want AI to write code as fast as possible. I want to compress execution time without delegating decisions that I must still be able to justify and maintain.
When AI becomes a real multiplier
Used this way, AI does not replace senior engineering work: it mainly amplifies the execution layer.
It can explore a solution quickly, identify references, produce an initial dependency map, propose alternatives, generate repetitive implementation work, and prepare tests. The developer can spend more time on requirements, trade-offs, architecture, and verification.
The same principle applies when AI is integrated directly into business software: the model should operate inside clear application boundaries. I discuss this further in the guide on integrating AI into a .NET business application without losing control of data and responses.
Practical checklist before accepting AI-generated code
Before
- Is the requirement clear?
- Has the assistant analyzed the code?
- Has it stated risks and assumptions?
- Have alternatives been considered?
Decision
- Does the proposal follow company patterns?
- Does it respect client constraints?
- Is the scope small enough?
- Is rollback simple?
After
- Have you read the diff?
- Does it compile without new warnings?
- Do tests verify the real requirement?
- Have edge cases been checked?
Production
- Can you explain every change?
- Have you considered security and performance?
- Are unnecessary data and secrets excluded?
- Will the solution remain maintainable by the team?
FAQ
Is it wrong to ask Codex or Claude to implement a feature directly?
Not necessarily. For very small and well-defined changes it can be reasonable. As impact, dependencies, or risk increase, separating analysis from implementation makes it easier to remain in control.
Does this process cancel out the speed advantage?
It reduces the apparent speed of code generation alone, but it can reduce the total time required to reach a reliable change. More importantly, it avoids saving minutes during implementation only to lose much more time debugging regressions later.
Do I really need to read all AI-generated code?
For code intended for production, yes. The depth of review should be proportional to risk, but responsibility cannot be transferred to the model.
What is the best role for a coding assistant?
For me it is simultaneously an analyst, a sparring partner, and an execution accelerator. It is not the owner of architectural decisions and it is not ultimately responsible for the software.
In summary
AI writes fast. Technical control must remain human.
Analyze first, discuss the proposal, implement within clear boundaries, and review afterwards: this makes it possible to benefit from Codex, Claude, and other coding assistants without trading speed for understanding and control.
Related guides
Want to introduce AI into your development process without losing technical governance?
I support software houses and .NET teams with codebase analysis, software modernization, and pragmatic adoption of AI tools inside controlled development processes.