AI Coding Tools Are Making Me Faster — But Are They Making Me Worse?

The Moment I Realized I Had a Problem

I was doing a technical phone screen — no IDE, just a shared Google Doc — and I needed to write a reduce() call to group an array of objects by a key. I’ve written that exact pattern probably 300 times across different codebases. My fingers went to the keyboard and just… stopped. I drew a complete blank on the accumulator argument. Not the concept, not what I wanted to do — the actual physical act of typing it from memory had become foreign to me.

What shook me wasn’t forgetting. It was the realization immediately after: I hadn’t typed that pattern in months. Every time I started writing a reduce(), Copilot had already generated the full call by the time I reached the opening parenthesis. I’d been reading completions and hitting Tab. Not typing. Reading and accepting. Those are completely different cognitive acts, and I’d been confusing them for skill maintenance.

// This is what I blanked on. A pattern I "know" cold.
const grouped = items.reduce((acc, item) => {
  const key = item.category;
  if (!acc[key]) acc[key] = [];
  acc[key].push(item);
  return acc;
}, {});

I want to be clear about what this post isn’t. I’m not going to tell you Copilot is bad, or that you should delete Cursor and suffer through writing boilerplate by hand to prove your worth. These tools genuinely make me ship faster — I’ve tracked it, the difference is real. But something specific happens to your brain when a tool removes the retrieval step from a skill you used to practice dozens of times a day. The aviation industry actually has a name for this: automation complacency. They’ve been studying it since the 80s. We’re just starting to feel it in software.

This is specifically for devs who are already deep in the AI-assisted workflow — daily Copilot users, people running Cursor with Claude or GPT-4o on their personal projects, engineers whose companies pay for the subscription. If you’ve ever closed your laptop after a productive day and had a nagging feeling that you’re not sure you could reproduce what you just shipped without the assistant, this is for you. That feeling is worth taking seriously — not as a reason to quit the tools, but as a signal that something in how you’re using them needs adjusting.

My Current AI Tool Stack (So You Know Where I’m Coming From)

Eight months ago I ditched VS Code + Copilot and moved to Cursor backed by Claude Sonnet 3.5. That switch felt risky at the time — I had years of muscle memory in VS Code and Copilot had been good enough. But “good enough” started feeling like a ceiling. The thing that actually pushed me over was Cursor’s @codebase indexing: I could ask a question about a file I hadn’t opened in three weeks and get an answer that understood the actual relationships in my project, not just what was visible in the current tab.

Claude.ai in the browser gets used differently from my in-editor tooling. I don’t use it for autocomplete or inline edits — I use it the way I’d use a senior engineer in a Slack thread. Architecture decisions, tradeoff discussions, “here’s my schema, tell me why this query pattern will bite me in six months.” The longer context window means I can paste an entire module and have a real conversation about it. I’ve had browser sessions with Claude where I’ve gone back and forth fifteen times refining a design before writing a single line of code. That’s a different workflow from tab-completion, and it took me a while to stop conflating the two use cases.

Codeium lives on my work-issued machines where I can’t install arbitrary software or burn a seat license on a third-party editor. The free tier is genuinely useful — completion quality is solid, it supports every language I touch, and the setup is about four minutes in any JetBrains IDE or VS Code. I wouldn’t choose it over Cursor if I had the option, but it’s meaningfully better than nothing and the price-to-value ratio at $0 is hard to argue with.

I stopped using Copilot X chat about nine months ago. The proximate cause was a specific incident: I was working in a codebase using a slightly older version of a library, and Copilot kept suggesting method signatures that didn’t exist in that version — confidently, with no hedging. Not once or twice. Repeatedly, across a multi-hour session. The context window was also too small to fit enough of my codebase to give it a fair chance at understanding what I was actually building. Small context + confident hallucination is a rough combination when you’re debugging something subtle.

If you want a broader map of what’s available right now — pricing, context window limits, IDE support, which models back which tools — the Best AI Coding Tools in 2026 guide has that covered in detail. My stack is one opinionated slice of a genuinely crowded space, and what works for my workflow (mostly TypeScript, Go, some Rust, medium-sized codebases, solo or small-team) might not match yours.

What These Tools Actually Help With (Honestly)

