Skip to content
AB
11 min readIntermediate

How I Use AI to Ship High-Quality Features Faster

My practical workflow for turning product intent into detailed plans, design documents, implementation slices, and carefully reviewed production code with AI agents.

Ashish BhakhandPublished

AI can write code quickly. That is the least interesting part of using it.

The difficult part of a feature is deciding what should be built, how it fits the existing product, where the boundaries belong, what can fail, and how we will know the result is correct. If those decisions remain vague, a coding agent does what any developer would do: it pauses for clarification, guesses, or implements a locally reasonable solution that does not fit the larger system.

My workflow is designed to remove that ambiguity before most implementation begins. I plan the product flow, UI and UX states, architecture, infrastructure, database model, delivery sequence, and verification strategy in detail. I often use one or more AI agents to challenge and document that plan. A coding agent then inspects the real repository, turns the plan into implementation-specific design documents, and delivers the feature in reviewable slices.

I still own the decisions. I also review the result manually. AI accelerates the work between intent and verification; it does not replace either one.

Speed starts before code#

Suppose the request is:

Add personalized weekly insights to a health application.

That sentence leaves almost every important question unanswered:

  • Which data contributes to an insight?
  • Is the result calculated when requested or generated in advance?
  • What happens when a user has only three days of data?
  • Does the insight need to work offline?
  • Where is it stored, and when does it expire?
  • Can users dismiss it?
  • Which screen states are required?
  • What data may be sent to an AI provider?
  • How do retries avoid creating duplicates?
  • How is the feature measured after release?

If I give the original sentence directly to a coding agent, it must discover these questions while editing code. Every unanswered decision becomes an interruption or an assumption. Both slow the project down.

I would rather spend focused time resolving the questions once, record the answers, and let implementation move through a clear path.

I plan the feature at several levels#

Planning does not mean producing a long document for its own sake. It means making the decisions that implementation will otherwise make accidentally.

The depth depends on the feature, but I normally work through the following areas.

1. Product intent#

I start with the user and the outcome:

  • What problem are we solving?
  • Who experiences it?
  • What can the user do after this ships that they cannot do today?
  • What is explicitly outside this version?
  • What observable behavior means the feature works?

This prevents a technically impressive implementation from solving the wrong problem.

For weekly insights, the first release might generate one useful summary from existing health data. It might deliberately exclude social sharing, historical comparisons, and user-authored prompts. Those non-goals matter because AI agents are good at expanding a feature beyond what the current release needs.

2. User flow and interface states#

I map the path through the product before choosing components:

Code
Home
  → insight preview
  → insight details
  → explanation of contributing signals
  → useful action for the coming week

Then I define states, not just the ideal screen:

  • first load;
  • loading and refresh;
  • partial data;
  • no eligible data;
  • generated insight;
  • stale insight;
  • recoverable failure;
  • offline state;
  • dismissed state;
  • accessibility and reduced-motion behavior.

This is where UI quality often comes from. A coding agent cannot implement a thoughtful empty state if the plan only describes the successful screenshot.

3. System flow and ownership#

Next I trace the feature across the system:

Code
App → API → use case → health-data repository
                    → insight generator
                    → insight repository
                    → event and observability boundaries

For each step, I decide what it owns and what it must not own. The API should validate and authorize the request. The use case should coordinate the business rules. A provider adapter should know the external AI API. The domain should not depend on its response format.

This creates instructions an agent can apply to the actual codebase instead of inventing a new architecture inside one feature.

4. Data and database design#

Database modeling happens before repository implementation. I write down:

  • entity identity and ownership;
  • required and optional fields;
  • relationships;
  • indexes and expected query patterns;
  • uniqueness and idempotency rules;
  • retention or deletion requirements;
  • migration and backward-compatibility strategy;
  • concurrency behavior;
  • sensitive fields and access rules.

A useful model for a generated insight may separate its stable identity, input period, generation status, content, source-data version, and dismissal state. That makes retries and regeneration explicit. A single unstructured JSON field would make the first write easy and every later change harder.

5. Infrastructure and operational behavior#

Features do not stop at application code. I decide whether the work belongs in a request, queue, scheduled job, or background task. I consider timeouts, retries, idempotency, rate limits, secrets, monitoring, cost, and deployment order.

For an expensive insight, the correct design might be asynchronous:

Code
schedule → enqueue eligible user → generate once → store result → notify app

The plan should say what happens when generation times out, the message is delivered twice, the provider is unavailable, or the mobile app opens before processing finishes.

6. Delivery and verification#

Finally, I split the work into slices that can be implemented and checked independently. For example:

  1. database migration and repository contract;
  2. domain rules and generation port;
  3. provider adapter and privacy filtering;
  4. background workflow and retry behavior;
  5. API contract;
  6. application state and UI;
  7. analytics, alerts, and release controls.

Each slice includes its acceptance criteria and tests. The coding agent now has a route through the work instead of one enormous instruction to “build the feature.”

I use planning agents to challenge the design#

I do not ask a planning agent to produce a plan and accept the first response. I use it as a design partner.

I give it the product goal, relevant architecture, existing conventions, known constraints, and my proposed direction. Then I ask it to find missing states, unsafe assumptions, data-model problems, scaling risks, and simpler alternatives.

Different planning conversations can focus on different concerns:

  • one on product flow and UX;
  • one on application architecture;
  • one on backend, data, and infrastructure;
  • one on testing, security, privacy, and operations.

