The suggestion landed before I finished typing the function name: a full retry wrapper with exponential backoff and a jitter calculation I hadn’t thought to include. I hit Tab, then spent the next twenty minutes working out why it swallowed one specific exception type. That’s the shape of most AI tooling for developers right now. Useful often enough to keep, wrong in ways that quietly cost more than typing the thing yourself.
I’ve been running some version of this setup for two years across a Rails monolith, a TypeScript frontend, and a pile of Python scripts nobody wants to own. Here’s what survived.
What these tools are genuinely good at
Forget the demos where someone builds a whole app from a sentence. The real wins are smaller and they repeat every week:
- Boilerplate with a known shape. Config files, DTOs, migration skeletons, the fourth paginated endpoint you’ve written this quarter.
- Translation work. Porting a jQuery widget to React, converting a bash script to Python, turning a 200-line SQL query into something a human can read.
- Reading unfamiliar code. “What calls this function, and what breaks if I change its return type?” gets answered faster than grep plus patience.
- Test scaffolding. Not good tests, but a starting file with the right imports and four obvious edge cases you then fix.
- Naming and rubber-ducking. Cheap, underrated, zero risk.
The categories worth knowing
Vendors blur the lines on purpose, but these tools behave differently enough that it helps to separate them.
Inline autocomplete
Copilot, Cursor’s tab completion, Codeium, that class of thing. It runs in the editor, suggests as you type, and knows little beyond the current file and whatever it can retrieve. Best for the keystroke-level grind. Worst when it confidently completes an API call that doesn’t exist in your version, so check the import path before you trust the signature.
Chat and reasoning models
A separate window where you paste code and argue. Architecture questions, debugging sessions, “explain this stack trace” all live here. Context window matters more than benchmark scores. A model that can hold your whole service in view gives noticeably better answers than one working from a 200-line excerpt.
Terminal and repository agents
These read files, run commands, and iterate against test output. Genuinely different from chat because they can check their own work. Also the category most likely to delete something you liked. Keep them on a branch, start from a clean git state, and never point one at a production connection string.
Review and test bots
PR comments, coverage suggestions, dependency audits. Value is uneven. A bot that flags a missing null check on a payment path earns its place; one that comments “consider extracting this into a function” on every file gets muted inside a week.
The subscription is the cheap part
Teams spend hours comparing $20/month plans and about four minutes thinking about the expensive part, which is review. A suggestion that takes 30 seconds to accept and 15 minutes to verify properly is a net loss, and it never shows up on an invoice. I watched a team’s average PR size roughly double after everyone started accepting multi-file suggestions, and their time-to-merge went up with it.
If you’re auditing what’s earning its keep, this breakdown of which AI tools are worth paying for and which to skip is a decent filter. The pattern is usually fewer tools used more deeply.
Where developers get burned
Three failure modes come up over and over.
Hallucinated dependencies. A package name that looks right, imports cleanly in the suggestion, and doesn’t exist. Typo-squatting on invented package names is a real attack vector now, so search the registry before installing anything a model recommended.
Confident staleness. Training data has a cutoff. Your framework shipped a breaking change eight months ago. The model writes the old API with total assurance, and the tests pass because nothing exercises the new path.
Silent scope creep. You asked for a bug fix; the model refactored three unrelated files “for consistency.” Small diffs are easier to review for a reason.
None of this argues against the tools. It argues for treating their output like a pull request from a fast contractor who has never met your codebase and won’t be around for the incident review.
Context is the whole game
Two developers using the same model get wildly different results, and the gap is almost entirely context. A window that knows your folder structure, your linting rules, and the three conventions in your contributing guide produces answers you can merge. One working from a bare prompt guesses.
Practically, that means a repository-level instruction file (AGENTS.md, .cursorrules, CLAUDE.md, pick one and commit it), a short architecture note explaining why the weird parts are weird, and pasting the actual error instead of your summary of it. The gap between “this endpoint returns 500” and the full stack trace plus handler source is not subtle.
Memory is the messy frontier here. I spent a week testing four ways to give an AI assistant persistent memory, and the most obvious approach, dumping everything into a giant system prompt, turned out to be the worst value by a wide margin.
Learning a new stack with one in the loop
Switching languages used to mean a month of writing code that works but reads like translated French. Models compress that considerably. Ask for the idiomatic version, ask why it differs from what you’d write in your old language, then ask it to review your attempt against local conventions. That loop of attempt, compare, ask why is roughly how I use AI to learn new topics faster, and it transfers to code better than to almost anything else.
Building AI features is a different job
Using AI tools to write code and writing code that calls a model are two separate problems, and people conflate them constantly. The second one carries its own failure modes: prompt injection, non-deterministic output wrecking your test suite, latency budgets, and the fact that your feature is a wrapper around an API you don’t control.
Projects that survive build the boring parts first. Evaluation sets. Logging of every prompt and response. A way to swap models without a rewrite. There’s a solid argument that the structure you build around the model matters more than the model, and it’s the difference between a demo and something maintainable in two years.
If your agents take actions rather than just answer questions, add governance to the list: approval gates, audit trails, spend limits, a kill switch. Governing AI agents is a shorter conversation than building them, but skipping it is how you end up with a bot that helpfully reopens 400 tickets at 2am.
A stack that survives contact with a real codebase
After a lot of churn, here’s what I’d actually pay for:
- One inline assistant in the editor. Fast, cheap, always on. Don’t overthink the vendor choice.
- One strong chat model with a large context window, used for debugging and design questions. Context size beats benchmark scores on real repositories.
- One terminal agent, used deliberately on well-scoped tasks with a clean git state and a branch to retreat to.
- Nothing else, unless a specific pain point demands it.
The number worth tracking isn’t lines generated. It’s how long a change takes from first keystroke to merged. If that number hasn’t fallen after a month of honest use, the tool you added is decoration, and the right move is to drop it and go deep on one thing instead of skimming five.