The honest version of this answer requires separating two questions: what do these tools actually do well, and what do developers use them for most. Those overlap but they’re not the same thing. I’ve watched myself reach for Copilot or Cursor on autopilot for things I should probably be doing manually, but there are also genuine productivity wins that I’d feel silly giving up.

Boilerplate That Genuinely Doesn’t Require Thought

Writing a Dockerfile for a Node 20 app is not a skill challenge. There’s a correct answer, it hasn’t changed much in two years, and re-deriving it from memory is pure friction. Same with Jest scaffolding — the describe/it/beforeEach skeleton adds zero value to anyone’s brain. I generate these instantly and move on. The one I use most is TypeScript interface generation from a raw JSON blob:

// paste this into Cursor or Copilot Chat:
// "generate a TypeScript interface from this JSON"
{
  "user_id": "abc123",
  "plan": "pro",
  "created_at": "2024-01-15T10:30:00Z",
  "features": ["sso", "audit_logs"],
  "limits": { "seats": 25, "storage_gb": 100 }
}

// you get back:
interface User {
  user_id: string;
  plan: string;
  created_at: string; // ← you'll want to convert this to Date manually
  features: string[];
  limits: {
    seats: number;
    storage_gb: number;
  };
}

The created_at gotcha is real — it always generates string because that’s what the JSON says. You still need to review the output. But generating this by hand from a 40-field API response is the kind of tedious work that makes people leave early on Fridays.

The API Spelunking Problem

I know Node’s filesystem module exists. I cannot always remember whether I want fs.promises.readFile, fs.readFile with a callback, or fsSync.readFileSync without Googling it. This is not a knowledge gap — it’s a lookup problem. AI tools handle this better than MDN search because the answer comes pre-contextualized to what I’m already doing:

// what I actually type to Copilot inline:
// read a JSON file async and parse it, Node 20, ESM

// what I get back (and actually use):
import { readFile } from 'fs/promises';

const data = JSON.parse(
  await readFile(new URL('./config.json', import.meta.url), 'utf-8')
);

The new URL('./config.json', import.meta.url) part is what I always forget — in ESM you can’t use __dirname, so you reconstruct the path like this. Would I have figured it out from the docs? Yes. Did it take 4 seconds instead of 3 minutes? Also yes.

Regex First Drafts

I verify every regex these tools produce — full stop. But starting from nothing on a complex pattern is genuinely slower than starting from a generated draft. For anything beyond \d+ I’ll prompt for a first pass and then run it through regex101.com against real inputs. The tools are good at common patterns (email-ish validation, semver, URL extraction) and unreliable at edge cases (Unicode, lookaheads that interact with quantifiers). The workflow that works for me:

  1. Describe the pattern in plain English, include 2-3 example inputs
  2. Get the generated regex
  3. Immediately test against edge cases I know will break it
  4. Fix it manually or iterate with the tool

The thing that caught me off guard was how confidently wrong these can be. A generated regex will look completely reasonable and fail silently on inputs with trailing whitespace or mixed line endings. Never skip the test step.

Decoding Other People’s Code

This might be the most underrated use case. Pasting a gnarly legacy function into Cursor Chat and asking “what does this do and what are the edge cases” has saved me real hours on codebases I’ve inherited. The AI doesn’t get intimidated by 200-line functions with implicit state mutations and six levels of callback nesting. It reads the whole thing and gives you a prose explanation that’s usually 80% accurate — good enough to know where to start your actual investigation.

// real prompt structure I use:
"Here's a function from a legacy codebase.
Explain what it does, what its inputs/outputs are,
and call out any side effects or cases where it
might return unexpected values."

[paste function]

The 20% inaccuracy matters. I’ve had Cursor confidently misread a stateful class method as a pure function because the shared state was defined three files away. It can only reason about what you give it. For legacy code specifically, I always paste in the surrounding context — the class definition, any relevant globals — before asking for the explanation. The quality difference is significant.

Where I’ve Actually Caught Myself Getting Dumber

Accepting Fixes Before Diagnosing the Problem

The most embarrassing one: Cursor surfaces a suggested fix, I read it, it looks plausible, I apply it, the tests go green — and I never actually figured out what was broken. The fix was a bandage. I don’t know where the wound is. Three weeks later the same class of bug shows up in a different part of the codebase and I’m just as lost as I was the first time, because I never built the mental model of why the original code was wrong. I’ve started forcing myself to write one sentence in a comment before accepting any AI suggestion: “This broke because…” — and if I can’t finish that sentence, I close the suggestion pane.