This does not mean every feature needs four agents or four documents. The point is to review the problem from the perspectives that matter before the cost of changing direction becomes high.

When two suggestions conflict, I make the decision. The output is not a collection of AI opinions. It is one coherent plan with clear tradeoffs.

The documents are the handoff#

Conversation history is a poor source of truth. Decisions become difficult to find, later prompts lose context, and a new agent may repeat debates that were already settled.

I turn the useful parts into repository documents. A larger feature commonly has four artifacts.

Feature brief#

This explains the problem, intended outcome, user flow, scope, non-goals, and acceptance criteria. Product behavior belongs here.

Technical design#

This records boundaries, data flow, API contracts, database model, infrastructure, security, failure behavior, and important alternatives that were rejected.

Execution plan#

This breaks the design into ordered, reviewable slices. Every step identifies the files or modules likely to change, dependencies, completion criteria, and verification commands.

Test and release plan#

This covers unit and integration tests, UI states, migrations, feature flags, observability, rollout, and rollback.

The documents should be specific enough to guide implementation but concise enough to stay useful. Repeating generic advice such as “follow clean architecture” adds little. Naming the existing repository boundary, the new operation, its error cases, and its test location gives the agent something it can execute.

The coding agent creates an implementation-specific design#

The planning documents describe what the system should become. They do not always know every detail of the current repository.

Before implementation, I ask the coding agent to inspect the relevant code, local guidelines, dependencies, tests, naming patterns, and nearby features. It then produces or updates a design document grounded in what actually exists.

This step answers questions such as:

  • Which existing abstractions can be reused?
  • Where do the planned boundaries map into this repository?
  • Does a proposed table duplicate an existing model?
  • Which dependency versions constrain the approach?
  • Which tests and commands represent the real quality gate?
  • Does the initial plan conflict with a local convention?

I review that design before large changes begin. If repository evidence challenges the original plan, I update the plan. I do not force the codebase to match an assumption simply because it appeared in an earlier document.

This two-stage design process is valuable: planning establishes intent, and repository inspection establishes reality.

Implementation happens in bounded slices#

Once the design is settled, I ask the coding agent to implement one coherent slice at a time. It should run the relevant checks, report what changed, and keep the execution document current.

A useful task has a clear boundary:

Code
Implement the insight persistence slice from the approved plan.

Include:
- migration and indexes;
- repository port and adapter;
- duplicate-generation protection;
- focused repository tests;
- documentation updates required by the design.

Stop after this slice and report deviations from the plan.

This is more reliable than asking for the entire cross-platform feature in one turn. Small slices keep failures local, make review easier, and allow later work to build on verified foundations.

I also expect the agent to pause when evidence invalidates the design. “Do exactly what the document says” does not mean hiding a migration conflict or forcing an abstraction that does not fit. It means following settled decisions while surfacing new facts.

AI review is useful, but it is not my final review#

After implementation, I use AI to inspect the diff against the plan. It is good at systematic checks:

  • missing acceptance criteria;
  • unhandled states;
  • inconsistent naming;
  • forgotten tests;
  • unsafe logging;
  • boundary violations;
  • duplicated logic;
  • documentation drift;
  • suspicious migration or retry behavior.

Then I review the feature myself.

My manual review focuses on areas where ownership and context matter:

Product behavior#

I run the flow and check whether it solves the original problem. I inspect loading, empty, error, offline, and recovery states rather than only the happy path.

Architecture#

I check that responsibilities sit in the correct layer, public contracts are intentional, and the feature has not introduced a convenient shortcut that the rest of the system will later copy.

Data safety#

I inspect migrations, access control, concurrency, idempotency, deletion, and sensitive-data handling. Passing types cannot prove that a health-data query respects the correct user boundary.

UI and UX#

I use the interface. I check hierarchy, spacing, copy, keyboard and screen-reader behavior, small screens, slow networks, and interactions that feel wrong even when snapshots pass.

Operations#

I verify that logs, metrics, events, alerts, rollout controls, and rollback steps are useful in production. A feature is not complete when it works only on a developer machine.

Automated tests and AI review expand coverage. Manual review decides whether the result is a product and system I am willing to own.

Why this is faster despite more planning#

This workflow can look slower because substantial work happens before the first implementation edit. In practice, it removes the expensive kinds of delay:

  • repeated clarification during coding;
  • incompatible frontend and backend assumptions;
  • database redesign after APIs already depend on it;
  • missing states discovered during final QA;
  • architecture rework during review;
  • agents losing context across long tasks;
  • large changes that are difficult to verify or safely revert.

The coding phase becomes fast because it is no longer carrying the full burden of product and system design.

The documents also compound in value. They help future agents, reviewers, new team members, incident investigation, and later feature extensions. A good plan is not prompt decoration. It becomes part of the engineering system.

The rule I follow#

I do not use AI to avoid thinking. I use it to examine more possibilities, make decisions explicit, and execute settled work faster.

The quality of an AI-assisted feature depends less on how quickly the model can generate code and more on the clarity of the environment around it: product intent, constraints, architecture, data ownership, review boundaries, and a concrete definition of done.

Plan until the important ambiguity is gone. Let agents challenge and document the plan. Give the coding agent repository-grounded instructions. Implement in bounded slices. Use AI review for breadth and manual review for ownership.

That is how I ship faster without treating quality as the price of speed.

← Back to all writing