This post contains affiliate links.
I’ve been writing code professionally for over a decade, and I’ll be honest with you: the last two years have changed how I work more than the previous eight combined. AI coding assistants are no longer a party trick or a way to generate boilerplate. They’re a genuine force multiplier — if you know how to use them correctly.
In this post I’m going to walk you through my actual workflow, the tools I rely on daily, and the specific techniques that helped me cut my average feature development time by roughly 3x. I’ll include real code examples and won’t shy away from the parts that still frustrate me.
The Honest State of AI Coding in 2026
Let me get the hype out of the way first. AI is not going to replace you. What it will do is make the developer who uses it well dramatically more productive than the one who doesn’t. I’ve watched junior devs on my team outpace senior engineers who refuse to adapt. That’s not a slight against experience — it’s a reality check about tooling.
The tools that have genuinely moved the needle for me are AI-native editors. I switched to Cursor about eighteen months ago and I haven’t looked back. It’s built on top of VS Code so the transition is nearly painless, but the deep integration of Claude and GPT-4 class models into the editing experience is night-and-day compared to a plugin bolted onto an existing editor. If you’re still running Copilot as a VS Code extension and wondering why AI coding feels underwhelming, that’s probably your answer.
My Core Workflow: The Three-Phase Approach
I’ve settled into a three-phase workflow for almost every non-trivial feature: spec with AI, implement with AI in the loop, review with AI. Let me break each one down.
Phase 1: Spec First
Before writing a single line of code I open a chat context and describe what I’m building. Not to get code — to pressure-test my thinking. I’ll write something like:
I need to build a rate limiter middleware for an Express API.
It should support per-user limits, sliding window algorithm,
and store state in Redis. What edge cases should I be thinking about
before I start implementing this?
The response usually surfaces two or three things I hadn’t considered. Race conditions in distributed environments, TTL handling when Redis restarts, what to return in the response headers. This five-minute conversation saves me hours of refactoring later.
Phase 2: Implement With Context
This is where most people use AI wrong. They dump a vague prompt into a chat window and paste whatever comes out into their codebase. That’s not how I do it.
In Cursor, I use the @codebase reference feature heavily. When I’m building a new feature, I reference existing similar implementations so the model understands my patterns, naming conventions, and architectural decisions. The output is code that actually fits into my project rather than generic tutorial code.
Here’s a real example. I needed to add webhook signature verification to an existing API. My prompt looked like this:
@src/middleware/auth.ts @src/middleware/rateLimit.ts
Add a new middleware function called verifyWebhookSignature.
It should:
- Extract X-Webhook-Signature header
- Compute HMAC-SHA256 of raw request body using WEBHOOK_SECRET env var
- Compare using timing-safe comparison
- Return 401 with {error: 'Invalid signature'} on failure
Follow the same pattern as the existing middleware in this file.
The output was production-ready on the first try:
import { Request, Response, NextFunction } from 'express';
import { createHmac, timingSafeEqual } from 'crypto';
export function verifyWebhookSignature(
req: Request,
res: Response,
next: NextFunction
): void {
const signature = req.headers['x-webhook-signature'] as string;
if (!signature) {
res.status(401).json({ error: 'Invalid signature' });
return;
}
const secret = process.env.WEBHOOK_SECRET;
if (!secret) {
throw new Error('WEBHOOK_SECRET environment variable is not set');
}
const rawBody = (req as any).rawBody;
if (!rawBody) {
res.status(400).json({ error: 'Raw body not available' });
return;
}
const expectedSignature = createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
const expectedBuffer = Buffer.from(expectedSignature, 'utf8');
const receivedBuffer = Buffer.from(signature, 'utf8');
if (
expectedBuffer.length !== receivedBuffer.length ||
!timingSafeEqual(expectedBuffer, receivedBuffer)
) {
res.status(401).json({ error: 'Invalid signature' });
return;
}
next();
}
That’s correct, idiomatic TypeScript that handles the timing-safe comparison properly — something I’ve seen even experienced developers get wrong when writing from scratch under deadline pressure.
Phase 3: AI-Assisted Code Review
Before I open a PR, I now routinely paste my diff into a chat and ask for a security and logic review. I’m not looking for style feedback — I have linters for that. I want the model to think adversarially about my code.
Review this diff for security vulnerabilities, logic errors,
and missing error handling. Be specific and don't soften your feedback.
The “don’t soften your feedback” instruction matters. Without it you get a lot of “this looks good, but consider…” With it you get direct callouts. Last month this caught an unvalidated redirect vulnerability I’d introduced without realizing it.
Where I Still Use Human Judgment
I want to be clear about what AI doesn’t replace in my workflow.
Architecture decisions. When I’m making a decision that will affect the codebase for years — should this be a microservice or stay in the monolith, should I introduce an event bus here — I don’t outsource that to AI. I’ll use it as a sounding board, but the decision stays mine.
Domain-specific business logic. AI doesn’t know my users, my company’s compliance requirements, or why we made certain tradeoffs two years ago. I have to bring that context explicitly, and even then I verify carefully.
Anything touching production data migrations. I write these by hand, review them twice, and test them on a copy of production before they get anywhere near the real database. No shortcuts here.
The Infrastructure Side of the Equation
Shipping faster also means having infrastructure that doesn’t slow you down. Half the productivity gains I’ve made would be wasted if deployments took 20 minutes or environments kept drifting from production.
I host most of my projects on DigitalOcean. The combination of App Platform for simpler services and Droplets for anything that needs more control gives me exactly the right level of abstraction without the complexity of AWS. When I’m moving fast, the last thing I want is to debug IAM permissions at 11pm. DigitalOcean’s predictable pricing also means I can spin up staging environments without worrying about surprise bills.
Practical Tips I Wish Someone Had Told Me
Be specific about constraints, not just requirements. “Write a function that fetches user data” produces mediocre results. “Write a function that fetches user data, handles rate limit errors with exponential backoff, times out after 5 seconds, and logs errors using our existing logger at @src/utils/logger.ts” produces something you can actually use.
Maintain a prompts file. I keep a prompts.md in my personal notes with my best-performing prompts for recurring tasks. Code review prompts, refactoring prompts, test generation prompts. Don’t reinvent the wheel every time.
Use AI for tests aggressively. Generating unit tests is one of the highest-ROI uses of AI coding assistants. It’s tedious work that developers routinely skip under deadline pressure. Now I generate a test scaffold, review it, and fill in the domain-specific assertions. Test coverage on my projects has gone up significantly since I started doing this.
Read every line it generates. This should be obvious but I’ve seen developers paste AI output directly into production. You own the code. Read it. The model hallucinates, uses deprecated APIs, and occasionally produces subtly wrong logic. Your review is not optional.
The Productivity Numbers, Honestly
I claimed 3x faster at the top of this post. Let me be precise about what that means. For features I can clearly spec and that don’t require deep architectural decision-making, yes, I complete them in roughly a third of the time compared to two years ago. That includes writing, testing, and doing an initial review.
For genuinely complex work — distributed systems problems, novel business logic, performance-critical code paths — the improvement is closer to 1.5x. The AI helps most with the parts that are tedious and well-understood. It helps less with the parts that are genuinely hard.
That’s still a massive productivity gain when you average it across a week’s work.
Where to Go From Here
If you’re just getting started with AI-assisted development, my practical recommendation is to pick one tool and go deep on it rather than dabbling in five. Get to the point where you’re not thinking about how to use the tool — just thinking about what you’re building.
For most developers I talk to, Cursor is the right starting point. The codebase context features alone are worth the switch from a standard editor plus Copilot setup.
The developers who will thrive in the next few years aren’t the ones who resist these tools or the ones who blindly trust them. They’re the ones who develop genuine fluency — who know when to lean on AI and when to step back and think for themselves. That’s the skill worth building.
I’ll be writing more about specific workflows and techniques here on WithStack. If there’s a particular aspect of AI-assisted development you want me to dig into, drop a comment below.