SQL JOINs Used to Be Automatic

I wrote complex multi-table queries fluently for years. LEFT JOIN with a NULL check to simulate NOT EXISTS, window functions over partitions, CTEs for readability — these were muscle memory. Now I catch myself opening a chat window before I’ve even tried to write the query. The AI output is usually fine. But “usually fine” means I’m also not catching when it’s subtly wrong — like when it generates a query that returns the right rows 95% of the time but silently drops duplicates because it chose DISTINCT instead of a proper GROUP BY. I missed one of those in review last quarter. That used to be the kind of thing I’d have spotted immediately because I’d have written it myself first.

Stack Traces Are a Learning Tool You’re Throwing Away

I caught myself doing this on a Tuesday morning: Python throws a KeyError, I copy the traceback, paste it into Claude, get a fix in 15 seconds. Job done. But that traceback was actually telling me something about how the config loading sequence works — which module initializes first, where the dict gets populated, why the key was missing at that specific callsite. Reading stack traces carefully is how you build a map of the codebase in your head. Pasting them into chat skips straight to the answer without building any of that map. Over months, you end up working in a codebase you don’t actually understand, even one you wrote yourself.

The Specific Failure Mode No One Talks About Enough

Getting the right output without building the mental model of why it’s right is genuinely dangerous, and it’s different from just being lazy. You feel productive. The code works. The PR merges. But your internal model of the system hasn’t updated. Compare this to the experience of grinding through a hard bug manually — you come out the other side understanding something you didn’t before. That understanding compounds. It’s what lets you estimate accurately, spot problems in code review, and make architectural decisions with confidence. AI-assisted debugging can short-circuit all of that, and the loss is invisible until you’re in a room where you need to think on your feet and realize the map in your head has huge blank patches.

The Skill Atrophy Is Real — But It’s Not Uniform

The thing that surprised me most wasn’t losing syntax. It was noticing I’d lost it silently. I opened a Python file without Copilot running one afternoon and stared at the argparse API for a full minute before giving up and checking the docs. Six months earlier I could have written that from memory. The atrophy doesn’t announce itself — it just quietly happens while you’re shipping faster than ever.

Here’s the split I’ve actually observed in myself and the devs I work with: the skills that vanish fastest are the ones that were always about memorization. Exact method signatures. Whether it’s str.split() or str.splitlines(). The specific order of arguments in subprocess.run(). Boilerplate you’ve typed five hundred times — JWT middleware, database connection setup, that Express route scaffolding you used to produce on autopilot. These go away fast because the AI is always there as a faster lookup than your own brain, and your brain eventually stops bothering to cache the data.

What doesn’t atrophy — and this matters — is the judgment layer. My ability to look at generated code and immediately smell that a Redis cache is going to cause a stampede under concurrent load has actually gotten sharper, not duller. Same with system design. Copilot can’t decide whether you need an event-driven architecture or a simple cron job. It can’t tell you your database schema is going to make that one query a full table scan at scale. Those skills sit higher in the abstraction stack, and because the AI handles the low-level noise, I find I’m spending more time at that level. The problem is that getting to that level requires having spent years at the lower level first.

The GPS analogy is the one that actually maps here. GPS didn’t make everyone equally bad at navigation. It made people bad at remembering routes while leaving intact the ability to sanity-check that the suggested route doesn’t take you through a river. You still need the mental model — you just don’t need to store the turn-by-turn details. AI coding tools do the same thing, except the “mental model” in software development takes years to build, and it’s built by writing the boilerplate, fighting the weird bugs, and wrestling with the API docs yourself. Skip that phase and you can use GPS fine until you’re somewhere GPS doesn’t work.

