GitHub Copilot IDE Workflow: From Idea to Pull Request

Copilot IDE & Developer WorkflowAcademy lesson 25Cluster 2 · Lesson 12 of 12Intermediate20 min readVersion-sensitive
Published
Updated
Last technically verified
GitHub Copilot IDE Workflow: From Idea to Pull RequestCopilot IDE & Developer Workflow12Intermediate/github-copilot/ide/idea-to-pull-request/

Eleven lessons have covered what Copilot does in each editor. This one puts it together: a single feature, from a one-line requirement to a merged pull request, with every step named and every step that is yours marked as yours.

The point of marking them is that the division of labour is the whole skill. People who get little from Copilot usually delegate the wrong steps — and people who get burned by it usually delegate the steps in the middle of this list.

The workflow

Idea to pull request
  1. Understand the requirementHuman judgementDecide what is actually being asked, and what done means.
  2. Explore the code that existsChat to orient: where does this concept already live?
  3. Choose the approachHuman judgementPick the design. Copilot can list options; the choice is yours.
  4. Create a branch and start cleanNothing uncommitted before anything agentic runs.
  5. Set up contextOpen the files that matter; check repository instructions are current.
  6. Write the contractsHuman judgementSignatures, types, interfaces. The strongest constraint you can give.
  7. Generate the implementationCompletion for narrow work, agent mode when scope is unclear.
  8. Read every line you acceptedHuman judgementNot skim. Read. This is where the value is preserved.
  9. Run itBuild, execute, observe. Not the agent's claim that it works.
  10. Write tests against the behaviourWith an existing test file open so conventions match.
  11. Break it deliberatelyConfirm the tests fail. A test that cannot fail is a liability.
  12. Handle the failure pathsErrors, edge cases, concurrency. The part that gets skipped.
  13. Review your own diffHuman judgementIn the diff view, not the editor. See what changed, not what you meant.
  14. Run Copilot code reviewWhere supported; otherwise on GitHub after pushing.
  15. Write the pull request descriptionDraft from the diff; correct the why, which Copilot cannot know.
  16. Respond to human reviewHuman judgementThe conversation about design is not something to delegate.

Steps marked Human judgement are the ones that do not get delegated — they are where you decide whether what Copilot produced is actually right.

Step 1 — Understand the requirement

A ticket says “add rate limiting to the API”. That sentence contains at least five unanswered questions: limited per what, at what threshold, what happens on breach, does it apply to authenticated and anonymous traffic alike, and is it per instance or shared.

Copilot cannot answer any of them. They depend on what your service does, who uses it and what the business has decided.

What Copilot can do here is help you notice the questions:

Copilot promptSurface the ambiguityChat, any IDE

This is the requirement: “add rate limiting to the API”. List the decisions someone has to make before this can be implemented, phrased as questions I could put to the person who wrote it. Do not propose an implementation and do not answer the questions yourself.

That is a legitimate use — it produces a checklist you take to a human — and it is meaningfully different from asking for the code.

Step 2 — Explore the code that exists

Before designing anything, find out what is already there. This is where chat is strongest, because it costs nothing and touches nothing.

Copilot promptOrientationChat, any IDE

Where in this project is request handling middleware configured? Identify anything that already inspects requests before they reach handlers, and say whether there is an existing pattern I should follow rather than introducing a new one.

The answer’s quality depends heavily on your editor. VS Code, JetBrains and Eclipse have workspace indexing and can genuinely search. Xcode does not — there you open the files yourself and say so in the prompt. This is the single largest practical difference between editors in this whole workflow.

Step 3 — Choose the approach

Copilot can lay out options. It cannot choose between them, because choosing requires knowing things that are not in the code: your traffic profile, your operational constraints, what the team tried two years ago and abandoned, how much complexity this codebase can carry.

Copilot promptOptions with trade-offs, not a recommendationChat, any IDE

Give me three approaches to rate limiting for this service. For each: how it behaves under a multi-instance deployment, what it costs operationally, and how it fails. Do not recommend one — I will choose.

Step 4 — Branch and start clean

Mechanical, and the step people skip.

Create a branch. Commit or stash everything. Confirm the working tree is clean before anything agentic runs.

Step 5 — Set up context

Two parts.

Open the files that matter. The code you are changing, the interface it implements, the configuration that governs it, the test file. In Xcode this is essential rather than helpful. Everywhere else it still improves results, because explicitly provided context outweighs indexed context.

