GitHub Copilot Code Completion Explained

GitHub Copilot FundamentalsLesson 7 of 13Beginner10 min read
Published
Updated
Last technically verified
GitHub Copilot Code Completion ExplainedGitHub Copilot Fundamentals7Beginner/github-copilot/getting-started/code-completion/

Inline suggestions are the layer of Copilot everyone meets first and almost nobody thinks about afterwards. That is a mistake, because it is also the layer where unreviewed code most easily accumulates — one Tab at a time, dozens of times an hour.

This lesson covers what actually drives suggestion quality, how to control accepting and rejecting precisely, and the review habits that keep completion from quietly degrading a codebase.

What inline suggestions are

As you type, Copilot proposes a continuation as greyed-out ghost text ahead of your cursor. Tab accepts it, Esc dismisses it.

Suggestions range from finishing the current expression to producing an entire function body. What arrives depends on where you are: mid-expression you get a completion, on a blank line after a signature you often get a whole implementation.

Single-line and multi-line completion

The two behave differently enough to be worth distinguishing.

Single-line completions finish what you are typing — the rest of a condition, the remaining arguments of a call, the closing of a chained expression. They are low-risk: short, easy to scan, and obviously wrong when they are wrong.

Multi-line completions propose a block — a function body, a loop, a whole test case. These carry the real risk, because a twenty-line suggestion embodies perhaps a dozen decisions it made on your behalf: which error to raise, whether to validate input, what to do with the empty case, whether to mutate or copy. None of those decisions are flagged.

What feeds a suggestion

This is the whole game, and it has nothing to do with prompting.

Copilot’s completion context is assembled from the file you are editing, the code immediately around your cursor, other files open in your editor, project structure and — in editors that support it — a workspace index, plus your custom instructions.

Which produces a rule you can act on immediately: the fastest way to improve completions is to open the right files. Not to phrase anything better. Open the interface definition, the neighbouring module that already implements this pattern, the test file describing expected behaviour.

Write the contract first

The single most effective technique is to give Copilot something to complete against. Compare what these two offer as context.

Weak — the name is the only signal:

def process(data):

Strong — the signature, types and documented contract are all context:

def parse_duration(value: str) -> int:
    """Convert a duration string such as '90s', '5m' or '2h' into seconds.

    Supports the suffixes s (seconds), m (minutes) and h (hours).
    Raises ValueError for an empty string, an unknown suffix,
    a non-numeric quantity, or a negative value.
    """

You did not write a prompt. You wrote a specification, and it is a far better prompt than most people type into a chat box — because it stays in the file, documents the function for the next reader, and shapes every future suggestion that touches it.

Comments-to-code

A comment describing intent will often produce an implementation:

// Debounce the handler so it fires at most once every 300ms,
// with the trailing call preserved.

This works, and it is worth knowing about. It is also the pattern most overstated in Copilot tutorials. A comment is weaker context than a typed signature, because it constrains nothing — no input types, no return type, no error behaviour. Use it for exploratory work; use signatures for real work.

Accepting suggestions

Accepting whole suggestions is the default, and the least precise option available.

Partial acceptance

The technique that separates comfortable users from frustrated ones: you do not have to take the whole thing.

Editors support accepting a suggestion word by word — in VS Code and JetBrains this is bound to a “accept next word” command, and in Eclipse it is Ctrl+ (or Command+ on macOS).

This matters because suggestions are frequently right for the first half and wrong afterwards. Taking the useful part and typing the rest yourself is faster than accepting everything and then deleting, and it keeps you reading.

Rejecting suggestions

Esc dismisses, and simply continuing to type replaces the suggestion with what you actually wanted.

Rejecting is underused. There is a mild pull toward accepting whatever appeared — it is there, and evaluating it takes effort while accepting it takes a keystroke. When a suggestion is close but not right, dismissing and writing two lines yourself is often faster than accepting and correcting, and the resulting code is code you understand.

Cycling alternatives