This is where the junior/senior split gets uncomfortable to talk about honestly. A senior dev losing syntax recall is fine — they built their mental models years ago and the AI is just offloading clerical work. A junior dev using Copilot to write their first CRUD app is potentially skipping the part where you struggle with why your foreign key constraint is failing, and that struggle is the point. The frustration of debugging a malformed SQL join at 11pm is how you learn to read query plans. I’ve interviewed recent grads who can ship features in Next.js with AI assistance at impressive speed, then completely freeze when asked to reason through what an index actually does. That’s not a knock on them — it’s a structural problem with how the tools remove productive friction from the learning path.

  • Goes fast: Language-specific syntax, stdlib method names, framework boilerplate, config file formats you used to know by heart
  • Goes slower: Debugging intuition, reading stack traces, knowing which error message means what
  • Actually improves: Code review quality, architectural reasoning, recognizing when generated code is subtly wrong in ways that won’t surface until production
  • The dangerous gap: Juniors who never had the lower-level skills in the first place — they’re fluent in the output of AI tools without having the model to validate that output

Junior Devs: The Risk Is Steeper Than You Think

The trap isn’t that AI tools produce bad code. The trap is that they produce plausible code — code that passes tests, code that reviewers skim past, code that runs fine until a Tuesday at 2am when it absolutely doesn’t. If you’re junior and you don’t understand what you shipped, you have no starting point when that happens. You’re not debugging anymore, you’re spelunking with no map.

Here’s a concrete pattern I’ve seen cause real pain. Copilot loves generating this async/await structure when you ask it to “fetch user data and handle errors”:

async function getUserData(userId) {
  try {
    const response = await fetch(`/api/users/${userId}`);
    const data = await response.json();
    return data;
  } catch (err) {
    console.error(err);
    // returns undefined silently — caller never knows it failed
  }
}

// somewhere else in the codebase:
const user = await getUserData(123);
console.log(user.name); // TypeError: Cannot read properties of undefined

The happy path works perfectly. QA signs off. You ship. Then a flaky network request hits, the catch block eats the error, the function returns undefined, and whatever called it explodes in a completely different file with a completely unrelated-looking error message. The fix is two lines — either re-throw the error or return a Result type — but if you accepted this code without reading it, you don’t know that. You don’t even know where to look. That’s the gap AI tools create when used as a replacement for understanding rather than a supplement to it.

What I’d tell a junior on my team is this: AI is an incredibly good second pair of eyes on code you already wrote. Use it to catch what you missed, to suggest a better data structure, to point out the edge case you forgot. Don’t use it to write the thing you were about to learn. The moment you let it skip the struggle, you’ve skipped the part that sticks. There’s no shortcut to the mental model — you need to have written 50 buggy async functions yourself before “error handling in async code” becomes an instinct rather than something you have to consciously remember.

The interview problem is real and nobody talks about it honestly enough. Whiteboard rounds, take-home challenges, live Leetcode sessions — they still exist at most companies, and the muscle memory you build copy-pasting AI output doesn’t transfer to a blank editor with an interviewer watching. I’ve interviewed candidates who could ship features with Copilot turned on and couldn’t reverse a linked list or explain why their own SQL query used a subquery instead of a join. That’s not an AI problem, that’s a “I never actually learned the thing” problem that AI made easier to hide. The reckoning happens in rooms where autocomplete isn’t available.

The rule I’d make non-negotiable: if you cannot explain every single line of AI-generated code out loud, in plain English, to another developer, don’t ship it. Not “roughly explain.” Not “I think this part does X.” Every line. What does this regex actually match? Why is this Promise.all instead of sequential awaits? What happens if this array is empty? If you hit a line you can’t explain, that’s not a shipping blocker — that’s a learning task. Go figure out that line. Read the MDN docs, trace through the logic, write a unit test that exercises it. That process is the job. The AI can help you get there faster, but it cannot do it for you.

How I’ve Adjusted My Workflow to Not Get Lazy

The most useful habit I’ve built isn’t about using AI better — it’s about deliberately using it worse, on purpose, at specific moments. The “write it first” rule sounds obvious until you realize how many times you’ve opened Cursor, typed a comment describing what you want, and just let the suggestion fill in. I catch myself doing this constantly. Now for anything non-trivial — a custom hook, a query optimizer, a state machine — I close the suggestion panel and write my version first. Then I compare. Sometimes the AI version is better and I learn something specific. Sometimes mine is better and I have proof I didn’t need the crutch. Either way, I’ve actually thought through the problem instead of just accepting the first plausible-looking output.

