Setting review turnaround targets and escalation Jump to heading

Waiting for review is often the largest share of a pull request’s lifetime. The author has finished, CI is green, and the change sits for a day and a half because everyone assumed someone else would look. Long waits push authors to batch more into each pull request, which makes reviews slower still, and they drive branches apart from main, which causes conflicts. Agreeing a turnaround target — a first response within a few working hours, say — and making sure each request has an owner and a nudge when it goes over is the cheapest improvement most teams can make to their delivery speed. This page sets a target, routes requests so they have owners, automates reminders, and defines an escalation path, within code review workflow engineering.

When to use this approach Jump to heading

  • Pull requests regularly wait a day or more for a first review.
  • Review requests go to a whole team and nobody feels responsible.
  • Authors work around slow reviews by batching changes into large pull requests.
  • You have measured latency, as in measuring review latency from Git history, and want to act on it.

Step 1 — Agree a target the team believes in Jump to heading

Set a target for time to first response, measured in working hours, not a target for approval. A first response — approval, questions or “I’ll look this afternoon” — unblocks the author’s planning. Base it on current data so it is achievable.

<!-- CONTRIBUTING.md (excerpt) -->
## Review turnaround
- First response to a review request within **4 working hours**.
- A first response can be a review, questions, or a note on when you will review.
- Small pull requests (under ~200 changed lines) are expected to be reviewed in that window.
- If you cannot review, re-request from someone else rather than leaving it.
A review request and its escalation pathA pull request is opened and a reviewer is assigned automatically. If there is no response after four working hours, a reminder is posted to the reviewer. After a working day, the request is reassigned to a second reviewer. After two days, it is raised in the team's daily stand-up or channel.Assignedone named reviewer0 hReminderto the reviewer4 hReassignedsecond reviewer8 hEscalatedraised with the team16 hmost requests never reach the second point once owners are named

Step 2 — Route each request to a named person Jump to heading

A request to a team is a request to nobody. Use team review assignment so the forge picks individuals, balancing load, and code owners for areas that need specific expertise.

# GitHub: team settings → Code review → enable auto assignment, 1 reviewer, load balance.
# The team is requested (for example by CODEOWNERS) and the forge swaps in one member.
gh pr view 1234 --json reviewRequests --jq '.reviewRequests[] | .login // .name'   # check who was picked

Ownership by path is covered in path-based CODEOWNERS in a monorepo.

Step 3 — Show what is waiting Jump to heading

Make waiting requests visible daily. A short list in the team channel each morning does more than any dashboard.

gh pr list --state open --search "review:required draft:false" \
  --json number,title,author,createdAt,reviewRequests,url \
  --jq '.[] | select(.reviewRequests | length > 0) |
        "\(((now - (.createdAt|fromdateiso8601))/3600)|floor)h  #\(.number) \(.title) → \([.reviewRequests[] | .login // .name] | join(", "))"' |
  sort -rn | head -n 15

Step 4 — Nudge automatically past the target Jump to heading

Run a scheduled job that comments on, or messages the reviewer for, requests past the target. Count working hours so weekends do not trigger reminders.

on:
  schedule: [{ cron: "0 9-17/2 * * 1-5" }]
jobs:
  nudge:
    runs-on: ubuntu-latest
    permissions: { pull-requests: write }
    steps:
      - env: { GH_TOKEN: "${{ github.token }}", GH_REPO: "${{ github.repository }}" }
        run: |
          gh pr list --state open --search "review:required draft:false" --json number,updatedAt,reviewRequests \
            --jq '.[] | select(.reviewRequests|length>0) | select((now - (.updatedAt|fromdateiso8601)) > 4*3600) | .number' |
          while read -r n; do
            gh pr comment "$n" --body "Friendly reminder: this has been waiting for review for over 4 hours."
          done

This uses time since last update as a simple proxy; a precise version counts working hours since the review request event.

Step 5 — Reassign and escalate Jump to heading

When a reminder does not help, the request should move rather than wait. Reassign after a working day, and raise it with the team after two. Make reassigning normal and blameless.

gh pr edit 1234 --remove-reviewer alice --add-reviewer bob
gh pr comment 1234 --body "Reassigned to @bob — @alice is focused on the incident today."
A request has passed the target — what next?If the reviewer is available but busy, a reminder is enough. If the reviewer is away or overloaded, reassign to someone else. If nobody on the team can review the area, escalate to the team lead because that is a staffing or knowledge gap, not a reminder problem.Why is it waiting?reviewer busyReminderusually enoughreviewer away / overloadedReassignno blamenobody can reviewEscalateknowledge gapthe third branch shows up repeatedly for the same code — that is a signal

Step 6 — Make reviews easy to do quickly Jump to heading

Targets are easier to meet when pull requests are small, described well and green before review is requested. Draft pull requests keep unfinished work out of reviewers’ queues.

gh pr create --draft --fill            # not in anyone's review queue yet
gh pr ready 1234                       # requests review only when CI is green and it is complete

See using draft pull requests effectively.

Step 7 — Review the numbers monthly Jump to heading

Track the median and 90th percentile time to first response, and the share of requests that needed reassignment. Discuss them as a team, adjust the target if it is consistently missed or easily met, and look at where escalations cluster.

gh pr list --state merged --limit 300 --json createdAt,reviews \
  --jq '[.[] | select(.reviews|length>0) | ((.reviews[0].submittedAt|fromdateiso8601) - (.createdAt|fromdateiso8601))/3600] | sort |
        "median \(.[length/2|floor]|floor) h, p90 \(.[(length*0.9)|floor]|floor) h"'
Time to first review before and afterAn illustrative team. Median time to first review drops from over a day to a few hours after introducing a target, named reviewers and reminders. The ninetieth percentile drops more slowly and improves further once reassignment becomes normal.hours to first review (illustrative)median before27 hmedian after3 hp90 before71 hp90 after9 hthe long tail shrinks when waiting requests move instead of sitting

Validation checklist Jump to heading

Frequently Asked Questions Jump to heading

Won’t targets make reviews shallower? Jump to heading

The target is for a first response, not approval. A reviewer can respond quickly with “this needs a proper look; I’ll do it at two”. What targets remove is silence, not care.

What about time zones? Jump to heading

Count working hours in the reviewer’s time zone, and prefer assigning reviewers who overlap with the author’s day for urgent changes. Teams spread across many zones often set a longer target.

Should reminders go to the channel or privately? Jump to heading

Start privately, by mention on the pull request. Raising it in a team channel is the escalation step, not the first nudge.