cd /news/ai-tools/when-not-to-use-github-copilot-an-ex… · home › topics › ai-tools › article
[ARTICLE · art-160821] src=dev.to ↗ pub= topic=ai-tools verified=true sentiment=· neutral

When Not to Use GitHub Copilot: An Experienced Developer's Checklist

An experienced developer published a checklist arguing that GitHub Copilot should be declined wherever the person accepting a suggestion cannot verify it, summarized by the rule "Never accept code you cannot judge." The writeup identifies five high-risk situations, including working in unfamiliar domains and security-sensitive code such as authentication and cryptography, and shows how a Copilot-style webhook signature check using `===` leaks timing information that enables byte-by-byte signature guessing, versus a constant-time `crypto.timingSafeEqual` version.

by read8 min views1 publishedOct 30, 2026

A while ago I accepted a Copilot suggestion that looked exactly right. It was idiomatic, it compiled, it passed code review. Weeks later I found out it was subtly wrong, in a way that never showed up in tests and only surfaced under real conditions.

That bug changed the question I ask about Copilot. Not “is it good?” It clearly is, for a lot of work. The better question is: where does it not belong?

This article is my answer. Not a list of hot takes, but a checklist of situations where I have learned to decline the suggestion, and the single rule that connects them.

Every situation in this article is a variation of one rule:

Never accept code you cannot judge.

Copilot produces plausible code. Plausible is not the same as correct, and the gap between the two is invisible in the editor. The only thing standing between a plausible suggestion and your main branch is your ability to verify it.

That means Copilot’s usefulness is not a property of the tool. It is a property of the situation. The same suggestion can be a time-saver for one developer and a liability for another, depending entirely on whether the person accepting it can tell right from almost-right.

Keep that lens in mind. Each of the five situations below is a place where verification ability drops, the cost of being wrong rises, or both.

The most tempting time to lean on Copilot is when you are working in a domain you don’t know well. A new language, an unfamiliar framework, a protocol you have never implemented.

It is also the worst time.

In a domain you know, Copilot is an accelerator. You recognize correct code on sight, so you accept good suggestions in seconds and dismiss bad ones just as fast. Verification is nearly free.

In a domain you don’t know, the economics invert. Copilot produces plausible code much faster than you can build the knowledge needed to evaluate it. You end up with working-looking code and no way to judge it. The suggestions all look reasonable, because “looks reasonable to a non-expert” is precisely what a language model is optimized to produce.

This is how you ship code that is wrong in ways you don’t have the vocabulary to detect yet. Concurrency code with race conditions you can’t see. Database queries that work until the table grows. API integrations that ignore an edge case the documentation warns about on a page you never read.

The practical takeaway: in unfamiliar territory, read the documentation and write the first version yourself, slowly. Use Copilot once you know enough to disagree with it. If you can’t imagine rejecting a suggestion, you are not qualified to accept one.

Authentication, cryptography, input handling. This is the worst-case combination: the stakes are high and the failure modes are invisible.

Here is a suggestion very much like ones Copilot produces for webhook verification:

function verifyWebhookSignature(payload, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(payload)
    .digest("hex");
  return signature === expected;
}

This code looks careful. It uses the right algorithm, derives the expected signature correctly, and compares the two values. It would pass most code reviews.

It is still wrong. The === comparison short-circuits: it returns as soon as it finds the first byte that differs. That means the comparison takes measurably longer the more leading characters an attacker gets right, which turns your response time into an oracle for guessing valid signatures byte by byte.

The correct version uses a constant-time comparison:

function verifyWebhookSignature(payload, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(payload)
    .digest();
  const provided = Buffer.from(signature, "hex");
  if (provided.length !== expected.length) return false;
  return crypto.timingSafeEqual(provided, expected);
}

Notice what happened here. The flaw was not a syntax error or a missing null check, the categories of mistake reviewers are trained to spot. It was a domain-specific property of the comparison operator that only matters in an adversarial context.

Copilot is trained on public code, and most public code gets this wrong. In security-sensitive contexts, the statistically common answer is often the vulnerable one. That is exactly the code Copilot has learned to suggest with confidence.