Turning off autocomplete in blocks has been the single biggest skill-preservation move I’ve made. Two hours, no suggestions, when I’m learning something new. I’m currently doing this with Rust’s borrow checker and before that with Go’s concurrency model. The friction is the point. When autocomplete is live, I never have to hold the syntax in my head — the tool fills the gap before my brain even registers there was one. To toggle in Cursor specifically:

# In Cursor on macOS
Cmd+Shift+P → type "Cursor: Toggle Copilot" → Enter

# Or add a keybinding in keybindings.json:
{
  "key": "ctrl+shift+a",
  "command": "editor.action.inlineSuggest.toggleAutomaticAppearance"
}

I actually use this. Not as a productivity flex — because going from 30 autocomplete accepts per hour to zero is genuinely painful the first few sessions. But after a week of 2-hour blocks without it while learning a new framework, I retain the APIs in a way that just doesn’t happen when the tool is constantly finishing my sentences. The cognitive load that feels annoying is the load that builds memory.

Using AI for code review after writing is a completely different relationship than using it as a co-pilot while writing. When I paste finished code and ask “what are the edge cases I missed?” or “is there a more idiomatic way to do this in Python 3.12?”, I’m stress-testing my own understanding. When I let it autocomplete while writing, I’m outsourcing the thinking. The review-after workflow also catches something the co-pilot mode never does: my own bad assumptions. The AI doesn’t know what I was trying to do, so when it suggests a different approach, it’s often surfacing a conceptual gap I didn’t know I had. This is the mode where I’ve learned the most from these tools, not the autocompletion.

The “things I looked up” note is embarrassingly simple and I wish I’d started it earlier. I keep a plain markdown file called lookup_log.md and every time I ask the AI something, I log it with a date. Three entries with the same question — like “how does useEffect cleanup work again” or “postgres LATERAL join syntax” — and I force myself to actually memorize it. Not because memorizing syntax is inherently noble, but because if I’m reaching for the same answer repeatedly, that gap is slowing me down in ways I can’t always see. The log also shows me patterns: I kept asking about async generator syntax in Python for six weeks straight. That told me I needed to actually sit down with the docs for an hour, not keep delegating the recall to a chat window.

  • Write before prompting — for anything that takes more than 10 minutes, write your version first. The comparison is where the learning happens.
  • Block autocomplete when learning — not permanently, just during active skill acquisition phases. Two hours is enough to feel the difference.
  • Review mode, not co-pilot mode — paste your finished code and ask for critique, not for completion.
  • Log repeated questions — three hits in the log means you need to internalize it, not ask a fourth time.

When AI Tools Actively Make You Better

The generator function moment is the one I keep coming back to. I had a utility that paginated through API results — my instinct was a while loop with a cursor variable. GitHub Copilot suggested a generator instead. I didn’t take it blindly; I read it, asked Claude to explain the tradeoff, then rewrote it myself from scratch to make sure I actually understood it. That’s the difference between AI as a crutch and AI as a teacher. The tool showed me a pattern I wouldn’t have reached for, and I deliberately made myself absorb it rather than just accepting the output.

# What I would have written
def fetch_all_pages(endpoint, params):
    results = []
    cursor = None
    while True:
        data = call_api(endpoint, {**params, "cursor": cursor})
        results.extend(data["items"])
        cursor = data.get("next_cursor")
        if not cursor:
            break
    return results

# What Copilot suggested — and what actually taught me something
def fetch_all_pages(endpoint, params):
    cursor = None
    while True:
        data = call_api(endpoint, {**params, "cursor": cursor})
        yield from data["items"]   # caller controls consumption, memory stays flat
        cursor = data.get("next_cursor")
        if not cursor:
            return

The second version is lazy. If the caller only needs the first 50 items, it stops after 50 items. The first version fetches everything into memory regardless. I knew generators existed. I just didn’t think to reach for them there. That’s a real skill gap the AI exposed without me having to fail in production first.

Finishing projects matters more than I gave it credit for for a long time. The learning curve on any project front-loads the familiar stuff — setting up the repo, wiring the first endpoints — and back-loads the hard parts: error handling, edge cases, deployment. Most abandoned side projects die exactly where the interesting problems start. Getting unstuck in 20 minutes instead of 3 days means I actually hit those hard parts now. The last four personal projects I shipped all taught me something about production I hadn’t seen before. The 15 projects before that mostly didn’t, because I never got there.

