How do we use Sourcegraph Batch Changes to open and track PRs across hundreds of repos for a dependency upgrade or API migration?
AI Codebase Context Platforms

How do we use Sourcegraph Batch Changes to open and track PRs across hundreds of repos for a dependency upgrade or API migration?

10 min read

When you’re staring down a dependency upgrade or API migration that touches hundreds of repositories, “just open a PR” stops being a realistic plan. You need a way to generate consistent changes, open pull requests everywhere, and then track those changes through checks and review until the last one is merged. This is exactly the problem Sourcegraph Batch Changes is built to solve.

Below is how I’d structure a repeatable workflow using Batch Changes to open and track PRs across hundreds of repos, based on running similar migrations in regulated enterprises with GitHub + Perforce and thousands of repositories.


Why use Batch Changes for dependency upgrades and API migrations?

AI-driven code growth and multi-repo sprawl mean most critical changes aren’t local anymore. You might have:

  • A library upgrade (e.g., v2v3) that needs import and call site updates.
  • An internal API with a breaking signature change.
  • A framework migration (e.g., HTTP client, logging library, ORM).

Doing this repo by repo is slow and error-prone. Scripts help, but they don’t give you:

  • End-to-end visibility: Which repos are done, failing, or blocked in review?
  • Safe iteration: Can you tweak the change and re-apply it consistently across all repos?
  • Integration with existing workflows: Real PRs in GitHub, GitLab, Bitbucket, Gerrit, Perforce, etc.

Batch Changes gives you a single declarative spec that:

  • Finds the right set of repositories.
  • Applies a scripted or declarative change per repo (or per file).
  • Opens PRs/changesets in each code host.
  • Lets you track and manage those PRs from one UI until they’re merged.

Prerequisites and setup

Before you run a dependency upgrade or API migration with Batch Changes, confirm:

  • Sourcegraph is connected to your code hosts.
    You should see the relevant repositories in Sourcegraph from GitHub, GitLab, Bitbucket, Gerrit, or Perforce—whether that’s 100 or 1M repositories.

  • You have Batch Changes enabled.
    This is a separate capability alongside Code Search, Deep Search, Monitors, and Insights. Make sure your instance or organization has it turned on.

  • Permissions are aligned with your access model.
    Batch Changes respects the same RBAC and repo permissions as your code hosts and identity provider:

    • SSO via SAML, OpenID Connect, or OAuth
    • User lifecycle via SCIM
    • Role-based access controls (RBAC) for who can create and apply batch changes
  • Your change is testable and idempotent.
    The script or transformation you’ll run per repo should be:

    • Safe to re-run.
    • Easy to validate with tests/linters.

Once that’s in place, you’re ready to implement the workflow.


Step 1: Use Code Search / Deep Search to scope the migration

First, you need to know where your dependency or API actually lives. This is where Sourcegraph’s code understanding layer matters.

For a dependency upgrade, you might search for:

file:(package.json|package-lock.json|yarn.lock) "my-library"

For a Java API migration, you might search for:

lang:java repo:your-org/.* "OldApiClient" OR "OldApiClientBuilder"

For a Go module:

lang:go file:go\.mod "github.com/your-org/oldpkg"

Use Code Search or Deep Search (Agentic AI Search) to:

  • Identify all repositories that reference the dependency or API.
  • Understand common call patterns and edge cases.
  • Confirm the presence of tests that will validate your changes.

Once you have the right repositories and patterns, you’ll feed that context into your batch spec.


Step 2: Design the change logic

Batch Changes doesn’t dictate how you edit code; it orchestrates it. Your change logic can be:

  • A shell script that:
    • Updates version strings.
    • Runs a formatter or code generator.
  • A Comby pattern (structured search-and-replace).
  • A custom script in Python, Go, Node, etc.

For example, if you’re updating a Go import path:

#!/usr/bin/env bash
set -euo pipefail

# Update go.mod
if grep -q "github.com/old-org/oldpkg" go.mod; then
  go get github.com/new-org/newpkg@v1.2.3
fi

# Update imports in .go files
comby \
  'import [:[anything]] "github.com/old-org/oldpkg":[@rest]' \
  'import :[anything] "github.com/new-org/newpkg":[@rest]' \
  -matching .go -in .

go fmt ./...

For an API migration (e.g., OldApiClientNewApiClient) in Java:

#!/usr/bin/env bash
set -euo pipefail

comby \
  'OldApiClient(:[args])' \
  'NewApiClient(:[args])' \
  -matching .java -in .

