Nightly full reindexes are late and expensive. On every merge, detect changed paths, rechunk symbols, upsert vectors keyed by (path, chunk_id, commit), and tombstone deletes so code RAG stays within minutes of main without burning a Bedrock Titan/Cohere budget every night.
⚡ TL;DR: Diff
prev_sha...HEAD; re-embed only changed/added files; delete vectors for removed paths; store commit metadata for provenance; run on CodeBuild/Batch triggered by merge. Measure staleness (p95 minutes behind main) and embedding $. See OpenSearch vs pgvector and RAG evaluation.
Diff-driven worklist
# CI on merge to main
PREV=$(cat s3://embed-state/last_sha || git rev-parse HEAD~1)
CURR=$(git rev-parse HEAD)
git diff --name-status $PREV...$CURR > /tmp/changed.txt
def work_items(diff_lines: list[str]) -> tuple[list[str], list[str]]:
upsert, delete = [], []
for line in diff_lines:
status, path = line[0], line[2:].strip()
if status == "D":
delete.append(path)
elif status in ("A", "M", "T"):
if path.endswith((".ts", ".tsx", ".py", ".go")):
upsert.append(path)
elif status.startswith("R"):
# rename: delete old, upsert new — parse git diff -M carefully
pass
return upsert, delete
Chunk by symbols, not naïve 512-token windows
// Prefer function/class boundaries for code RAG
export function chunkTypeScript(source: string, path: string): Chunk[] {
// ts-morph or tree-sitter → symbol ranges
// ✅ each chunk: {path, symbol, startLine, endLine, text, hash}
// ❌ sliding windows that bisect signatures
return [];
}
Hash chunk text; skip re-embed when hash unchanged (reformats-only commits).
Upsert + tombstone
# OpenSearch example — analogous for pgvector
def upsert_chunks(chunks: list[dict], commit: str):
for c in chunks:
client.index(index="code", id=f"{c['path']}::{c['chunk_id']}", body={
**c,
"commit": commit,
"embedding": embed(c["text"]), # Bedrock embedding model
"deleted": False,
})
def tombstone(paths: list[str]):
for p in paths:
client.update_by_query(index="code", body={
"query": {"term": {"path.keyword": p}},
"script": {"source": "ctx._source.deleted = true"},
})
Retrieval must filter deleted=false. Optionally hard-delete after N days.
State and backfill
s3://embed-state/last_sha
s3://embed-state/runs/{sha}.json # counts, $ estimate, duration
Weekly: sample full-tree hash audit vs index to catch missed events. On disaster: full reindex job with the same chunker version — version your chunker like an API.
Illustrative cost: embedding ~1–3M tokens of changed code/day on a busy monorepo vs 80–200M for nightly full — order-of-magnitude savings.
Closing checklist
✅ Dos
– ✅ Merge-triggered incremental embed from git diff
– ✅ Symbol-aware chunking with content hashes
– ✅ Tombestone deletes; filter at query time
– ✅ Persist last_sha + per-run metrics on S3
– ✅ Audit weekly; version the chunker
❌ Don’ts
– ❌ Don’t rely only on nightly full reindex for eng chat
– ❌ Don’t leave deleted files retrievable
– ❌ Don’t re-embed unchanged hashes
– ❌ Don’t forget renames (delete old path keys)
– ❌ Don’t mix chunker versions in one index without namespaces
Related reading
- RAG on AWS: OpenSearch vs Aurora pgvector for Codebase Chat
- Bedrock Prompt Caching and Batch Inference: Cut Latency and Cost
- Amazon Bedrock Agents: Tool Use, Memory, and Production Guardrails
- AWS DynamoDB: Advanced Patterns for Production at Scale
Last updated on September 11, 2026
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.

Pingback: Hybrid Search for Code: BM25, Vectors, and Symbol Indexes Together - CheatCoders