All articles in Code review
Code review

Async code review without 24-hour latency

Two-touch SLO, structural defaults, comment templates. How distributed teams keep review latency under 4 hours without forcing synchronous meetings.

9 min read

A distributed team that defaults to synchronous code review introduces 24-48 hour latency into every change. The PR opens at 5pm in San Francisco; the reviewer in Berlin sees it the next morning; the author wakes up to comments at noon SF time; the reviewer is at dinner. The change merges 36 hours after it opened, blocking dependent work the whole time.

Async code review without 24-hour latency is possible. The teams doing it well have a two-touch SLO, structural defaults that absorb timezone gaps, and a comment-template vocabulary that compresses the back-and-forth. Each of these is mechanical.

The two-touch SLO

The single most important number: the time from PR open to first review touch, where "touch" means either an approval, a request for changes, or a substantive comment. Healthy async teams target:

  • p50 ≤ 2 hours during working windows
  • p95 ≤ 8 hours, measured during the union of all timezones the team spans
  • p99 ≤ 24 hours, regardless of weekends or holidays

The "working windows" qualifier matters: a team spanning SF + Berlin + Singapore has roughly 18 hours per day of overlapping working time; the p50 should measure within that window, not against a 24/7 calendar.

The reason p50 is the target rather than mean: a single overnight PR can drag the mean from 4 hours to 14 without changing the experience for 90% of changes. p50 is what most engineers feel.

Structural defaults that absorb the gap

Hitting the SLO across timezones requires four structural defaults:

1. Two reviewers, parallel notification

Every PR notifies at least two potential reviewers — typically one team member + one code-owner (or two team members for code without specific ownership). Either can complete the review; the first to engage wins. This eliminates the failure mode where the single notified reviewer is on PTO/sick/busy and the PR sits.

The tooling pattern is straightforward: GitHub Code Owners + branch protection rules that require code-owner approval; Slack notifications routed to a team channel rather than a single DM; rotation among team members for "review buddy" assignment per week.

2. The PR author writes the review brief

The first comment on every PR — by the author, before reviewers engage — is a 3-sentence brief: what the change does, what the reviewer should focus on, and what to skip. Without this, the reviewer reconstructs context from the diff, which is slow and error-prone across timezones.

The brief structure that works:

What this does: Adds a /vs/<slug> page for [competitor], wired into the existing sitemap + JSON-LD generation.

Focus on: Whether the strengths/weaknesses ring true and the FAQ answers are accurate for the competitor's current product.

Skip: The TypeScript boilerplate is templated from /vs/jira; no novel structure.

Three sentences, written in 90 seconds, eliminates 30 minutes of reviewer context-reconstruction. The ROI is roughly 20:1 in reviewer time.

3. PR size cap

Async review breaks down on large PRs because the reviewer can't load the change into working memory across a multi-day gap. The cap that works is:

  • Under 200 LOC changed: review in a single session, comment immediately
  • 200-500 LOC: review in a session, may need a follow-up question round
  • 500+ LOC: split before opening, or expect 48+ hour review cycles

Teams with a hard 400-LOC cap and an explicit "split this" review action see dramatically lower review latency. The exception list (migrations, generated code, dependency upgrades) needs to be explicit and the exception PRs need to label themselves to skip the cap.

4. Comment templates for the common cases

Most async review back-and-forth is recoverable into ~6 comment templates. Standardising them cuts both review time and round-trip count:

  • nit: — non-blocking, author's call. "nit: could be Map<string, User>"
  • question: — author needs to answer before merge. "question: what's the behaviour when input is empty?"
  • request: — author needs to change before merge. "request: this needs a test for the timeout case"
  • praise: — explicit positive feedback. "praise: nice handling of the race condition in the consumer"
  • fyi: — informational only, no action needed. "fyi: we have a helper for this in /lib/format/date"
  • block: — design-level concern that needs discussion before more work. "block: this changes the public API; let's discuss before merging"

The convention removes the most common review-friction source: the author re-reading every comment to figure out which require action vs which are suggestions vs which are praise. With prefixes, the author can scan the comments and identify the action set in seconds.

The PR-author practices that close the loop