For auth, crypto, session handling, and input sanitization: write it yourself against the official documentation, or use a well-audited library. If Copilot contributes anything here, treat it as untrusted input.

Not every Copilot failure is a bug. Some of the most expensive ones are code that works perfectly and never should have been written.

Copilot optimizes for a locally complete answer: a self-contained block that fully handles the prompt in front of it. It does not optimize for the simplest answer, and it does not know what your codebase already contains.

Ask for “a function that deduplicates items by a key” and you often get something like this:

function deduplicateByKey(items, key, options = {}) {
  const { keepLast = false, comparator = null } = options;
  const seen = new Map();
  for (const item of items) {
    const k = item[key];
    if (comparator) {
      let found = false;
      for (const existing of seen.keys()) {
        if (comparator(existing, k)) { found = true; break; }
      }
      if (found && !keepLast) continue;
    }
    if (!seen.has(k) || keepLast) {
      seen.set(k, item);
    }
  }
  return Array.from(seen.values());
}

The version an experienced developer writes:

function uniqueBy(items, key) {
  return [...new Map(items.map(i => [i[key], i])).values()];
}

The generated version is not wrong. It handles options nobody asked for, supports a comparator no caller will ever pass, and adds a code path that will never be tested. Every accepted suggestion like this raises the maintenance cost of the codebase a little. Multiply by hundreds of suggestions across a team and a year, and you have a codebase that is significantly harder to read, test, and change, without a single bug to point at.

The takeaway is a habit: when a suggestion is longer than you expected, that is a signal to , not to be impressed. Ask what the simplest version looks like, and whether the function should exist at all.

Here is the risk I see discussed least: not the quality of the suggestions, but what they do to the person reviewing them.

When a junior colleague submits code, you read it critically. You expect mistakes, so you look for them. The rough edges in the code, the slightly-off naming, the hesitant structure, all of it keeps your skepticism engaged.

Copilot output has none of those signals. It arrives fluently formatted, idiomatically named, confidently structured. It looks like code written by someone who knew what they were doing. And so you read it the way you read a senior colleague’s code: skimming for structure rather than checking for correctness.

This is backwards. The generated code deserves more scrutiny than the junior colleague’s code, not less. The junior developer at least understood the requirements, ran the code, and had something at stake. The model did none of those things. Fluency is a property of the training data, not evidence of correctness.

The failure compounds at the pull request level. A PR full of generated code reads smoothly, so it reviews quickly, so more of it gets merged, so reviewers see even more fluent code and calibrate their attention even lower.

The countermeasure is simple to state and genuinely hard to practice: review generated code as if a stranger wrote it. Because one did.

Some situations are not dangerous at all. Copilot is just slower than doing the work yourself, and it is worth recognizing them so you stop paying the overhead.

Every accepted suggestion has a hidden loop attached: prompt, read, verify, fix. For boilerplate, that loop is cheap and the typing saved is large. But three kinds of tasks reliably invert the math:

None of these are failures of the model. They are situations where verification costs more than generation saves. Noticing them is not an anti-AI stance. It is the same judgment you apply to any tool.

The five situations reduce to one judgment call, made on two axes: how well can I verify this output, and what does it cost if I’m wrong?

Reading the quadrants:

Before accepting a nontrivial suggestion, the whole rule fits in one question: if this is subtly wrong, would I catch it, and what happens if I don’t?

The bug from the introduction was never really a Copilot problem. It was a verification problem. I accepted code faster than I could judge it, and the tool did exactly what it was built to do: produce something plausible.

Knowing when to decline AI assistance is the same skill as knowing when a green test suite actually proves something, or when a code review approval means “verified” versus “looks fine.” It is quality engineering judgment applied to a new source of code.

Copilot did not change the fundamentals. Every line in your codebase is one you own, whoever or whatever wrote it. The developers getting the most out of these tools are not the ones who accept the most suggestions. They are the ones who know, before the tab key goes down, whether they are in a situation where they can tell right from plausible.

That judgment is the checklist. The five situations are just the places where it matters most.

This essay first appeared on my blog, where I write about what AI-written code does to tests and code review.

── more in #ai-tools 4 stories · sorted by recency
── more on @github copilot 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/when-not-to-use-gith…] indexed:0 read:8min 2026-10-30 · —