Check your repository instructions are current. .github/copilot-instructions.md is read in every IDE covered in this cluster. Three cautions carried forward from earlier lessons:

  • JetBrains treats custom instructions as public preview.
  • Eclipse chat reads only the repository-wide file — path-specific instructions apply to agent mode there, not chat.
  • Visual Studio does not read agent files such as AGENTS.md.

Step 6 — Write the contracts

The highest-leverage thing you do all day, and it is fast.

Write the signature. The types. The interface. The error cases in the throws clause or the return type. In Python, the type hints. In TypeScript, the interface. In Swift, the protocol.

A precise contract constrains completion far more effectively than a paragraph of English, because it is unambiguous and the model has to satisfy it. It also gives your IDE something to check the result against — which is what turns IntelliJ’s gutter or Xcode’s compiler into a review pass rather than decoration.

Step 7 — Generate the implementation

Now delegate, choosing the mode by scope:

SituationMode
One function, contract writtenInline completion
Several files you can nameEdit mode (VS Code, JetBrains)
You cannot name the filesAgent mode
Understanding, not changingChat
Copilot promptAgent mode with real boundariesAgent mode, any IDE

Implement rate limiting following the middleware pattern already used in this project. Change only the middleware layer and its configuration. Do not modify existing handlers, do not add dependencies, and do not change tests. Stop and tell me if the change requires either.

Boundaries in the prompt are guidance, not enforcement. The approval prompt is the enforcement, and only if you read it.

Step 8 — Read every line you accepted

This is the step that separates people who benefit from Copilot from people who accumulate debt with it, and it does not scale by skimming.

What to look for, in order of how often it matters:

Invented APIs. Methods that do not exist. Your IDE catches most of these immediately — the gutter in IntelliJ, the compiler in Xcode, unresolved imports in PyCharm. Look before moving on.

Wrong-version APIs. Harder, because they resolve. Pydantic v1 patterns in a v2 project, pre-Compose Android idioms, Spring Boot 2 configuration. These compile and fail later.

Silent failure handling. A swallowed exception, a default returned where an error should propagate, a catch that logs and continues. Generated code is biased towards not crashing, which is not the same as being correct.

Duplication. A helper that already exists three files away. Indexing reduces this; in Xcode, without it, expect duplication.

Security-relevant patterns. String-concatenated SQL, missing authorisation checks, credentials in code, unvalidated input reaching a sensitive operation.

Step 9 — Run it

Build it. Execute it. Observe what happens.

An agent reporting success is reporting on its own work. Agent mode can run commands and read output, and that genuinely helps — but the loop that ends with you having seen the thing work is the one that counts.

Step 10 — Write tests against the behaviour

With an existing test file open, so the generated tests inherit your framework, fixtures and naming rather than the most common ones on the internet.

The failure mode to watch for is the test that asserts a mock was called when the question was what value came back. It passes whether or not the code works, and it costs maintenance forever.

Copilot promptTests that could failChat, any IDE

Write tests for this, matching the framework and fixtures in the test file I have open. Cover the boundary conditions and the failure paths. Every assertion must check a value that would be different if the implementation were wrong — do not assert only that a dependency was called.

Step 11 — Break it deliberately

Change the implementation so it is wrong. Invert a condition, return the wrong field, skip a validation. Run the tests.

If they still pass, they are not testing anything. Fix them before continuing.

Step 12 — Handle the failure paths

The happy path is what gets generated. What is left is everything else: invalid input, absent input, the downstream service being down, concurrent access, the resource that must be closed, the transaction that must roll back.

Copilot is useful here precisely because enumerating failure modes is tedious and it does not get bored:

Copilot promptEnumerate what goes wrongChat, any IDE

List the ways this code fails in production: invalid or missing input, downstream failure, timeout, concurrent access, and partial failure leaving inconsistent state. For each, say what currently happens — not what should happen.

“What currently happens” is the important phrasing. Asking what should happen gets you an essay on best practice; asking what does happen gets you a list of bugs.

Step 13 — Review your own diff

In the diff view. Not by re-reading the files.

The distinction is not pedantry. Reading files in place shows you code you already believe in, and you see what you intended. A diff shows you what actually changed — including the three lines the agent altered in a file you had forgotten it touched.

Every editor in this cluster has a diff view. Use it before anything is pushed.

Step 14 — Run Copilot code review