The code review angle is underrated. I don’t use AI to replace human review — I use it as a pre-pass before I bother my colleagues. My specific prompt is usually something like: “Here’s a function that processes user payment data. What input combinations could cause it to behave unexpectedly, and are there any places this could fail silently?” Claude caught a case last month where I was swallowing a KeyError inside a dict comprehension and returning an empty list instead of surfacing the error. It was exactly the kind of thing that would’ve made it through a human review because reviewers read for logic, not for silent failure modes.

The occasional-language problem is real and nobody talks about it honestly. I write Go maybe three times a year — enough to remember the mental model, not enough to remember whether error wrapping is fmt.Errorf("context: %w", err) or errors.Wrap(err, "context") without looking it up (it’s the former in stdlib, the latter in pkg/errors, and yes that distinction has burned me). Before AI tools I’d spend the first two hours of any Go task re-learning syntax I used to know. Now I spend those two hours on the actual problem. That’s not dependency — that’s appropriate tool use for a polyglot environment. I still understand what the code does. I’m just not wasting cognitive budget on retrieval tasks that a language server can handle better than my six-month-old memory.

A Practical Framework: When to Use AI vs. When to Struggle

Stop Treating AI Like a Binary Choice — It’s a Throttle, Not a Switch

The most useful mental model I’ve landed on after a year of daily Copilot and Claude usage: AI assistance exists on a spectrum, and the skill is knowing where to set the dial for each task, not whether to open the tab at all. Most debates about AI and developer skill frame it as “use it or don’t” — that’s the wrong frame. The real question is at what cost are you accepting the output, and whether that cost is worth it right now.

Use It Freely — But Be Specific About What “Freely” Means

There’s a category of work where AI just removes friction with zero skill penalty. I use Copilot without hesitation for these:

  • Boilerplate: Express middleware setup, Dockerfile starters, GitHub Actions workflows I’ve written a dozen times. The knowledge is already in my head — I’m just not interested in retyping it.
  • Docs you’ve already internalized: If you understand Array.reduce() but blanked on the accumulator parameter order, asking AI is no different than cmd+clicking into IntelliSense. You can verify the answer instantly.
  • Test scaffolding: Generating the describe/it structure, mocking imports, setting up fixtures. The thinking is in designing what to test — AI can wire the plumbing.
  • Syntax you know but can’t recall: Bash parameter expansion, regex lookaheads, Python’s __slots__ syntax. You understand the concept; you just want the exact tokens.

The common thread: you could verify every line of the output in under 30 seconds. If you can’t, you’ve drifted into the next category.

Use It Carefully — This Is Where Most People Get Sloppy

Debugging is the most dangerous place to over-rely on AI, and I say that from experience burning 90 minutes on a problem I made worse by pasting error messages into Claude before thinking. My rule now: form a hypothesis first, then use AI to pressure-test it. If you hand the AI a stack trace and say “fix this,” you’re skipping the part of debugging that actually builds skill — the causal reasoning. Use it like a rubber duck that can read source code, not like an oracle.

Unfamiliar algorithms are the other minefield. If you’ve never implemented a consistent hash ring or a skip list and you ask AI to generate one, you’ll get something that looks correct and probably compiles. The problem is you have no frame to evaluate it. I’ve caught subtle bugs in AI-generated tree traversals that I only spotted because I drew the structure on paper first. The workflow that actually works: read the algorithm from a primary source (a paper, a textbook chapter, a well-sourced Wikipedia article), implement a naive version yourself, then ask AI to review or optimize it. That sequence keeps the learning intact while still saving time.

# Wrong workflow (skill-destroying):
"Write me a Bloom filter implementation in Go"
# → paste, ship, never understand it

# Right workflow (skill-preserving):
# 1. Read the bit array + hash function mechanics yourself
# 2. Write your own version, even if it's ugly:
#    - Initialize the bit array
#    - Pick k hash functions (FNV + offset is fine to start)
#    - Implement Add() and MayContain()
# 3. Then ask: "Here's my Bloom filter — what am I missing for production use?"
# → AI fills gaps in your understanding, not the understanding itself

Don’t Use It — Hard Stops That Aren’t Negotiable

