DEV Community

RobustTrueTry
RobustTrueTry

Posted on

Your Pull Request Review Queue Is Stalling

When pull requests sit untouched for days, feedback slows and morale drops. A stalled review queue often signals a process issue rather than lazy teammates. You’ll learn to spot the warning signs, choose a lightweight remedy, and avoid common pitfalls.

Why Reviews Get Stuck

Reviews stall when the workload exceeds capacity or when ownership is unclear. Common failure modes include:

  • Reviewers overwhelmed by unrelated tasks
  • No clear expectation for response time
  • PRs that are too large to review quickly
  • Lack of rotation, causing the same people to bear the burden

These patterns create a feedback loop: delayed reviews lead to larger batches, which further delay reviews.

Signals to Watch

Track simple metrics to catch stagnation early.

  • Median time from PR creation to first review
  • Percentage of PRs older than two days without feedback
  • Number of reviewers assigned per PR

If any of these trends worsen, it’s time to intervene.

Strategies to Unblock the Queue

You can apply one or more of the following tactics. Each has strengths and drawbacks.

Approach When it helps Tradeoff
Manual reminder (e.g., Slack ping) Small teams, occasional spikes Relies on human memory, can feel naggy
Automated bot that assigns reviewers Consistent flow, predictable volume Requires setup and maintenance
Rotating reviewer pool Prevents burnout, spreads knowledge May reduce deep expertise on niche areas

Pick the approach that matches your team’s size and culture.

Implementation Example: A Simple Reminder Bot

The following Python script uses the GitHub API to find PRs older than a threshold and posts a friendly comment asking for a review.

import os
import requests
import time

REPO = os.getenv('GITHUB_REPOSITORY')  # e.g., 'org/repo'
TOKEN = os.getenv('GH_TOKEN')
THRESHOLD_HOURS = int(os.getenv('STALE_HOURS', '24'))

headers = {
    'Authorization': f'token {TOKEN}',
    'Accept': 'application/vnd.github+json'
}

def get_open_prs():
    url = f'https://api.github.com/repos/{REPO}/pulls?state=open'
    resp = requests.get(url, headers=headers)
    resp.raise_for_status()
    return resp.json()

def post_comment(pr_number, body):
    url = f'https://api.github.com/repos/{REPO}/issues/{pr_number}/comments'
    payload = {'body': body}
    resp = requests.post(url, json=payload, headers=headers)
    resp.raise_for_status()

def main():
    now = time.time()
    for pr in get_open_prs():
        created = pr['created_at']
        # GitHub returns ISO 8601; parse simply
        from datetime import datetime, timezone
        created_dt = datetime.fromisoformat(created.replace('Z', '+00:00'))
        age_hours = (now - created_dt.timestamp()) / 3600
        if age_hours > THRESHOLD_HOURS:
            message = (
                f'Hey team, this PR has been open for {int(age_hours)} hours. '
                'Could someone please take a look when you have a moment?'
            )
            post_comment(pr['number'], message)
            print(f'Posted reminder on PR #{pr["number"]}')

if __name__ == '__main__':
    main()
Enter fullscreen mode Exit fullscreen mode

This script is intentionally minimal: it fetches open PRs, checks their age, and adds a comment if they exceed the threshold. By keeping the logic straightforward, you can run it as a cron job or integrate it into a CI pipeline without heavy dependencies.

Automation with GitHub Actions

You can schedule the script to run every hour using a workflow file.

name: Stale PR Reminder

on:
  schedule:
    - cron: '0 * * * *'  # every hour
  workflow_dispatch:

jobs:
  remind:
    runs-on: ubuntu-latest
    env:
      GITHUB_REPOSITORY: ${{ github.repository }}
      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      STALE_HOURS: '24'
    steps:
      - uses: actions/checkout@v4
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - name: Install dependencies
        run: pip install requests
      - name: Run reminder script
        run: python .github/scripts/stale_reminder.py
Enter fullscreen mode Exit fullscreen mode

The workflow uses the default GITHUB_TOKEN, which has permission to comment on PRs in the same repository. Adjust the cron expression if you need a different frequency.

Tradeoffs and Pitfalls

  • Over‑reminding: If the threshold is too low, you’ll spam reviewers and they may start ignoring the messages. Start with a higher threshold (e.g., 24 hours) and tune based on team feedback.
  • False sense of security: Automation does not replace a culture of timely feedback. Use the bot as a safety net, not a substitute for explicit expectations.
  • Permission limits: Fine‑grained tokens may lack the issues:write scope needed to comment. Ensure your token or the default GITHUB_TOKEN includes that permission.
  • Large PRs: The bot cannot assess size; consider coupling it with a label‑based system that flags huge changes for optional splitting.

Key Takeaways

  • Track median review time and age of open PRs to detect stalls early.
  • Choose a remedy that matches your team’s scale: manual reminders for small groups, bots for steady flow, rotation for burnout prevention.
  • Keep automation simple and observable; start with a conservative threshold and adjust.
  • Remember that tools support, but do not replace, clear expectations and a helpful mindset.

Source

If we do not stop to help each other, what do we become? – I expanded the source’s reflection on mutual aid into concrete, actionable steps for maintaining healthy code review practices.

Support this work

These write-ups are researched and published with no paywall, sponsor, or tracking. If one saved you an afternoon, a small tip keeps them coming.

USDT, USDC or USDD · TRC-20 (Tron)

TFTNsfyomKrnUutRjBTGVULp19ByW29KbY
Enter fullscreen mode Exit fullscreen mode

Top comments (0)