Run it on your own changes before a human sees them. It reliably catches the mechanical class of finding — missing null checks, unclosed resources, inconsistent error handling — which is exactly the class that wastes a reviewer’s attention.

It does not tell you whether the change is the right change, whether it belongs in this service, or whether it contradicts a decision made before it was written. That is what the human review is for, and clearing the mechanical findings first is what makes room for it.

Step 15 — Write the pull request description

Copilot drafts well from a diff, and a draft is the right output here because the part that matters is the part it cannot know.

A good description answers three questions: what changed, why, and what a reviewer should look at hardest. Copilot can answer the first from the diff. It can guess at the third. It cannot know the second, because the reason lives in a ticket, a conversation or an incident.

Copilot promptDraft, then correctChat, any IDE

Draft a pull request description from these changes. Cover what changed and where to look most carefully. Leave the “why” as a placeholder for me to fill in — do not guess at the motivation.

Then write the why yourself. It is two sentences and it is the part future readers will actually need.

Step 16 — Respond to human review

A reviewer questions the approach. That is a conversation about design, involving context neither you nor Copilot has fully written down, and it is not something to hand to a model.

Copilot is useful around the edges — implementing an agreed change, explaining unfamiliar code a reviewer pointed at, checking whether a suggested alternative has a problem. The judgement about whether the reviewer is right stays with you.

A complete worked example

The abstract version above, run once on a real-shaped task. The feature: an API endpoint is being hammered by one client and the team wants rate limiting.

Step 1. The ticket says “add rate limiting”. You ask the questions and get answers: limit per API key, 100 requests per minute, return HTTP 429 with a Retry-After header, applies to authenticated traffic only, and the service runs three instances behind a load balancer. That last answer is the one that changes everything, and nobody would have volunteered it.

Step 2. You ask where request handling is configured, and learn there is an existing authentication middleware. Following its pattern is better than inventing a new one. You open it and confirm what you were told.

Step 3. Three approaches come back: in-memory per instance, shared state in Redis, or delegating to the load balancer. In-memory is simplest and wrong here — three instances means the effective limit is 300, not 100. The load balancer would work but is owned by another team and would take weeks. Redis it is, because the service already uses Redis for sessions.

Notice that the deciding facts — three instances, Redis already present, the other team’s timeline — were all outside the codebase.

Step 4. Branch, clean tree.

Step 5. Open the authentication middleware, the Redis client wrapper, the middleware registration, and the middleware test file.

Step 6. You write the contract. A middleware type matching the existing one, a limiter interface with a single method taking a key and returning an allow decision with the retry delay. Two small types, and the design is now fixed.

Step 7. Agent mode, scoped to the middleware layer and its configuration, with handlers and tests explicitly off limits.

Step 8. Reading the result, three things stand out. The Redis operations are not atomic — a read followed by a write, which races under exactly the concurrent load this feature exists to handle. The Retry-After value is computed but never set on the response. And a failed Redis call is caught and logged, silently allowing the request through.

Copilot promptFix a specific, diagnosed problemChat or edit mode

This check-then-increment races under concurrent requests for the same key. Make it atomic using the Redis primitives already used in the client wrapper I have open. Do not introduce a new dependency or a Lua script.

The third finding is not a bug to fix but a decision to make: when the rate limiter is unavailable, does the service fail open or closed? Failing open means an outage removes the protection. Failing closed means a Redis blip takes the API down. That is a business decision, and it goes back to whoever wrote the ticket.

Step 9. Run it. Two terminals, a loop of requests, watch the 429s appear.

Step 10. Tests, with the existing middleware test file open: under the limit allowed, over the limit rejected with 429, Retry-After present and plausible, different keys counted separately, and the window resetting.

Step 11. Break it. Invert the comparison so the limiter allows everything. Two tests fail; three still pass, and one of those three should not have. It was asserting the Redis client was called rather than what the middleware returned. Rewrite it.

Step 12. Failure paths. Redis unavailable — now a deliberate choice rather than an accident. A key that has never been seen. A clock skew across instances. The very first request in a window.

Step 13. Read the diff. The agent touched the middleware registration file in a way you had not noticed, reordering two entries. Harmless here, but you would not have seen it in the editor.

Step 14. Copilot code review flags an unclosed resource in the test setup. Real, minor, fixed in a minute — and it would otherwise have cost a reviewer a comment and a round trip.

Step 15. Draft the description, then write the why: which client, which incident, why 100 and not 1000, and why Redis rather than the load balancer. None of that is in the diff.

