-
-
Notifications
You must be signed in to change notification settings - Fork 11
Incremental Indexing
Incremental indexing makes SCIP index updates O(changed files) instead of O(entire repo). After editing a file, the index updates in seconds instead of requiring a full reindex.
Availability: Go projects only (v7.4+). Other languages fall back to full reindexing.
Full SCIP indexing scans your entire codebase, which can take 30+ seconds for large projects. This creates friction:
- During development: You edit one file but wait 30s for the index to update
- In CI/CD: Every commit triggers a full reindex even if only one file changed
- With watch mode: Frequent reindexes burn CPU and slow down your machine
Incremental indexing solves this by only processing changed files.
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Change Detection│ ──► │ SCIP Extraction │ ──► │ Delta Application│
│ (git diff -z) │ │ (changed only) │ │ (delete+insert) │
└─────────────────┘ └──────────────────┘ └─────────────────┘
CKB detects changes using git:
git diff --name-status -z <last-indexed-commit> HEADThe -z flag uses NUL separators, correctly handling paths with spaces or special characters.
Tracked change types:
- Added - New .go files
- Modified - Changed .go files
- Deleted - Removed .go files
- Renamed - Moved/renamed .go files (tracks old path for cleanup)
Fallback: For non-git repos, CKB falls back to hash-based comparison against stored file hashes.
CKB runs scip-go to regenerate the full SCIP index (protobuf doesn't support partial updates), but then:
- Loads the index into memory
- Iterates documents, only processing those in the changed set
- Extracts symbols and references for changed files only
- Skips unchanged documents entirely
This means even though scip-go runs on the full codebase, CKB only does the expensive database work for changed files.
For each changed file, CKB applies updates using delete+insert:
Modified file.go:
1. DELETE FROM file_symbols WHERE file_path = 'file.go'
2. DELETE FROM indexed_files WHERE path = 'file.go'
3. INSERT new symbols and file state
Renamed old.go → new.go:
1. DELETE using old path
2. INSERT using new path
This approach is simple and correct—no complex diffing logic.
# Incremental by default
ckb index
# Output for incremental update:
Incremental Index Complete
--------------------------
Files: 3 modified, 1 added, 0 deleted
Symbols: 15 added, 8 removed
Refs: 42 updated
Time: 1.2s
Commit: abc1234 (+dirty)
Accuracy:
OK Go to definition - accurate
OK Find refs (forward) - accurate
!! Find refs (reverse) - may be stale
!! Call graph - may be stale
Run 'ckb index --force' for full accuracy (47 files since last full)# Full reindex (ignores incremental)
ckb index --forceUse --force when:
- You need 100% accurate reverse references
- Call graph analysis is critical
- After major refactoring across many files
- When incremental reports issues
Incremental indexing maintains forward accuracy but may have stale reverse references.
| Query Type | After Incremental | Notes |
|---|---|---|
| Go to definition | Always accurate | Definitions are in the changed files |
| Find refs FROM changed files | Always accurate | We re-extracted these references |
| Find refs TO symbols in changed files | May be stale | Other files' refs weren't updated |
| Call graph (callees) | Always accurate | We know what changed files call |
| Call graph (callers) | May be stale | Other files' calls weren't updated |
| Symbol search | Always accurate | Symbol table is fully updated |
Consider this scenario:
// utils.go (unchanged)
func Helper() { ... }
// main.go (changed - removed call to Helper)
func main() {
// Helper() <- removed this line
}After incremental indexing:
-
main.gois re-indexed correctly (no longer references Helper) -
utils.gois NOT re-indexed (unchanged) - CKB's stored references still show
main.go→Helperfromutils.go's perspective
This is the "caller-owned edges" invariant: references are owned by the FROM file, not the TO file.
Impact: When you ask "what calls Helper?", CKB might still show the deleted call from main.go until you run ckb index --force.
CKB automatically triggers a full reindex when:
| Condition | Reason |
|---|---|
| No previous index | Nothing to diff against |
| Schema version mismatch | Database structure changed |
| No tracked commit | Can't compute git diff |
| >50% files changed | Incremental overhead exceeds full reindex |
You'll see messages like:
Full reindex required: schema version mismatch (have 5, need 6)
CKB tracks index state in the database:
Index State:
State: partial (3 files since last full)
Commit: abc1234
Dirty: yes (uncommitted changes)
States:
-
full- Complete reindex, all references accurate -
partial- Incremental updates applied, reverse refs may be stale -
full_dirty/partial_dirty- Uncommitted changes detected
Incremental indexing uses these settings (in .ckb/config.json):
{
"incremental": {
"threshold": 50,
"indexTests": false,
"excludes": ["vendor", "testdata"]
}
}| Setting | Default | Description |
|---|---|---|
threshold |
50 | Fall back to full reindex if >N% of files changed |
indexTests |
false | Include _test.go files in change detection |
excludes |
["vendor"] |
Paths to exclude from change detection |
| Scenario | Full Index | Incremental |
|---|---|---|
| Small project (100 files) | ~2s | ~0.5s |
| Medium project (1000 files) | ~15s | ~1-2s |
| Large project (10000 files) | ~60s | ~2-5s |
| Single file change | ~60s | ~1s |
The key insight: incremental time is proportional to changed files, not total files.
Current limitations that may be addressed in future versions:
- Go only - Other languages always do full reindex
-
Reverse refs may be stale - Use
--forcewhen accuracy is critical - Call graph staleness - Callers of changed symbols may be outdated
- No partial SCIP - Still runs full scip-go, just processes less output
Check that:
- You're in a git repository
- The previous index completed successfully
- Schema version matches (may need
--forceafter CKB upgrade)
If incremental takes as long as full reindex:
- Check how many files changed (
git status) - If >50% changed, CKB falls back to full automatically
- Large individual files still take time to process
If you're seeing phantom references:
# Force full reindex
ckb index --forceThis rebuilds all references from scratch.
-
User Guide - CLI commands including
ckb index - Performance - Latency targets and benchmarks
- Configuration - All configuration options