Copilot usually has more than one candidate. Alt+] and Alt+[ (Option on macOS) move between them.

Cycling is most useful when the first suggestion is structurally wrong rather than superficially wrong — it took an iterative approach and you wanted a recursive one, or it caught a broad exception where you wanted a narrow one. Alternatives often differ in exactly that kind of decision.

You can also open additional suggestions in a separate pane with Ctrl+Enter, which is easier to compare than cycling in place.

Next edit suggestions

A distinct feature, and one that changes how completion feels once you notice it.

Rather than only completing at the cursor, Copilot predicts where your next edit is likely to be needed and proposes it. Rename a parameter and it will often suggest the corresponding updates further down the file. Change a function’s return type and it may propose adjusting the callers.

Completion feature support by IDEVerified August 20, 2026
Completion feature support by IDE. Columns are IDEs; each cell states whether the feature is supported, in public preview, or not supported.
FeatureVS CodeVisual StudioJetBrainsEclipseXcodeNeovim
Code completionSupportedSupportedSupportedSupportedSupportedSupported
Next edit suggestionsSupportedSupportedIn public previewIn public previewIn public previewNot supported

Captured from the latest release of each integration at the time of verification: VS Code 1.108.0, Visual Studio 18.6.0, JetBrains 1.5.66 (extension), Eclipse 0.14.0 (extension), Xcode 0.46.0 (extension), Neovim 1.18.0 (extension). GitHub publishes the feature matrix as a public preview and changes it often.

This is genuinely useful for the mechanical follow-through of a change — the edits you know you need to make and would otherwise make one at a time. It is still a suggestion, and it still needs reading: a proposed update in a distant part of the file is harder to review than one at your cursor, because you have less of its context in mind.

Improving your suggestions

In rough order of impact:

  1. Open the relevant files. Interfaces, the neighbouring implementation, the tests. This does more than anything else on this list.
  2. Write repository custom instructions. State your language version, test framework, error-handling convention and anything a new engineer would get wrong on day one. Write once, benefit permanently, share with the team.
  3. Write the signature and docstring before the body. A stated contract is the best per-function context available.
  4. Use type annotations. They constrain the space of plausible completions.
  5. Name things descriptively. retry_with_backoff produces better suggestions than handle.
  6. Keep files coherent. A file that does one thing gives clearer local context than one that does five.

Notice that none of these are prompt techniques. Completion has no prompt — it has context, and you control it.

Practical examples

Three situations where completion earns its place, and one where it does not.

Repetitive structure

Completion is at its best on code whose shape is fully determined and whose typing is the only cost — mapping between representations, exhaustive switch arms, parametrised test tables:

@pytest.mark.parametrize(
    "value,expected",
    [
        ("30s", 30),
        ("5m", 300),

Type the first two rows and completion will usually propose the rest of the table in the same style. Verify the values; the pattern will be right and an individual arithmetic result may not be.

Configuration formats

YAML, HCL and Dockerfiles are where completion quietly saves the most time, because their syntax is fiddly, their keys are hard to remember, and a neighbouring block gives excellent local context:

services:
  api:
    build: .
    ports:
      - "8000:8000"
    environment:

Following an established pattern

If your repository already contains four handlers written the same way, writing the fifth is where completion is most reliable — the pattern is visible in context, and consistency is exactly what a next-token model is good at.

Where it does not help

Anything where the decision has not been made yet. If you are still working out whether this belongs in the service layer or the model layer, completion will confidently implement whichever one your cursor happens to be in, and the confidence is not evidence.

Limitations

  • It cannot run your code. A suggestion is a guess that has never been executed.
  • It does not know your system. Your production behaviour and institutional decisions are not in the context.
  • It reproduces common patterns, including common mistakes. Patterns abundant in public code — string-built SQL, permissive defaults, outdated crypto — are well represented in what these models learned from.
  • It is confident regardless of correctness. Ghost text looks identical whether it is right or wrong.
  • Library knowledge drifts. A suggestion may use an API that has been deprecated or removed.
  • Output is not reproducible. The same cursor position gives different suggestions between attempts.

Security

Two more habits worth building:

Check suggested imports. Completion will happily import a package that is unmaintained, unnecessary, or simply not the one your team standardised on. The import line is easy to skim past.

Never let a completion re-introduce a secret. If a credential ever existed in the file, a completion may reproduce a plausible variant of it. Keep secrets in environment variables and secret managers:

export EXAMPLE_TOKEN="replace-with-your-token"

Review practices

Three habits, each cheap:

Read before Tab. Accepting and then evaluating is a different cognitive act, and a worse one. Once code is in the file it acquires the authority of something you wrote.

Scale scrutiny with length. A one-line completion needs a glance. A twenty-line one needs the attention you would give a colleague’s pull request.

Read your own diff before committing. git diff catches accepted suggestions that seemed fine in isolation and are wrong in aggregate — duplicated logic, an inconsistent error style, an import nothing uses.

Next steps

Continue to Copilot Chat for the layer where you can state constraints and ask questions, or Agent Mode where Copilot makes changes across files rather than suggesting at your cursor.

For the complete list of documented bindings, see the Keyboard Shortcuts Cheat Sheet.

Sources

Every version-sensitive claim on this page was checked against first-party documentation. Only sources actually used are listed.

Primary sources