How OpenCodeReview Handles Large Changesets: Scalable Architecture Explained
OpenCodeReview (OCR) processes massive pull requests by breaking changesets into deterministic batches, running parallel workers, and streaming LLM responses to control memory usage.
When reviewing pull requests with thousands of files or megabytes of changes, most code review tools struggle with memory exhaustion, timeouts, or non-deterministic output. OpenCodeReview, Alibaba's open-source code review agent, solves this through a multi-layered approach that normalizes input, batches files intelligently, and persists intermediate results. This article examines how the OCR codebase in alibaba/open-code-review scales to enterprise-sized changesets.
Normalizing Input: The Unified Diff Model
Every input to OCR—whether a git diff or a full file scan—converts to a consistent internal representation. In internal/model/diff.go, the Diff struct encapsulates all necessary metadata:
type Diff struct {
OldPath string `json:"old_path"`
NewPath string `json:"new_path"`
Diff string `json:"diff"`
NewFileContent string `json:"new_file_content"`
// ...
}
This normalization ensures the downstream pipeline remains identical regardless of input source. For scenarios without a unified diff—such as scanning entire files—OCR creates synthetic Diff objects from ScanItem structs defined in internal/model/scan.go.
Full-Scan Mode for Diff-Heavy Changesets
When a changeset exceeds configurable thresholds for file count or diff size, OCR switches to full-scan mode. Rather than attempting to parse unwieldy diffs, this mode reads each file's complete content:
// ScanItem carries full file content when diff parsing is impractical
type ScanItem struct {
Path string
Content string // Full file contents
// Diff field intentionally empty in scan mode
}
According to the source in internal/model/scan.go:
"The Diff field stays empty since scan mode has no unified diff; NewFileContent carries the whole file so resolver.resolveFromFileContent can still find the source lines."
This design preserves line number resolution capabilities without requiring diff parsing, enabling OCR to handle PRs with extensive binary modifications or generated code where diffs become meaningless noise.
Batching Strategy: Grouping Files for Efficient Processing
The core scaling mechanism lives in internal/scan/batch.go. OCR supports three deterministic batching strategies controlled by the BatchStrategy type:
type BatchStrategy string
const (
BatchNone BatchStrategy = "none" // One file per batch
BatchByLanguage BatchStrategy = "by-language" // Group by extension
BatchByDirectory BatchStrategy = "by-directory" // Group by top-level dir
)
- BatchNone: Each file becomes its own batch—simplest, highest parallelization
- BatchByLanguage: Groups
.go,.py,.javafiles separately—useful when language-specific context matters - BatchByDirectory: Colocates files from
src/auth/,src/api/, etc.—preserves architectural boundaries
After grouping, files within each group are sliced into chunks of configurable BatchSize (default: 10). The groupBatches and batchKeyFunc implementations in batch.go ensure deterministic ordering by sorting group keys with sort.Strings(keys), making outputs reproducible and cacheable.
Parallel Dispatch with Background Persistence
Each batch executes through independent workers spawned by the scan dispatcher in internal/scan/agent.go. This architecture delivers two critical benefits for large changesets:
- Multi-core utilization: Workers run identical pipelines (parse → LLM request → comment generation) on disjoint file subsets
- Latency control: Review time scales with batch size rather than total PR size
For resilience, OCR implements a background file mechanism in cmd/opencodereview/background_file.go. Intermediate LLM responses serialize to disk, enabling:
- Crash recovery without reprocessing completed batches
- CLI resume capability for interrupted long-running reviews
- Idempotent re-execution of partially completed reviews
Streaming LLM Responses to Constrain Memory
The LLM client in internal/llm/client.go processes responses as streaming chunks rather than buffering complete replies. This approach:
- Eliminates memory spikes from large generated reviews
- Validates usage limits incrementally
- Merges partial responses into final batch results
Memory pressure stays bounded regardless of response length because the full output never materializes in memory simultaneously.
Deterministic Output and Intelligent Caching
OCR guarantees reproducible results through:
- Sorted batch keys:
sort.Strings(keys)ensures consistent processing order - Content-addressed caching: Batch results cache to
.ocr/cache/using hashes of file lists and contents - Independent batch processing: No cross-batch state dependencies
Re-running the same PR becomes nearly instant when cached results exist, supporting iterative review workflows and CI/CD integration.
CLI Controls for Tuning Large Changeset Handling
Users tune OCR's scaling behavior through flags defined in cmd/opencodereview/flags.go:
| Flag | Purpose | Default |
|---|---|---|
--batch-size <N> |
Files per chunk | 10 |
--batch-strategy <mode> |
Grouping policy | none |
--scan |
Force full-scan mode | off |
# Review a 500-file PR grouped by language, 20 files per batch
ocr review \
--repo https://github.com/example/monorepo \
--pr 456 \
--batch-strategy by-language \
--batch-size 20
# Force full-scan when diff contains many binary files
ocr review --repo . --scan --batch-strategy by-directory
Programmatic Batch Control
For custom integrations, the batch engine exposes clean APIs:
import (
"github.com/alibaba/open-code-review/internal/model"
"github.com/alibaba/open-code-review/internal/scan"
)
func reviewLargePR(items []model.ScanItem) [][]model.ScanItem {
// Group by directory, 15 files per batch
return scan.GroupBatches(items, scan.BatchByDirectory, 15)
}
Key Source Files
Understanding OCR's large-changeset handling requires familiarity with these components:
internal/model/diff.go— CoreDiffstruct definitioninternal/model/scan.go—ScanItemand synthetic diff generationinternal/scan/batch.go— Batching strategies and chunking logicinternal/scan/agent.go— Worker dispatch and parallel processingcmd/opencodereview/background_file.go— Intermediate result persistenceinternal/llm/client.go— Streaming response handlingcmd/opencodereview/flags.go— CLI configuration for batch controls
Summary
OpenCodeReview handles large changesets through seven coordinated mechanisms:
- Input normalization: All sources convert to uniform
Diffstructures - Full-scan fallback: Bypasses problematic diffs for massive changes
- Configurable batching: Groups files by language, directory, or individually
- Deterministic chunking: Fixed-size batches with sorted, reproducible ordering
- Parallel workers: Multi-core processing of independent batches
- Background persistence: Disk-based crash recovery and resume capability
- Streaming LLM I/O: Bounded memory regardless of response size
These techniques enable OCR to review PRs with thousands of files predictably, without memory exhaustion or timeout failures.
Frequently Asked Questions
What is the maximum PR size OpenCodeReview can handle?
There is no hard limit imposed by OCR's architecture. The tool processes PRs with thousands of files by batching them into manageable chunks. Practical limits depend on available disk space for background files and LLM API rate limits rather than OCR's internal memory constraints.
How does batching affect review quality?
Batching preserves quality through deterministic grouping strategies. Files in the same batch share context naturally—either linguistic similarity (by-language) or architectural proximity (by-directory). The none strategy maximizes parallelization but may miss cross-file patterns; larger batch sizes increase context at the cost of slower per-batch processing.
Can I resume a review if the process crashes?
Yes. OCR's background file mechanism in cmd/opencodereview/background_file.go persists intermediate LLM responses to disk. When restarted, the CLI detects completed batches in .ocr/cache/ and skips already-processed chunks, continuing from the point of interruption.
When should I use full-scan mode versus diff mode?
Use full-scan mode (--scan) when: diffs exceed reasonable parsing time, the repository contains many binary or generated files, or you need consistent line number resolution regardless of diff complexity. Diff mode is preferred for smaller, source-focused changesets where incremental review history matters.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →