Software teams increasingly rely on AI to move faster without sacrificing quality. Whether you’re a developer, project manager, or architect, the question isn’t if AI can help—it’s how to apply it effectively in your context. One of the biggest contextual factors is your repository strategy: many small repositories (polyrepo) versus a single large repository (monorepo). The way you index, retrieve, and apply AI assistance to your codebase varies dramatically between these approaches.
This article explains concrete ways to use AI to increase throughput and how your repo topology impacts the tools and patterns you should adopt.
AI accelerates teams that already have strong engineering practices. The more consistent your patterns and the clearer your code boundaries, the more precisely AI can help.
What “Throughput” Means in Software Delivery
Before tooling, clarity: throughput is not just “more code per day.” It includes:
- Reduced cycle time: task start → production
- Faster, reliable reviews and merges
- Higher test coverage without slowing releases
- Lower context-switch costs
- Fewer regressions and rollbacks
AI contributes by automating mechanical work (tests, docs, summaries), augmenting cognition (search, explanations, impact analysis), and standardizing patterns (refactors, reviews).
Where AI Helps Most in the Development Lifecycle
1) Code Generation and Completion
- AI pair-programmers suggest code, patterns, and API usage.
- Best for repetitive glue code, simple transformations, scaffolding.
- Guardrails: linting, static analysis, and tests still decide what ships.
2) Code Search and Q&A Over Your Codebase
- AI-powered semantic search turns “Where is the auth middleware applied?” into a few retrieved snippets and a concise explanation.
- Powerful for onboarding, cross-repo architecture questions, and impact analysis.
3) Test Generation and Failure Diagnosis
- Suggest unit tests based on function signatures and examples.
- Create property-based tests and fuzz inputs for edge cases.
- Summarize failing CI logs and suggest fixes.
4) Code Review and Refactoring Assistance
- AI reviewers catch obvious smells, missing null checks, or non-idiomatic usage.
- Batch refactors across services or packages with pattern-aware suggestions.
- Keep policy and domain-specific rules in your prompts or validation scripts.
5) Issue Triage, Planning, and Documentation
- Summarize issue threads and logs into actionable tasks.
- Generate API or ADR drafts from code and diffs.
- Convert vague requests into concrete acceptance criteria.
Polyrepo vs. Monorepo: How Repo Topology Impacts AI
Your repo strategy changes how you index, retrieve, and scope AI over code.
Small Repositories (Polyrepo)
Strengths:
- Smaller codebases fit within an AI model’s context window more often, reducing retrieval complexity.
- Simpler permission boundaries map to organizational access control.
- Faster indexing and cheaper embedding stores.
Challenges:
- Cross-repo work (impact analysis, refactors) is harder—you need federation across indices.
- Knowledge can fragment; duplicated patterns across services confuse AI without shared context.
AI Tactics That Shine:
- Per-repo assistants with local embeddings and quick context windows.
- PR review bots that know the repo’s conventions and tests.
- Lightweight test generators and doc bots per service.
Large Single Repository (Monorepo)
Strengths:
- Global visibility enables AI to reason across packages and layers.
- Consistent build and dependency metadata helps automatic impact analysis.
- Centralized standards and tooling amplify AI automation.
Challenges:
- Indexing and retrieval at scale; naive “dump everything” exceeds context.
- Cost and latency: embeddings, search, and summarization need careful partitioning.
- Permissioning and privacy across teams within one repo.
AI Tactics That Shine:
- Hierarchical retrieval: organization → domain → package → files → symbols.
- Graph-aware tools that leverage dependency graphs and CODEOWNERS.
- Batched change proposals and multi-package refactor assistants with safeguards.
For more on repo trade-offs, see:
- Monorepo: Please don’t by Martin Fowler (balanced perspective)
- Why Google Stores Billions of Lines of Code in a Single Repository (scale trade-offs)
Architecting AI for Your Codebase
Design a Context Strategy
Models are only as good as the context they see. Plan for:
- System prompts: encode team conventions, style, and “do/don’t” rules.
- Retrieval: use embeddings and keyword search; combine them for robustness.
- Chunking: split files by semantic boundaries (functions/classes) rather than arbitrary sizes.
- Recency bias: prioritize recent diffs and touched modules.
Indexing Strategies by Repo Size
- Small repos:
- Embed all source and key docs ( List[str]:
return [
f for f in glob.glob(os.path.join(root, "**", "*"), recursive=True)
if os.path.isfile(f) and os.path.splitext(f)[1] in INCLUDE_EXT
]
def chunk_text(content: str, path: str, max_chars=800) -> List[Tuple[str, str]]:
chunks = []
start = 0
while start start + 200:
end = newline
chunks.append((path, content[start:end]))
start = end
return chunks
def build_index(paths: List[str]):
model = SentenceTransformer(MODEL_NAME)
vectors = []
metas = []
for p in paths:
try:
with open(p, "r", encoding="utf-8", errors="ignore") as f:
content = f.read()
for path, chunk in chunk_text(content, p):
metas.append({"path": path, "text": chunk})
vectors.append(model.encode(chunk))
except Exception as e:
console.print(f"[yellow]Skipping {p}: {e}")
if not vectors:
raise RuntimeError("No content found to index.")
mat = faiss.numpy_to_array(vectors).astype("float32")
index = faiss.IndexFlatIP(mat.shape[1])
faiss.normalize_L2(mat)
index.add(mat)
return index, metas, model
def search(query: str, index, metas, model, k=5):
qv = model.encode(query).astype("float32")
faiss.normalize_L2(qv.reshape(1, -1))
D, I = index.search(qv.reshape(1, -1), k)
results = []
for score, idx in zip(D[0], I[0]):
results.append((float(score), metas[idx]))
return results
if name == "main":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--root", default=".")
parser.add_argument("--query", required=True)
parser.add_argument("--save", default=".code_index.json")
args = parser.parse_args()
files = iter_files(args.root)
console.print(f"Indexing {len(files)} files...")
index, metas, model = build_index(files)
res = search(args.query, index, metas, model, k=7)
console.print(f"\nTop results for: [cyan]{args.query}[/cyan]\n")
for score, meta in res:
console.rule(f"{meta['path']} (score={score:.3f})")
console.print(meta["text"])
How to use:
- For small repos: run per repo; check in a Makefile target; rebuild on PR open.
- For monorepos: add a domain filter (e.g., restrict to “packages/payments”) before indexing; build multiple indices.
### Example B: Generate Commit Messages With a Local LLM
Automate consistent, informative commit messages from staged diffs. This speeds reviews and enforces a standard without manual effort.
file: .git/hooks/prepare-commit-msg
Make executable: chmod +x .git/hooks/prepare-commit-msg
Requires: an LLM locally accessible via 'ollama' CLI; adapt if using another endpoint.
#!/usr/bin/env bash set -euo pipefail
MSG_FILE="$1" if [ -n "${2-}" ]; then
Do not override merge or squash messages
exit 0 fi
DIFF="$(git diff --staged)" if [ -z "$DIFF" ]; then exit 0 fi
PROMPT=$(cat /dev/null || true)
Fallback if model unavailable
if [ -z "$SUMMARY" ]; then SUMMARY="chore: update code (manual message; LLM unavailable)" fi
printf "%s\n" "$SUMMARY" > "$MSG_FILE"