This article grew out of work on a private autonomous multi-agent development system I run alongside my main projects. The six evidence layers and the rollout plan are not an abstract model — they are a contract that survived its real scenarios: polyglot code search for agents, separating evidence from actor in memory, retention in telemetry for long-running sessions, and mandatory traceability of every agent-driven change down to a commit.
Code intelligence stack 2026: building a map of the code, not a collection of AI toys
In a large codebase, the question “where is this function defined?” quickly turns into an investigation. You need to find every implementation and every caller, understand dependencies, check static violations, correlate code with real production behaviour, and then verify that the change didn’t break a neighbouring service. In a monorepo or a polyglot system this is no longer a job for plain grep, and no longer a job you can safely hand to a single chatbot.
By code intelligence I mean not a specific AI product, but a closed loop of six kinds of facts:
- Text and symbol facts — search across files, branches, commits, and definitions.
- Static facts — AST, types, data-flow, dependencies, and rule violations.
- Dynamic facts — traces, profiles, latency, errors, and load behaviour.
- Operational facts — logs, metrics, release-to-incident correlation, and retention.
- Relational / knowledge-graph facts — code-as-data queries, typed recipes, inter-repo symbol links, and multi-hop questions.
- Generative interpretation — an LLM that answers questions only over the layers above and shows the evidence behind its output.
The first five are the evidence layers around a single CODEBASE; the LLM is the actor that interprets them, not a sixth source of evidence. The illustration below visualises that contract.
I compared tools across six axes: code search, static analysis, runtime tracing, log aggregation, LLM-assisted investigation, and knowledge-graph code models. The primary sources were last verified on 2026-07-20. Where the data moves quickly, that is called out explicitly.
TL;DR
If you want a practical starter set rather than a marketing list, I would begin like this:
- For fast symbol search in unfamiliar code — Sourcegraph or Zoekt.
- For catching vulnerabilities before production — Semgrep and CodeQL in CI, layered with SAST and SCA (expansion in §2).
- For explaining “why is this slow” on a Linux service — perf/bpftrace plus Pyroscope or Parca for continuous profiling.
- For replacing “one AI chatbot for everything” — five evidence layers (search, static, runtime, telemetry, relational graph) plus an LLM actor on top, with the AI as the last link rather than the first.
This list does not say “install everything”. First decide which question you want to make cheap: find a symbol, catch a vulnerability, explain latency, or verify a change through an LLM.
How to choose: maturity over stars
For each tool I used four filters:
- is there a clear primary source and living documentation;
- how well it fits a production scenario and not just a demo;
- where the OSS / commercial / vendor-lock boundary lies (lock-in to a specific vendor);
- what happens to code, logs, and personal data under self-hosted and SaaS deployment.
“Mature” here means a stable production scenario and not in beta or maintenance mode (where the project is frozen). It is not a promise of zero bugs. Conversely, a beta tool may be fast and convenient, but it should not be put into a blocking gate (a CI check that fails the build) without explicit observation. But you already know that.
1. Code search: find the facts first
Code search is the most under-rated layer. Before you build a graph or wire an LLM, you must be able to answer “where is it declared”, “who calls it”, “in which branch did it change”, and “does the same problem exist in a neighbouring repo”.
Sourcegraph and Zoekt
Sourcegraph Code Search is the practical choice for the enterprise tier when you care about multi-host, multi-branch, multi-commit search, symbol search, batch changes, and a Cody layer on top of the index. Its strength is not just string search — it is a unified interface for cross-repo investigations. The cost of that depth is the commercial model, opaque pricing, and a separate privacy review for SaaS or AI features.
Zoekt is Apache-2.0 and a good base for an OSS-only scenario. It is a trigram-based engine that powers Sourcegraph Code Search. It is fast across large code volumes, supports regexp, boolean queries, and symbol ranking. Zoekt by itself is not a drop-in replacement for the full Sourcegraph platform: UI, repository sync, auth, and ops still need to be assembled.
The choice is simple: if you need a finished enterprise product and the licence works — Sourcegraph; if you value control over the data and have a team to integrate a platform — Zoekt.
OpenGrok and livegrep
OpenGrok stays a reasonable choice for Java-heavy and legacy environments. It has cross-references, multi-language support, web UI, and API. The downside is the JVM stack, the cost of full reindex, and tuning for very large repositories. The licence mix also needs review: the project is CDDL-1.0 with mixed components.
livegrep is narrower but convenient for a single-tenant setup. It is fast regex search across gigabytes of source with re2 and mmap indices. It fits an internal “Google Code Search” when you do not need a complex permission model or multi-repo semantics.
Hound is worth considering only if you need a very small deployment footprint and tolerate maintenance mode. I would not make it the base of a new strategic layer.
What not to do
Do not start with semantic search if you cannot do exact text and symbol search. Otherwise the LLM will paraphrase an incomplete index nicely, and the team will not be able to verify the answer with a simple re-query.
2. Static analysis: split fast feedback from deep investigation
Instead of one “magical” analyser, separate work by cost.
- Ruff — fast Python linter and formatter. It replaces several familiar tools and works well for every local run and every pull request.
- Semgrep — pattern-based SAST, SCA, and secrets scanning. YAML rules are easy to write and review. Good for team-specific rules and quick dangerous-pattern lookup.
- CodeQL — GitHub’s code-as-data engine. Queries run against the relational model of the parsed code and are especially useful for vulnerability hunting, data-flow, and re-running a single rule across many repositories. CodeQL sits at the boundary between STATIC and GRAPH: it is a deep analyser on the static side and a relational query engine on the graph side (see the six-layers diagram above).
- SonarQube Community Build — broad quality-gate layer with large language and rule coverage. Useful for portfolio reporting and unified gate rules, but the depth depends on the edition.
- ty — a fast Python type checker from the Astral ecosystem. As of 2026-07-23 it is still 0.0.x (latest release 0.0.63), despite a mature LSP, Pydantic support, and regular releases. It fits for experiments and non-blocking feedback, but should not be the single production gate without a separate observation window.
The practical shape: Ruff and local Semgrep rules give fast feedback; CodeQL and deep taint analysis run in CI; SonarQube collects cross-repo quality. Every finding must have an owner, a severity, a suppression reason, and a review date. Otherwise the tool just produces backlog.
3. Runtime tracing and profiling: code explains intent, production shows the fact
Static analysis does not show which branch actually runs or where time is spent. For Linux-native systems the base is perf (the Linux profiling subsystem) events, perf, ftrace, and Brendan Gregg’s tooling. They give hardware counters, kernel tracepoints, kprobes, uprobes, and USDT with low overhead.
bpftrace is convenient when you need a quick diagnostic query without a separate C program: syscalls, locking latency, network events, or process behaviour. It is mature, but it requires Linux with BPF support and is not a replacement for a full continuous-profiling system.
For JVM workloads you want async-profiler. It captures CPU and heap profiles, works with native frames, JIT, and GC threads, and can export to JFR and flame graphs. For a Java service this is usually a more useful starting point than a universal agent for every language.
For continuous profiling you can pick Pyroscope or Parca:
- Pyroscope fits well in the Grafana ecosystem if you already run Loki, Tempo, and Mimir.
- Parca uses an eBPF agent and the pprof format, so it is especially attractive for Go, Rust, C/C++, and Kubernetes without adding an SDK to the application.
A profile without context is useless. Every measurement must be tied to the build version, the service, the environment, and the time window. Otherwise the flame graph becomes a picture without causality.
4. Logs and observability: data standard first, then backend
The biggest observability decision is not “Loki or Elastic” — it is whether you will still be able to swap the backend without rewriting instrumentation.
OpenTelemetry is the first layer to put in. It is a vendor-neutral model, SDK, and Collector for traces, metrics, and logs. OpenTelemetry does not store data and is not a search system, but it establishes common semantic conventions, routing, and the boundary between the application and the backend.
The next choice depends on the shape of queries:
- Grafana Loki — label-based aggregation, cheap storage, good Grafana integration. Do not expect Elasticsearch-grade full-text: body lines are not indexed as deeply, and high-cardinality labels can hurt performance.
- Quickwit — S3-native search backend on Tantivy. Useful for large log and trace volumes with columnar storage. Delete tasks help build deletion flows, but they do not make a deployment GDPR-compliant on their own.
- OpenObserve — single-binary observability backend on Rust with SQL and PromQL. Convenient for a small self-hosted footprint, but AGPL-3.0 and a younger ecosystem require their own legal and operational review.
- Elastic Stack — mature option if you need deep full-text search, a rich ingest ecosystem, and SIEM scenarios. Licensing and storage cost need to be evaluated separately from the technical capabilities.
For self-hosted SaaS, AGPL may be acceptable, but embedding a component in a closed commercial product requires a legal review. Also decide retention, data residency, erase workflow, and log access in advance. You cannot promise deletion if the storage cannot reliably find and remove every copy.
5. LLM-assisted investigation: the model is not the source of truth
An LLM is useful when it shortens the path from a question to verifiable facts. It is dangerous when it produces confident explanations over an incomplete index.
Aider is a strong OSS option for terminal-based work. It builds a repository map, works with different models, can run lint and tests automatically, and commits the changes. Its natural role is a guided CLI helper, not an autonomous production deployer.
Cursor is convenient as an enterprise IDE, and Sourcegraph Cody is a natural fit for teams already on Sourcegraph that want to feed its code graph to the model. Both come with a price: commercial dependency, vendor-specific indexing, and the need to verify where source code, prompts, snippets, and search results are sent.
Devin is positioned as an autonomous coding agent for end-to-end tasks (issue → patch → PR). As of 2026-07 it remains a commercial product with a closed runtime; for code-intelligence scenarios it is best treated as a black box — there are no links to intermediate evidence, and a reproducible trace requires a separate vendor agreement.
Continue.dev can no longer be recommended as a base for new installations: per the continuedev/continue README, the repository is no longer maintained and is left read-only after the final 2.0 release. It is useful as a historical reference for prompt patterns, but it is not a current deployment target.
A minimum guardrail for any LLM tool:
- the model only sees the repositories and branches it is allowed to see;
- every answer includes links to files, symbols, commits, or runtime traces;
- changes land in a diff first, not directly in production;
- lint, tests, SAST, and secret scanning run after generation;
- access to sensitive code, logs, and personal data goes through a separate review.
If the model cannot surface evidence, it is a hypothesis, not a finding.
6. Knowledge graph: different graphs answer different questions
The term “code knowledge graph” is used for three different things.
OpenRewrite builds a Lossless Semantic Tree and applies type-aware recipes. It is a strong choice for mass refactoring and framework migrations: instead of a regex replace, the tool understands the structure of the program and can re-apply the recipe across multiple repositories. Parser maturity varies by language — Java is usually better covered than newer ones.
CodeQL stores code in a relational model that can be used in practice as a knowledge model: queries express relationships between data sources, calls, types, and sinks. Especially useful for security and cross-repo searches for vulnerable patterns.
SCIP is the Sourcegraph Code Intelligence Protocol index format. It is useful as an interoperability layer: separate language indexers can publish symbols and relationships in a common format. The SCIP ecosystem is still mostly tied to Sourcegraph.
Sourcegraph Code Graph is a commercial production-grade graph for cross-repo symbol navigation and AI-grounded Q&A. It is most valuable when you already have Sourcegraph-scale code search. For a small repo, a separate graph is almost always over-engineering.
The practical rule: a graph is for relationships and multi-hop questions — “which services depend on this type?” or “which calls lead to this sink?” — not for every string search. For local fact lookup, the plain index is faster, cheaper, and easier to verify.
Reference architecture
A minimal production shape can look like this:
Git repositories
-> Zoekt or Sourcegraph index
-> SCIP and symbol metadata
-> Semgrep, CodeQL, SonarQube, Ruff
-> OpenTelemetry Collector
-> logs, traces, profiles
-> Loki, Quickwit, OpenObserve, or Elastic
-> Aider, Cody, or another guarded LLM interface
-> diff, tests, SAST, reviewer
The sequence matters. The LLM should not be the first link. First build the indexes and the telemetry contracts, then add a model that can go from a question to evidence. If you swap the order, you get an expensive chat with stale context, not code intelligence.
Three practical deployment profiles
On the diagram above, compliance-sensitive concerns are shown as a horizontal overlay across both main profiles rather than a third column. Compliance is orthogonal to the tool choice: you can run OSS or GitHub-centric, and in both cases you can enable a compliance-sensitive mode. The two questions — which vendor mix? and which compliance posture? — are independent.
OSS-only and self-hosted
Zoekt, Semgrep OSS, CodeQL CLI, Ruff, perf/bpftrace, OpenTelemetry, Loki or Quickwit, Aider, and OpenRewrite. This gives you control over code and data, but requires in-house work on auth, indexing, UI, retention, and upgrades.
GitHub-centric enterprise
Sourcegraph or GitHub-native search, CodeQL, Semgrep, SonarQube, OpenTelemetry, the chosen observability backend, Cody or Cursor. Faster to start, but you have to nail down SaaS boundaries, indexing cost, and an exit plan (a playbook for migrating off the vendor).
Compliance-sensitive production
Start not with an LLM, but with a data map: which repositories, logs, and profiles contain personal data, where they are stored, who has access, and how deletion requests are executed. Then pick self-hosted indexing, an OpenTelemetry Collector with filtering, a backend with clear retention, and only then an AI tool with a verified data-handling policy.
Four-stage rollout plan
- Stage 1 — inventory. Lock in languages, repositories, branches, owners, recurring investigation questions, and data classification. Pick one pilot repository.
- Stage 2 — static baseline. Wire up Ruff or a language-native linter, Semgrep, and one deep analyser. Strip the noise, assign owners, and start a regression corpus.
- Stage 3 — runtime loop. Turn on OpenTelemetry, collect minimal traces and logs, add perf or async-profiler for one critical service. Tie every event to a build revision.
- Stage 4 — guarded AI. Connect Aider, Cody, or Cursor only to the pilot scope. Require citations, diff, tests, and security checks. Compare model answers against a fixed investigation set.
After the pilot, measure not the number of tools or token spend, but time-to-find, time-to-explain, the share of findings with reproducible evidence, the false-positive rate, and the time from a runtime signal to a fix.
Exit gates
Each stage on the diagram ends with an explicit exit gate. The next stage starts only when the gate is satisfied — this is a sequencing rule, not a calendar rule. A gate is a one-line verifiable criterion, not a goal:
- Gate 1 — pilot repository. Owner assigned, scope locked (which repositories, branches, environments), a data-classification document exists. Without this, stage 2 will optimise noise, not findings.
- Gate 2 — owned findings. Every static-analysis finding has an owner, a severity, and either a fix path or an explicit suppression reason. The linter and Semgrep are wired; one deep analyser (CodeQL or another) emits curated output.
- Gate 3 — release-to-event link. Every telemetry event (trace, log line, profile sample) carries a build revision, a service, an environment, and a time window. A flame graph or log query without this context cannot answer “is this our change?”.
- Gate 4 — cited & gated. Every model answer includes links to files, symbols, queries, or traces; every generated diff passes tests, SAST, and human review; the LLM is connected only to the pilot scope. Without this, stage 4 produces chat, not workflow.
If a gate is not passed, fix the gap first and then continue. The pilot repository and the gates are the only durable artefacts that survive the rollout; everything else can be replaced.
Anti-patterns
- Choosing by stars. Popularity does not replace the permission model, retention, reindex cost, and operational responsibility.
- Building a graph before a baseline search. A graph will complicate the system but will not fix a bad index.
- Using LLM-as-source-of-truth. A model answer without references to code, queries, or traces is just a hypothesis.
- Wiring SaaS to sensitive code without review. Indexing and prompts can carry more data than the UI suggests.
- Treating AGPL as a technical detail. For embedded closed-source scenarios this is a separate legal decision.
- Putting a beta tool into a blocking gate. For
tyyou need observation mode and explicit version pinning until it leaves 0.0.x. - Storing logs without an erase plan. You cannot promise privacy if raw data, replicas, backups, and deletion deadlines are not described.
- Running every analyser on every commit. Separate fast feedback, PR checks, and nightly / full scans.
- Using a maintenance-mode project for a new core. Hound and the archived Continue can be useful as references, but not as a strategic foundation.
What to re-check before publishing
Code intelligence moves quickly, so this article is not a permanent catalogue:
- commercial pricing and roadmaps for Sourcegraph, Cursor, Cody, and Devin should be checked before each external release;
- Quickwit is still in 0.8.x (latest release v0.8.2 as of 2026-07-23), so breaking changes require pinning and a test upgrade;
tyshould be revisited after the 1.0 release or once a production benchmark against mypy / Pyright lands (still 0.0.63 on 2026-07-23, despite LSP and Pydantic support);- Continue.dev stays an archived reference: the
continuedev/continuerepository is read-only after the final 2.0 release, with no active development; - licences and AI-indexing terms must be checked against the current text before enterprise rollout;
- regulatory claims must be verified with the legal team, not the observability product page.
In a field like this, a re-fetch every six months is reasonable, and for commercial AI tooling and compliance-sensitive deployments it should happen before every substantial change.
Sources
Code search
- Sourcegraph Code Search: https://sourcegraph.com/docs/code-search
- Zoekt: https://github.com/sourcegraph/zoekt
- OpenGrok: https://github.com/oracle/opengrok
- livegrep: https://github.com/livegrep/livegrep
- Hound: https://github.com/hound-search/hound
Static analysis
- Semgrep: https://docs.semgrep.dev/
- CodeQL: https://codeql.github.com/docs/
- SonarQube: https://www.sonarsource.com/products/sonarqube/
- Ruff: https://github.com/astral-sh/ruff
- ty: https://github.com/astral-sh/ty
Runtime tracing and profiling
- Linux perf: https://www.brendangregg.com/perf.html
- perf-tools: https://github.com/brendangregg/perf-tools
- bpftrace: https://github.com/bpftrace/bpftrace
- async-profiler: https://github.com/async-profiler/async-profiler
- Pyroscope: https://pyroscope.io/docs/
- Parca: https://github.com/parca-dev/parca
Logs and telemetry
- OpenTelemetry: https://opentelemetry.io/docs/
- Grafana Loki: https://grafana.com/oss/loki/
- Quickwit: https://github.com/quickwit-oss/quickwit
- OpenObserve: https://github.com/openobserve/openobserve
- Elastic Logstash: https://github.com/elastic/logstash
LLM investigation and code models
- Aider: https://github.com/Aider-AI/aider
- Cursor: https://www.cursor.com/
- Sourcegraph Code Search and code-graph context: https://sourcegraph.com/docs/code-search
- Continue.dev status (archived, read-only after final 2.0): https://github.com/continuedev/continue
- Devin: https://www.cognition.ai/devin
- OpenRewrite: https://github.com/openrewrite/rewrite
Compliance references
- Federal Law N 152-FZ (Russian personal-data law), official publication: http://pravo.gov.ru/proxy/ips/?docbody=&nd=102108261
- Federal Law N 152-FZ, legal-database text: https://www.consultant.ru/document/cons_doc_LAW_61801/
- GDPR Article 17 (right to erasure): https://gdpr-info.eu/art-17-gdpr/
- Quickwit delete-tasks feature: https://quickwit.io/docs/operating/storage/