Step 16. A reviewer asks whether the limit should be configurable per key rather than global. It is a good question. You discuss it, agree that it is a follow-up rather than a blocker, and note it in the ticket.

Count where Copilot contributed: orientation, options, the bulk of the implementation, the atomic fix, the tests, the review pass and the draft. Count what would have shipped had you not read the output: a racing limiter, a missing header, and a silent fail-open.

Both counts are the point. The tool did most of the typing and none of the deciding.

What the marked steps have in common

Six steps are marked human judgement: understanding the requirement, choosing the approach, writing the contracts, reading what you accepted, reviewing your own diff, and responding to review.

They share a property. Each depends on knowledge that is not in the codebase — what the business wants, what the team decided, what failed before, what a change was supposed to achieve. Copilot has read your code. It has not attended your meetings.

The other ten steps are largely mechanical, and mechanical work is exactly what generation is good at. That is the trade this whole cluster has been describing: delegate the mechanical, keep the judgement, and be deliberate about which is which.

Anti-patterns worth naming

Four ways this workflow gets deformed in practice, each of which looks efficient in the moment.

Accept-and-move-on. Skipping step 8 because the code compiled. This is how technical debt accumulates faster with Copilot than without it — not because the code is worse, but because more of it arrives per hour and none of it was read. The cost is deferred, not avoided.

Prompting instead of thinking. Rewriting a prompt five times to get better output, when the actual problem is that step 1 was never done and the requirement is still ambiguous. If three attempts have not produced something usable, the problem is upstream of the prompt.

Asking the model to check its own work. “Are you sure?” is not verification. It produces a confident answer either way and costs a round trip. Verification means running the code, reading the diff, or using a tool that resolves symbols.

Long agent sessions. Twenty minutes of unattended agent work produces changes across a dozen files that nobody followed. Short sessions with a commit between them cost the same total time and leave you able to say what happened.

How this varies by editor

The workflow is the same everywhere. Four steps change shape:

StepWhere it differs
2 — ExploreXcode has no workspace indexing; open files yourself
4 — Start cleanEclipse has no checkpoints; git is the only undo
7 — GenerateEdit mode exists only in VS Code and JetBrains
14 — ReviewNot in the editor in Eclipse; use GitHub

Everything else — contracts, reading, running, breaking tests, diffs — is identical in all six.

Security

Adapting the workflow to smaller work

Not every change is a feature. Two common shapes, and which steps survive.

A bug fix. Steps 1 to 3 collapse into one: reproduce it. A bug you cannot reproduce is a bug you cannot verify a fix for, and no amount of prompting substitutes. Once reproduced, the rest of the workflow runs unchanged — and step 11, breaking it deliberately, becomes the most valuable step of all, because a regression test that does not fail against the original bug is not a regression test.

A dependency upgrade. Generation barely features. The work is understanding what changed between versions and what in your code depends on it, which is a comprehension task Copilot is genuinely good at. Steps 8 and 13 stay; step 6 does not apply; step 12 becomes the whole job.

The pattern is that the marked steps survive every shrink and the generation steps are what fall away. That is worth noticing, because the instinct when work is small is to skip the review and keep the generation, which is exactly backwards.

Frequently asked questions

Can I skip steps when the change is small? Steps 4, 8 and 13 are cheap regardless of size, and small changes are where people skip reading. Scale the generation steps down, not the review ones.

Which step do people get wrong most often? Step 1. An under-specified requirement produces a polished implementation of the wrong thing, and everything downstream is wasted.

Is agent mode safe for the whole workflow? It is safe for step 7, within boundaries you set and approvals you read. The marked steps are not agent work — not because agents are dangerous there, but because they lack the information those steps require.

Do I need checkpoints for this to work? No. Commit before agent sessions and git covers you in every editor, including Eclipse where nothing else does.

What if my editor lacks Copilot code review? Push the branch and request review on GitHub. Only the in-editor loop is missing.

How long does this take? Less than doing it without Copilot for most features, and the saving comes from steps 7, 10, 12 and 15. The marked steps take the same time either way — which is the point.

Next steps

That completes Cluster 2. You now have the editor-specific detail from lessons 14 through 24 and the workflow that ties it together.

To revisit a specific editor, the cluster index is at GitHub Copilot for IDEs, which also carries the full feature matrix. For the foundations this cluster builds on — plans, modes, prompting and custom instructions — see GitHub Copilot Academy.

Sources

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

Primary sources