./gradlew test -q || echo "Tests failed; please investigate" >&2

Keep this script in a repo or inline it in your batch spec.


Step 3: Write a batch spec for your dependency upgrade or API migration

A batch spec is a declarative YAML file that tells Sourcegraph:

  • Which repos to target.
  • What to run in each repo.
  • How to title and describe the resulting PRs.

Here’s a concrete example for a dependency upgrade:

name: dependency-upgrade-my-library-v3
description: >
  Upgrade my-library to v3 across all repositories and update usage patterns
  where needed.

on:
  - repositoriesMatchingQuery: |
      (file:(package.json package-lock.json yarn.lock) "my-library") fork:no

steps:
  - run: |
      ./scripts/upgrade-my-library.sh
    container: alpine:3.18

  - run: |
      if [ -d .git ]; then
        git status --porcelain
      fi
    container: alpine:3.18
    outputs:
      has_changes:
        value: $(
          [ -n "$(git status --porcelain)" ] && echo "true" || echo "false"
        )

changesetTemplate:
  title: "chore: upgrade my-library to v3"
  body: |
    This PR upgrades **my-library** to v3 and updates usages accordingly.

    - Performed with [Sourcegraph Batch Changes](https://sourcegraph.com/batch-changes)
    - Scoped to repositories referencing `my-library`
    - Please run your service-level tests before merging.

  commit:
    message: "chore: upgrade my-library to v3"
  branch: "chore/upgrade-my-library-v3"
  published: true

An example for an API migration:

name: api-migration-oldapi-to-newapi
description: >
  Migrate OldApiClient usage to NewApiClient across all Java services.

on:
  - repositoriesMatchingQuery: |
      lang:java "OldApiClient" fork:no archived:no

steps:
  - run: |
      ./scripts/migrate-oldapi-to-newapi.sh
    container: openjdk:17

changesetTemplate:
  title: "refactor: migrate OldApiClient to NewApiClient"
  body: |
    This PR replaces `OldApiClient` usage with `NewApiClient`.

    - Automated with Sourcegraph Batch Changes
    - Please review any TODO comments and run integration tests
    - If this change isn’t applicable, you can close this PR and leave a note.

  commit:
    message: "refactor: migrate OldApiClient to NewApiClient"
  branch: "refactor/migrate-oldapi-to-newapi"
  published: true

Key things to notice:

  • repositoriesMatchingQuery mirrors the Code Search query you used to scope the migration.
  • steps runs your change logic inside a container per repo.
  • changesetTemplate standardizes commit message, branch name, PR title, and body.

Step 4: Preview the batch change before opening PRs

You don’t want to open 400 PRs just to discover your script missed a corner case. Batch Changes gives you a safe preview flow.

  1. Save your batch spec as batch-spec.yml.
  2. Use the Sourcegraph CLI (or UI) to create a preview:
    src batch preview -f batch-spec.yml
    
  3. The preview shows:
    • Which repositories will be affected.
    • The diff for each repository.
    • How many changesets will be created.

Use this preview to:

  • Spot repos that match the search but should be excluded (e.g., archived, deprecated).
  • Verify that the change compiles conceptually (e.g., import swaps look correct).
  • Adjust the spec (queries, steps, or templates) before publishing.

You can iterate on the spec and re-run the preview as many times as needed. No PRs are opened until you explicitly apply and publish.


Step 5: Apply the batch change and open PRs across hundreds of repos

Once the preview looks good:

  1. Apply the batch change:

    src batch apply -f batch-spec.yml
    

    Or apply directly from the preview in the UI.

  2. Batch Changes will:

    • Clone each target repository.
    • Run your steps in the container environment.
    • Create branches and commits.
    • Open PRs (or equivalent changesets) on your configured code hosts.

This is where the platform-level design matters. Whether your repos are in GitHub, GitLab, Bitbucket, Gerrit, or Perforce—across one host or many—Batch Changes handles the fan-out for you.


Step 6: Track PRs until every migration is merged

The hard part isn’t always opening PRs; it’s tracking them.

After applying your batch change, you get a central dashboard with:

  • Status per changeset:
    • Open / merged / closed / draft.
    • CI status (passing, failing, pending).
  • Filters:
    • By repo, code host, state, and label.
  • Bulk actions (depending on configuration):
    • Retry publishing.
    • Close or update changesets.

This becomes your control panel for the migration:

  • Use it in weekly reviews to see:
    • How many services merged the dependency upgrade.
    • Which teams are blocked on CI failures or review.
  • Drill into outliers:
    • PRs with failing tests indicating subtle API usage differences.
    • Repositories where the change doesn’t apply cleanly.

If you want an organizational view over time, pair this with Code Insights:

  • Create dashboards that:
    • Track the count of OldApiClient vs NewApiClient references over time.
    • Show how many repositories still depend on old versions.
  • Use Insights to demonstrate migration progress to leadership, risk, or compliance.

Handling edge cases and failures

Large-scale changes rarely go 100% smoothly. A few strategies that work well in practice:

Repos that should be excluded

Sometimes the search is right, but the repo is special (archived, third-party fork, PoC). You can:

  • Use additional filters in repositoriesMatchingQuery:
    on:
      - repositoriesMatchingQuery: |
          "my-library" fork:no archived:no repo:^your-org/
    
  • Or exclude specific repos explicitly:
    on:
      - repositoriesMatchingQuery: |
          "my-library" fork:no
    scopeQuery: |
      -repo:your-org/archive-repo -repo:your-org/forked-library
    

CI failures and manual follow-up

If some PRs fail CI:

  • Use the Batch Changes UI to filter to failing changesets.
  • Triage whether:
    • The script needs refinement (update the batch spec and re-run).
    • The repo has truly unique code that requires manual fixes.

You can update the batch spec and run another apply to modify existing branches or add new ones where necessary.

Re-running the migration or bumping versions again

When you need to bump to v4 later, reuse your patterns:

  • Update the batch spec:
    • New version number.
    • New branch and commit naming.
  • Repeat:
    • Search → spec → preview → apply → track.

This gives you a repeatable migration playbook rather than a one-off fire drill.


Security, governance, and auditability

In regulated environments, the question isn’t just “Can we do this?” but “Can we do this in a controlled, auditable way?”

Batch Changes helps by:

  • Respecting existing authentication and authorization:
    • SSO via SAML, OpenID Connect, OAuth.
    • Central user provisioning via SCIM.
    • RBAC for who can create, apply, or administer batch changes.
  • Keeping code and insights inside your environment:
    • Sourcegraph offers SOC2 Type II + ISO27001 Compliance.
    • Zero data retention for LLM inference, so AI-assisted searches don’t retain your code.

Combined, this gives you an enterprise-ready way to run high-impact codebase changes without bypassing governance.


Example: Using Batch Changes for a dependency upgrade end to end

To make this concrete, here’s how I’d run a Java HTTP client library upgrade across 300 services:

  1. Scope the work with Code Search:

    • lang:java "OldHttpClient" to find all repos using the old client.
  2. Draft the migration script:

    • migrate-http-client.sh that:
      • Rewrites imports.
      • Adjusts constructor calls.
      • Updates configuration objects.
  3. Author the batch spec:

    • name: http-client-upgrade-old-to-new
    • Query: the search above, limited to repo:^my-org/.
    • Steps: run migrate-http-client.sh in openjdk:17.
    • changesetTemplate: standardized branch, commit, and PR body.
  4. Preview:

    • Run src batch preview -f http-client-upgrade.yml.
    • Validate a sampling of repos and diffs.
  5. Apply and publish:

    • Run src batch apply -f http-client-upgrade.yml.
    • Let Batch Changes open 300 PRs across GitHub and GitLab.
  6. Track and resolve:

    • Use the Batch Changes UI to:
      • Monitor CI.
      • Coordinate with teams that need manual follow-up.
    • Use Code Insights to track the decreased count of OldHttpClient usage.

Within hours—not weeks—you have consistent, auditable change across your entire codebase.


Final recommendations

Use Sourcegraph Batch Changes for dependency upgrades and API migrations when:

  • The change spans dozens to thousands of repositories.
  • You need consistent edits, centrally tracked PRs, and clear visibility into progress.
  • You want to pair automation with governance, RBAC, and compliance instead of ad-hoc scripts.

Keep the loop tight:

  1. Use Code Search / Deep Search to understand where and how the dependency or API is used.
  2. Encode your change as a repeatable script or Comby pattern.
  3. Capture it in a batch spec with clear queries and templates.
  4. Preview, then apply and let Batch Changes open PRs across all repositories.
  5. Use the Batch Changes UI and Code Insights to drive the migration to completion.

If you’re ready to run your first cross-repo dependency upgrade or API migration with Sourcegraph Batch Changes, you can get hands-on guidance or a tailored walkthrough here:

Get Started