Three situations where I just close the AI tab entirely:

  • Deliberately learning something new: If the point of the task is to build a mental model — a new language, a new paradigm, a data structure you’ve never touched — AI assistance short-circuits the thing you’re trying to do. Struggle is the mechanism. Bypassing it means you’ll be in exactly the same position next time the topic comes up.
  • Interviews and technical assessments: Beyond the obvious ethical issue, using AI during practice interviews means you’re rehearsing the wrong skill. You’re training yourself to prompt instead of to reason, which fails you the moment the whiteboard appears.
  • When you can’t verify the output: If you don’t understand the domain well enough to spot a wrong answer, you’re not using AI as a tool — you’re outsourcing judgment you don’t have yet. This is how production incidents happen. I’ve seen junior devs ship AI-generated SQL with an implicit Cartesian join because nothing in the result set looked obviously wrong until load hit.

The Real Red Flag: Anxiety When the Tool Is Gone

Here’s the tell I’ve started watching for in myself and in others: notice how you feel when the AI is unavailable. Cursor is down, you’re on a plane, your company’s proxy blocks the API. If your reaction is mild inconvenience, you’re fine — you’re using it as acceleration. If your reaction is something closer to panic, a genuine drop in confidence about your ability to do your job, that’s dependency, and it’s worth taking seriously. I had this moment about eight months into daily AI use when GitHub’s Copilot had a 2-hour outage and I caught myself staring at a blank function signature feeling stuck on code I’d written a hundred times before. That was the signal. I spent the following two weeks deliberately coding without autocomplete for the first two hours of each day — not as a purity exercise, but as maintenance on the underlying skill. The same way pilots do manual approaches periodically so instrument dependency doesn’t quietly hollow out their ability to fly.

My Honest Verdict After 2 Years of Daily Use

Two years of daily Copilot, Cursor, and Claude use has landed me somewhere uncomfortable: I ship more than I ever have, and I catch myself struggling with things I used to do cold. Both of those are true simultaneously, and I think anyone who tells you otherwise is selling something.

The output boost is real and I won’t pretend it isn’t. I’m generating first drafts of API integrations, boilerplate, test scaffolding, and regex patterns in a fraction of the time. A task that used to take me 45 minutes of context-switching and Stack Overflow archaeology now takes 8. But the fundamentals tax is also real — I noticed it when I had to debug a gnarly memory leak in a service where AI suggestions were actively misleading me, and I had to fall back on first principles I hadn’t exercised in months. The muscle was there, but softer than I’d like.

The developers I’ve seen come out genuinely ahead share one trait: they already knew what good code looked like before they adopted these tools. They use AI output as a first draft to interrogate, not a solution to accept. The ones who struggle are the ones who learned to code alongside heavy AI use without building the underlying mental models first. When the AI confidently generates a race condition or a SQL injection vector, a senior dev spots it in 10 seconds. A junior who skipped the fundamentals ships it.

My hiring filter has quietly shifted because of this. I’d take a developer who pushes back on AI output, asks “why did it generate it this way,” and occasionally throws the suggestion away over someone who treats acceptance rate as a productivity metric. Skepticism about AI output is a proxy for understanding the domain. Blind trust is a proxy for not having learned it yet. The interview tells you everything — ask them to walk through a piece of AI-generated code and explain what they’d change. The answer is more signal than any LeetCode score.

Refusing to use these tools out of some principled stance about “real programming” is just cope at this point. I’ve seen it. The people making that argument are shipping slower, and their competitive position is eroding. The question that actually matters is how you stay sharp while using them — and my answer has been deliberate constraint. I write certain classes of code by hand, always. Core data structures, anything security-adjacent, performance-critical loops. Not because AI can’t do it, but because those are the reps that keep me dangerous when the AI gets it wrong.


Disclaimer: This article is for informational purposes only. The views and opinions expressed are those of the author(s) and do not necessarily reflect the official policy or position of Sonic Rocket or its affiliates. Always consult with a certified professional before making any financial or technical decisions based on this content.


Eric Woo

Written by Eric Woo

Lead AI Engineer & SaaS Strategist

Eric is a seasoned software architect specializing in LLM orchestration and autonomous agent systems. With over 15 years in Silicon Valley, he now focuses on scaling AI-first applications.

Leave a Comment