Number 7Oct 2026

At work I don't hand off Figma frames anymore. I change the codebase with Copilot, push a feature branch, and open a pull request into develop. QA tests it on SIT after it merges. This is that process written down step by step, with the branch names, the commands, and the parts that still go wrong. Pinned to October 2026.

Devon Kim Handoff Is a PR

Handoff Is a PR


Conditions

  • Figma for ideation; existing components come in through MCP
  • I create feature/* branches off develop, nothing else
  • PR into develop, merge commit, two approvals
  • QA tests develop on SIT; release/* goes to UAT
BEFORE — THE FILE SHIPSFigmaevery state polishedHandoffredlines · ticketEngineeringrebuild from fileSITbuild vs FigmaUATReleasepixel bugs → next roundAFTER — THE PR SHIPSFigmaideation · MCPfeature/*Copilot agent modePR → developscreenshots · reviewSITQA tests developrelease/* → UATmainproductionmeCopilotQA / reviewdevelopersThe handoff didn't get faster. It moved into the pull request.
Fig. 1one screen, idea to release: before and after

Why this page

For years my job ended at a Figma file. Redlines, a Dev Mode link, a ticket, and weeks later a build that was close but not quite. Now it ends at a merged pull request. I make the change in the codebase with GitHub Copilot in VS Code, push a branch, and open the PR myself. QA meets the change on SIT once it's in develop.

The first draft of this page leaned on industry research and made Figma look more central than it is. This version is the process I actually run: the branches I touch, the commands, how QA picks it up, and what happens when a developer and I end up in the same code. The industry context is still at the end, because it's useful to know this isn't only my team.

Before and after

One screen, from idea to release.

Before

  1. 01Figma — explore, then polish final frames for every state
  2. 02Handoff — redlines, spec notes, a Dev Mode link, a ticket
  3. 03Engineering — a developer rebuilds the screen from the file
  4. 04SIT — QA compares the build to Figma and files pixel bugs
  5. 05Round trip — the developer fixes, I re-check, repeat

After

  1. 01Figma — ideation only; existing detailed components come in through Figma MCP when I need them
  2. 02feature/* — I branch off develop and make the change with Copilot agent mode, using the real components and tokens
  3. 03Pull request — into develop, with before/after screenshots; Copilot review first, then two human approvals
  4. 04develop → SIT — after merge, the build goes to SIT and QA hears about it from the deploy notice
  5. 05release/* → UAT → main — a developer cuts the release branch and merges to main

What disappeared is everything that existed only to translate a file into code: redlines, spec notes, "the build doesn't match the mock" tickets, the wait between rounds. Polishing every state in Figma went too. The states now get polished once, in code.

GONE — EXISTED TO TRANSLATEredlines.pngspec notesDev Mode link"build ≠ mock" ticketevery state polished in FigmaNEW — SMALLER, CONCRETEPR into developbefore/after screenshotsCopilot review commentsdeploy notice to QAfix branch for SIT bugsmeCopilotQA / reviewEverything on the left existed because the same screen was built twice.
Fig. 2

Branches and environments

We use a Git Flow shape. It's not fashionable. Vincent Driessen, who wrote git-flow, said in 2020 that teams doing continuous delivery should use something simpler, and Atlassian now calls it a legacy workflow. It still fits a product with SIT and UAT stages, because each long-lived branch maps to an environment.

The map

  1. 01main — production; a developer merges into it from the release branch
  2. 02release/* — cut from develop for a release; this is what UAT tests
  3. 03develop — where finished work collects; this is what SIT runs
  4. 04feature/* — mine; short-lived, branched off develop, merged back into develop

I only ever create feature/* branches. Release branches and the merge into main belong to the developers, and we merge with merge commits, so the history keeps every branch visible.

How QA knows what is on SIT

No build hash in a footer. A deploy notification and the team channel. When something merges into develop and goes out, QA hears about it there. Fixes found during testing go up separately as their own branches. Per Git Flow, once a release branch exists, bug fixes for that release are made on the release branch and merged back into develop; before that, they go into develop like any other change. Which of the two a fix takes depends on where the release is.

maindevelopersPRODrelease/*developersUATdevelopsharedSITfeature/*melocalgit switch -cPR · merge commitUAT fixcut releasemerge to mainback into developmeQA / reviewdevelopersEach long-lived branch maps to an environment. I only create feature/* branches.
Fig. 3

One screen, branch to merge

What a typical change looks like, with the commands I actually type. Copilot does most of the editing; Git is still mine.

  1. 01Start from fresh develop — git switch develop, then git pull
  2. 02Branch — git switch -c feature/payout-history-filter
  3. 03Build it — Copilot agent mode in VS Code makes the change across files; I check it running locally, every state, not just the happy path
  4. 04Commit — small commits with plain messages; GitHub Desktop and VS Code can draft them with Copilot
  5. 05Push — git push -u origin feature/payout-history-filter
  6. 06Open the PR — base develop, before/after screenshots for each state I touched, the ticket linked
  7. 07Review — Copilot code review comments first; it doesn't count as an approval. Then two human approvals, and it merges with a merge commit

Two habits matter more than any command. Sync with develop before opening the PR, not after the reviewer finds the conflict. And keep the PR to one screen or one fix, because a reviewer can actually read that.

For syncing I merge develop into my branch (git merge origin/develop). Rebasing rewrites history and needs a force push, so I only do it on a branch nobody else has pulled. One trap worth knowing: during a rebase, "ours" and "theirs" in the conflict editor are the reverse of what you'd expect.

ONE CHANGE, BRANCH TO MERGEFresh developgit pull1meBranchfeature/*2meBuildagent mode3CopilotCommitsmall, plain4mePushpush -u5meOpen PRbase: develop6meReviewCopilot → 2 approvals7reviewersmeCopilotQA / reviewBefore opening the PR: git merge origin/developRebase only a branch nobody else has pulled — it rewrites history and needs a force push.Copilot does most of the editing. Git is still mine.
Fig. 4

What the repo tells Copilot, and Figma's small job

Copilot doesn't know our design system unless the repo tells it. That's what .github/copilot-instructions.md is for, along with path-specific .instructions.md files that apply only to matching folders. Agent mode reads them on every request, and since August 2025 Copilot code review reads the same file, so the rules that guide the change are also the rules it's reviewed against.

The rules that matter most for UI work are boring and literal: use the existing component before writing a new one, use tokens instead of hex values or pixel spacing, cover loading, empty and error states, don't touch shared business logic without saying so in the PR, and warn me before editing code that someone is already changing on another branch.

Figma has a small job now

Ideation, and sometimes pulling an existing, detailed component into the conversation through Figma MCP so Copilot builds against the real spec instead of guessing. The setup is one entry in VS Code's MCP config pointing at Figma's remote server. As of October 2026 Figma still lists that server as beta, and in an enterprise org an admin has to allow MCP servers in Copilot first.

IN THE REPO.github/copilot-instructions.md.github/instructions/*.instructions.mdcomponents first · tokens, not hex · every stateWHEN NEEDEDFigma MCPpull an existing, detailed componentAgent modewrites the changeCopilot code reviewchecks the PRCopilotQA / reviewPRsame rules,both sidesCode review reads copilot-instructions.md since Aug 2025. Review comments are never the approval.
Fig. 5

When a developer and I touch the same logic

This is the part nobody writes about, and the part that actually goes wrong.

The case

During payment work, the payment history page and the payout approval flow shared the same logic. I was fixing something on one screen. A developer was fixing something in the same logic for the other. Neither change was wrong on its own. Together they collided, and because the code was shared, a fix on one screen could quietly change behavior on the other.

There are three kinds of conflict, and only one of them is loud:

  1. 01Text conflict — two people edit the same lines; Git stops and asks
  2. 02Semantic conflict — Git merges cleanly, but the result is broken; Martin Fowler's example is two changes that are each fine and wrong together
  3. 03Ownership conflict — a designer's UI change and a developer's logic change land in the same shared code at the same time

The payment case started as the third kind and could easily have shipped as the second.

What I do instead

Prevention, not resolution. One of the instruction files tells the Copilot agent to warn me when the code I'm about to change is already being worked on in another branch, before it writes anything. When that warning comes up, we decide who takes that part. If it's the developer, I don't touch it. They finish and merge into develop, I pull, and only then do I start the work that connects to it. The conflict editor never opens because the two changes never overlap in time.

We don't use CODEOWNERS, so these boundaries aren't enforced by the repo. The warning and the quick conversation after it do that job, and every PR still needs two approvals before it merges.

PAYMENT WORKPayment historyscreenPayout approvalscreenShared payment logicused by both screensfeature/* · medev's brancheach fix is fine alone · together they collideWHAT I DO INSTEADCopilot warnsin progress on another branchDecide whoone owner for that partHands offthey finish and mergePull, then buildconnected work starts aftermeCopilotQA / reviewdevelopersAn instructions file makes the agent warn first. The two changes never overlap in time.
Fig. 6

QA meets it on SIT

QA used to spend a lot of their time comparing builds to Figma. Spacing that was a few pixels off, the wrong grey, a missing hover state. Those bugs existed because the same screen was built twice, once in Figma and once in code. When the designer writes the UI, that whole category mostly disappears.

So their process got shorter, and their scope got wider. They pick up the change on SIT once it's in develop and spend the time on what I and the agent are bad at: functional edge cases, real data and integrations on SIT, regressions on other screens that share the same logic, accessibility, and devices. The payment case above is exactly the kind of bug they're now positioned to catch.

The loop

  1. 01Merge — my PR lands in develop
  2. 02Deploy — develop goes to SIT; the notice goes to the team channel
  3. 03Test — QA runs the change and the screens around it
  4. 04Bug — a ticket comes back to whoever wrote the UI, which is often me
  5. 05Fix — a new branch, a new PR, back through SIT; after the release branch is cut, the fix goes to release/* and back into develop
  6. 06Promote — release/* to UAT, then a developer merges to main

Nobody has published numbers on how QA changes when designers write the UI. The World Quality Report 2025-26 describes tester time moving from writing test scripts toward risk analysis and test strategy, which matches what I see, but this part is observation, not measurement.

BEFORE · COMPARING TO FIGMAQA's timeBuild vs Figmaspacing, colors, missing statesPixel bug ticketsfile, wait, re-checkRound tripsdeveloper fixes, designer re-checksFunctionalNOW · TESTING DEVELOP ON SITQA's timeFunctional edge casesReal data & integrationson SITScreens sharing logicregressionsAccessibilitybeyond the scannerDevicesQualitative — what my team's QA stopped and started doing. Not a measured split.
Fig. 7

Where I stop, and the industry around it

Things I don't do: create release or hotfix branches, merge into main, or change shared business logic without a developer agreeing to it first. New components go through a developer. Copilot's review is a first pass, never the approval.

I'm not alone in working this way. Buzzvil, a Korean ad-tech company, published six months of designer PRs in September 2026: 432 of them, participation from 17% of the design team to 100%, and Figma Dev Mode seats from 23 down to three or four because there was no handoff left to inspect. Figma's 2026 AI report says designers doing development work doubled in a year, from 21% to 41%. But almost every public case is a startup testing on per-PR previews, mostly with Claude-based tools. A Copilot workflow running through a shared SIT and a UAT stage, with QA in the loop, is barely documented anywhere.

What I would tell another designer

Git isn't the hard part. The hard part is three judgments. Can I rebase this branch, or has someone else pulled it? Is this conflict mine to resolve, or is it logic? And is the thing on SIT actually my change? The design file used to carry the design decisions. Now the pull request does, and so does the code it merges into.


Sources