Async review's other failure mode is the author dropping out after pushing the PR. The discipline:

  • Check for review comments at the start of each working day. Treat them as the highest-priority interrupt; the PR has been blocked while you slept.
  • Reply to every comment within an hour of seeing it. "Will address" is a valid reply if you need time; silence is not.
  • Push fixes in atomic commits with messages tied to the comment. "Address review: handle empty input case." This lets the reviewer re-review just the new commit rather than the whole diff.
  • Re-request review explicitly when ready. GitHub's "re-request review" button matters; the reviewer otherwise doesn't know whether the author has more work to do.

The reviewer practices

Reviewer-side discipline matters equally:

  • First touch within the SLO. Even if the substantive review will take 30 minutes later, leave a comment within the SLO: "I'll review this morning after standup; flagging timezone." The author isn't blocked on unknowns.
  • Batch comments, not drip-feed. Read the entire diff, then write all comments at once. Drip-feed comments produce 5-10 round trips for what could be 1-2.
  • Use the comment templates. They're as much for the reviewer's clarity as the author's.
  • Hard-block sparingly. Reserve block: for genuine design concerns. Overuse trains authors to ignore the prefix.

Measurement

Track three numbers weekly:

  1. Median time-to-first-touch (the SLO metric)
  2. Median round trips per PR (proxy for comment quality; healthy is 1-3)
  3. PRs >24 hours open count (the long-tail metric that hides in averages)

Trends matter more than absolute values. A team starting at 8-hour median time-to-first-touch can reach 2 hours in 4-6 weeks of disciplined application; a team at 24 hours has a deeper organisational problem (timezone coverage, reviewer assignment, PR sizing).

The synchronous escape hatch

Async review handles 90-95% of changes well. The remaining 5-10% are better handled synchronously: high-stakes changes (security, payments, schema migrations), changes that require shared design discussion before review can be meaningful, and changes by junior engineers who benefit from pairing through the review.

For these, the explicit move is "let's mob this" or "let's pair on this" — see the mob review article for the structured format. The point is that the synchronous mode is the explicit exception, not the implicit default.

Common failure modes

Three patterns to watch for:

  • The single-reviewer bottleneck. One senior engineer becomes the de-facto code-owner for everything. Time-to-first-touch is great for their reviews, awful for everyone else's. Fix: distribute review load across the team via rotation or auto-assignment.
  • The drive-by approval. Reviewer approves without comments to clear their queue. Quality drops without anyone noticing. Fix: track approve-to-comment ratio; >70% bare approvals is a red flag.
  • The stalled mid-conversation PR. Reviewer requests changes; author addresses; reviewer doesn't notice the re-request; PR sits for days. Fix: SLA on re-review (1-2 hours during working windows) and notification routing that doesn't get drowned by other GitHub noise.

The pattern across all three: async review is a system, not a heuristic. When it breaks, the fix is structural — not "everyone try harder."

For the SLO targets in more detail, see Service-level objectives for code review. For the synchronous exception case, see Mob review. For the checklist the reviewer actually uses, see Review checklists that catch bugs.

Frequently asked questions

How can distributed teams keep code review latency under a day?
Four structural changes: (1) auto-assign at least two reviewers per PR so one being unavailable doesn't block the queue, (2) require the PR author to write a 3-sentence review brief up front so reviewers don't reconstruct context from the diff, (3) enforce a hard PR-size cap (~400 LOC) so reviews fit in a single session, (4) standardise comment prefixes (nit:, question:, request:, block:) to compress round-trips.
What is a good code review SLO target?
For distributed teams: median time-to-first-touch ≤2 hours during working windows, p95 ≤8 hours, p99 ≤24 hours. Time-to-merge: median ≤8 hours for PRs under 200 LOC, ≤24 hours for larger PRs. These targets are achievable with disciplined async practices and don't require synchronous review meetings.
How should reviewers handle PR comments?
Batch comments rather than drip-feed: read the entire diff, then write all comments at once. Drip-feed reviews produce 5-10 round trips for what could be 1-2. Use prefix conventions (nit: for nitpicks, request: for blockers, question: for clarifications) so the author can scan comments and identify the action set in seconds.
Is async or synchronous code review better?
Async handles 90-95% of changes well at much lower latency for distributed teams. The remaining 5-10% (security-sensitive, schema migrations, complex architecture changes) benefit from synchronous mob review. The right default is async with explicit escalation to mob for high-stakes changes — not synchronous review for everything.
Defined in our glossary

More in Code review