GitHub And Open Source Collaboration
GitHub Copilot In The IDE And Website
Use GitHub's official documentation for current product behavior. GitHub documents Copilot in your IDE, Copilot Chat, code review with Copilot, and content exclusions for organizations. Model versions, feature availability by plan, and exact settings screens change frequently; the stable lesson is how to treat the output, not which button enables which feature this month.
Where Copilot Shows Up
- Inline completions in the IDE — ghost text suggesting the next few lines as you type, based on the current file and open tabs as context.
- Copilot Chat in the IDE — conversational, can explain code, suggest a fix for a specific error, or generate a function from a description, with more explicit context than inline completions.
- Copilot Chat on github.com — can answer questions grounded in a specific repository's code, issues, and discussions, useful for orienting in an unfamiliar codebase.
- Copilot code review — can leave automated review comments on a pull request's diff, flagging potential issues the way a human reviewer might.
All four share the same underlying model behavior: they generate statistically plausible text conditioned on context. None of them "know" your business rules, your production incidents, or facts about the world beyond what's in the context window and training data — they produce output that pattern-matches to what a correct answer usually looks like, which is different from actually being correct.
What It's Reliably Good At
Copilot's suggestions are strongest exactly where the pattern-matching nature helps rather than hurts:
- Boilerplate with a clear local pattern — a getter/setter, a repeated array-mapping shape, a test scaffold that mirrors three tests already in the file.
- Translating a known algorithm into PHP syntax — "sort this array of objects by a computed key" has a standard shape Copilot has seen many times.
- Explaining unfamiliar code — asking Chat "what does this regex do" is low-risk because you're using it as a starting point for your own verification, not as an unverified source of truth.
- Generating a first draft to edit, when you already know roughly what correct looks like and can spot deviations quickly.
What It's Unreliable At — And Why That's Structural, Not A Bug
- Business rules and domain-specific correctness. Copilot cannot know that your application's tax calculation has a specific rounding rule mandated by a regulator, or that
Order::cancel()must never run after a shipment webhook fires. It will confidently generate code that looks like a normal implementation and violates rules it has no way of knowing about. - API surfaces that don't exist. Asking for "the Laravel method that does X" can produce a method name that sounds right — matches Laravel's naming conventions, plausible signature — and doesn't exist, or existed in a different major version. This is not a rare glitch; it's the direct consequence of a model trained to produce plausible-looking text generating an answer for a case it doesn't actually have information about.
- Security-sensitive code. Authentication, cryptography, input sanitization, and access-control logic are exactly the places where "looks right" and "is right" diverge most dangerously, and where a subtle mistake (a timing side-channel, a missing check on one code path) is easy to miss in review because the surrounding code looks so plausible.
- Licensing. Suggestions can echo patterns from training data closely enough to raise real licensing questions for code meant to be redistributed under a specific license — a concern GitHub's own content-exclusion and filtering features exist to partially address, not eliminate.
The Review Discipline That Makes This Safe
None of the above means Copilot is unsafe to use — it means Copilot-generated code needs the same standard of review as code from an unfamiliar contributor, every time, regardless of how confident it looks:
- Read every line before accepting. Accepting a multi-line suggestion without reading it is the single habit most responsible for shipping bugs — the suggestion looking plausible is not evidence it's correct.
- Verify anything that names an API, library method, or configuration option. If Copilot names a method, confirm it exists in the version you're using — checking the actual documentation, not asking Copilot to confirm its own suggestion.
- Write or extend tests for what was accepted, especially edge cases — empty input, boundary values, concurrent access — that a pattern-matched suggestion is disproportionately likely to have gotten wrong, because those are exactly the cases where "looks like normal code" and "handles this case correctly" diverge.
- Treat Copilot code review comments as one more reviewer's opinion, not a gate. A Copilot review comment can be wrong or miss context a human reviewer would have; it's a useful second pair of eyes, not a substitute for human sign-off on a pull request.
- Never let it touch secrets or credentials directly. Don't paste API keys or production credentials into a chat prompt for context, and configure content exclusions for files that legitimately contain sensitive configuration, so they're never sent as context in the first place.
Organizational Controls
For a team or organization, three settings turn the above from individual discipline into a floor everyone operates above:
- Content exclusions — configure paths (a
secrets/directory, a vendor directory with a restrictive license) that Copilot should never read as context, enforced centrally rather than left to individual awareness. - A stated policy on what needs human review — most organizations don't ban AI-assisted code, but many require it to go through the same PR review as anything else, with no fast path for "Copilot wrote it so it's already reviewed."
- Audit logging, where available, to understand usage patterns — not to police individuals, but to know whether the org's policy (e.g., "no accepting suggestions in files under
src/Payment/") is actually being followed.
Failure Modes
The most common failure is skipped verification because the output looked polished — well-formatted, idiomatic-looking code lowers a reviewer's guard exactly when they should be checking hardest, since polish is a property of the model's training, not of correctness.
The second is treating a hallucinated API as real until a runtime error proves otherwise — a plausible-but-wrong method name often passes a quick visual review and fails only when actually executed, which means it can survive into a PR if the author didn't run the code.
The third is using Copilot Chat as a source of truth for facts instead of a drafting tool — asking "does this comply with GDPR" or "is this the current recommended way to hash passwords in PHP" and accepting the answer without checking current documentation treats a plausible-sounding answer as an authoritative one.
Review Checklist
- The pull request shows evidence the author read and understood the suggestion, not just accepted it — via tests, edge-case handling, or comments explaining a nontrivial choice.
- Any named API, method, or library behavior in AI-assisted code has been checked against actual current documentation, not assumed correct.
- Security-sensitive code (auth, crypto, input validation, access control) received the same or greater review scrutiny regardless of whether it was AI-assisted.
- Tests exist for edge cases, not just the happy path the suggestion most likely handled well.
- Organizational content exclusions cover any directories with secrets or restrictively-licensed code.
- Copilot-authored review comments on this PR were evaluated on their merits, not treated as a substitute for a human reviewer's approval.
What You Should Be Able To Do
After this lesson, you should be able to explain what makes Copilot's suggestions reliable versus unreliable, apply the same review standard to AI-assisted code as to any other contributor's code, verify a suggested API against real documentation before trusting it, and set up organizational content exclusions and review policy so individual discipline isn't the only safeguard.
Practice
Review a Copilot suggestion
function isValidResetToken(string $submittedToken, string $storedToken): bool
{
return $submittedToken == $storedToken;
}
It looks correct, reads clearly, and passes a quick manual test where the developer typed in the right token and got true back.
Review this suggestion and answer:
- what's wrong with it, specifically — not "AI code is risky" in general, but the concrete defect in this function;
- why a quick manual test ("type the right token, get
true") would not catch the problem; - the fix, and why it's the right one;
- what a reviewer should have flagged in the pull request, if this had shipped as-is.
Show solution
Review
The specific defect: == is a loose comparison, and comparing two strings with == is usually fine — until one of them can look like a number. PHP's loose comparison has historically had surprising numeric-string coercion behavior ("0e12345" == "0e67890" is true, because both are treated as 0 in scientific notation). If the token generation ever produces a string that looks like "0e..." followed by digits — which is exactly the kind of value a hex or base-encoded random token can accidentally produce — an attacker who can get the server to compare against a token starting with "0e" followed by digits can potentially match without knowing the real token. Separately, and just as importantly: this comparison is not constant-time. String comparison in PHP short-circuits on the first differing byte, so the time it takes to reject a wrong guess leaks information about how many leading characters were correct — a timing side-channel that, given enough attempts, can help an attacker guess the token faster than brute force alone would allow.
Why the manual test doesn't catch it: typing the correct token and confirming true only exercises the true-positive path. Both defects — the type-juggling edge case and the timing side-channel — only manifest with an incorrect token under specific conditions (a magic-hash-shaped guess, or a timing measurement across many attempts). A suggestion that "looks right" and passes the one test a developer intuitively reaches for is exactly the failure mode this lesson's article describes: plausible output that's wrong in a way that's easy to miss because the happy path works.
The fix: use hash_equals(), which PHP provides specifically for this purpose — a constant-time string comparison that also does not perform loose type coercion.
<?php
declare(strict_types=1);
function isValidResetToken(string $submittedToken, string $storedToken): bool
{
return hash_equals($storedToken, $submittedToken);
}
var_dump(isValidResetToken('abc123', 'abc123'));
var_dump(isValidResetToken('wrong', 'abc123'));
// Prints:
// bool(true)
// bool(false)
hash_equals() takes the known/stored value first and the user-supplied value second — the fix doesn't just swap the operator, it uses the function designed for exactly this comparison, which is the durable lesson: security-sensitive comparisons (tokens, password hashes, HMACs) should almost never use == or === directly, because "are these two strings equal" and "are these two strings equal in a way that's safe against timing attacks and type coercion" are different questions.
What a reviewer should have flagged: any comparison involving a security token, secret, or hash should be an automatic pause point in review — "is this comparison safe against timing attacks and type juggling?" — regardless of how confidently the surrounding code reads or whether it came from Copilot, a search result, or a human teammate. This is precisely the class of code the article calls out as one where Copilot's plausible-looking output is least trustworthy, and where review scrutiny should go up, not down.
Diagnose an accepted hallucination
$timeout = array_get_or_warn($config, 'timeout', 30);
It looks like a real PHP standard-library function — the naming matches PHP's array_* conventions closely (array_key_exists, array_column, array_search). The code passed a quick visual review from a teammate who also assumed it was a real function they simply hadn't used before, and it merged. The next deploy fails immediately:
PHP Fatal error: Uncaught Error: Call to undefined function array_get_or_warn()
Diagnose the failure and write a plan covering:
- the first three pieces of evidence to inspect;
- the false assumption both the author and the reviewer made, and why it was an easy assumption to make;
- how to reproduce or confirm this class of problem before it reaches production, rather than after;
- the durable fix — both for this specific line and for the process that let it merge.
Show solution
Diagnosis
First three pieces of evidence:
- The error message itself —
Call to undefined function array_get_or_warn()is unambiguous: this function does not exist anywhere in the loaded code (PHP core, installed extensions, or the project's own autoloaded classes/functions). That immediately rules out "it's defined somewhere I haven't found" and confirms this is a nonexistent-API problem, not a namespacing or autoload issue. - The PHP manual / a
function_exists()check — confirming there is noarray_get_or_warnin PHP's function list at any supported version rules out "maybe it's from a newer PHP version than we're running." - The pull request and its review — checking whether tests were run locally before merge, and what the reviewer actually looked at, tells you where the process broke: did the author run the code, or trust the suggestion on sight? Did the reviewer read for logic, or skim for style? This is the evidence that fixes the process, not just this line.
The false assumption: both the author and the reviewer assumed that a function name matching PHP's naming conventions (array_*, snake_case, parameter order that looks idiomatic) is evidence the function exists. It's an easy assumption to make precisely because it's usually a reliable signal for hand-written or copy-pasted code — a human copying from documentation or an IDE's autocomplete rarely invents a plausible-sounding nonexistent function, because autocomplete and documentation both constrain them to real APIs. Copilot has no such constraint: it generates the shape of a plausible PHP standard-library call because that shape is statistically common in its training data, independent of whether that specific function exists. The naming convention that would normally be a trust signal is, for AI-generated code, no signal at all.
Reproducing before production: the fastest catch is simply running the code locally, or letting CI run it — php -l (lint) would not have caught this, since array_get_or_warn(...) is syntactically valid; but actually executing the code path (a unit test that calls whatever function reads this config value) would have failed immediately with the same fatal error, in a local run or CI job instead of a production deploy. This is the general lesson: linting checks syntax, tests check behavior, and a hallucinated function name is a behavior problem that only running the code surfaces.
Durable fix — this line: replace with real PHP, which for "get with fallback, warn if missing" is a few lines of actual standard-library calls:
<?php
declare(strict_types=1);
function configValueOrWarn(array $config, string $key, mixed $default): mixed
{
if (!array_key_exists($key, $config)) {
trigger_error("Missing config key: {$key}, using default.", E_USER_WARNING);
return $default;
}
return $config[$key];
}
$timeout = configValueOrWarn(['timeout' => 30], 'timeout', 60);
echo $timeout;
// Prints:
// 30
Durable fix — the process: the specific rule worth adopting is that any suggested function or method call the author or reviewer hasn't personally used before gets a one-second verification — function_exists(), a manual search, or just running the code — before merge. This costs almost nothing when the function is real, and it's the only thing that would have caught this before the deploy. Pairing that with "run the code, don't just read it" as a stated PR expectation addresses the root cause: this shipped because nobody actually executed the line between accepting the suggestion and merging the PR.
Design an AI-assisted review policy
Your team has adopted Copilot broadly — inline completions, Chat, and code review comments are all enabled for everyone. So far there's no written policy; usage and review standards vary developer to developer. Leadership has asked for a short, practical policy (not a lecture on AI risk) that the team will actually follow.
Write a policy covering:
- what, if anything, is off-limits for Copilot to see as context (and how that's enforced, not just requested);
- whether AI-assisted code needs anything beyond the team's normal PR review, and for which categories of code specifically;
- a rule for verifying suggested APIs/methods before they merge;
- how Copilot's own automated review comments on PRs should be treated relative to a human reviewer's approval;
- what you are explicitly not going to do (to keep the policy practical rather than exhaustive).
Show solution
1. Content exclusions — enforced, not requested. Configure organization-level content exclusions for: any directory holding credentials or secrets even in example/template form (.env*, secrets/), and any vendored code under a license incompatible with the project's own. This is set at the org/repository level in GitHub's Copilot settings, not left as a per-developer reminder — a policy that depends on everyone remembering not to paste secrets into a chat prompt will fail exactly when someone's in a hurry.
2. Extra review scrutiny for security-sensitive categories, not for everything. Ordinary PR review applies to all AI-assisted code. For a specific, named list — authentication, authorization/access-control checks, cryptography and hashing, payment handling, and anything comparing secrets/tokens — the reviewer must explicitly confirm they read the logic line-by-line rather than skimming, and this is called out in the PR template as a checkbox for those categories only. Applying maximum scrutiny to every single line of every PR isn't practical and trains reviewers to skim everything; concentrating it on the categories where a plausible-looking mistake is most costly is what makes the policy sustainable.
3. Verify suggested APIs before merge. Any PR introducing a call to a function, method, or configuration option the author hasn't personally used before must include either a link to the actual documentation confirming it exists and behaves as expected, or a passing test that exercises it. This is the direct fix for the hallucinated-API failure mode — cheap to do, and it's a rule that's checkable in review ("where's the doc link or test for this call") rather than relying on the author's memory of whether they checked.
4. Copilot's PR review comments are input, not approval. Copilot-generated review comments are visible to human reviewers the same as any other comment, but they do not count toward required approvals, and a human reviewer is expected to form their own judgment rather than defer to or rubber-stamp past a Copilot comment. If Copilot flags something and a human reviewer disagrees, the human's call stands, documented with a one-line reason in the PR if the disagreement is non-obvious.
5. What this policy deliberately does not do: it does not require disclosing which lines were AI-suggested (unenforceable and not clearly useful — the review standard is the same either way), it does not ban Copilot from any part of the codebase outright, and it does not mandate a separate AI-specific review step for routine, low-risk changes (a config typo fix, a test scaffold). Keeping the policy to a handful of concrete, checkable rules — content exclusion enforced at the platform level, extra scrutiny on a named list of categories, a verification requirement for unfamiliar APIs, and clear precedence for human review over Copilot's own comments — is what makes it something the team will actually follow instead of a document nobody reads after the first week.