Code intelligence stack 2026: building a map of the code, not a collection of AI toys
Six evidence layers, four evidence-loop stages, three deployment profiles, and a four-week rollout — a practical code intelligence stack for large polyglot codebases.
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:
Textual and symbolic 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 behaviour under load.
Operational facts — logs, metrics, release-to-incident correlation, and retention.
Relational / knowledge-graph facts — code-as-data queries, type-aware recipes, symbol links between repositories, and multi-hop questions.
Generative interpretation — an LLM that answers questions only on top of the sources above and shows where the answer came from.
The first five are evidence layers around a single CODEBASE; the LLM is the actor that interprets them, not a sixth source of evidence. The illustration at the top of this section visualises this contract.
I compared tools along six axes: code search, static analysis, runtime tracing, log aggregation, LLM-assisted investigation, and knowledge-graph code models. Primary sources were verified on 20 July 2026. Where data changes quickly, that is flagged in place.
TL;DR
If you don't want a marketing checklist but a practical starter, I'd begin here:
If you need to find a symbol in someone else's code — Sourcegraph or Zoekt.
If you need to catch vulnerabilities before production — Semgrep + CodeQL (GitHub's code-as-data engine: code is converted into a relational model, queries are written in QL to find vulnerabilities and patterns.) in CI; SAST (Static Application Security Testing — static security analysis without running the application; looks for vulnerabilities via patterns and data-flow.) and SCA (Software Composition Analysis — analysis of open-source dependencies for known CVEs and licence conflicts.) are two separate layers.
If you need to explain "why is it slow" in a Linux service — perf/bpftrace + Pyroscope/Parca for continuous profiling.
If you need to replace "one AI chatbot for everything" — assemble a stack from 5 evidence-layer types (search, static, runtime, telemetry, relational graph) plus an LLM actor on top, and put the LLM last.
If you need to comply with GDPR — start with a data map, a retention policy, and an exit plan for each vendor.
This list does not mean "install everything." First decide which question you want to make cheap: find a symbol, catch a vulnerability, explain latency, delete data on user request, or validate a change through an LLM.
How to choose: maturity beats star count
For every tool I applied four filters:
is there a clear primary source and living documentation;
how well it fits a production scenario, not just a demo;
where the boundary lies between OSS, commercial features, and vendor lock-in (dependence on a specific vendor);
what happens to code, logs, and personal data in self-hosted (own infrastructure) versus SaaS deployments.
"Mature" here means the tool has a stable production use case and is not in beta or maintenance mode (frozen project). It is not a promise of bug-free behaviour. Conversely, a beta tool can be fast and pleasant to use, but you cannot put it in a blocking gate (CI merge blocker) blindly.
1. Code search: find the facts first
Code search is the most under-rated layer. Before you build a graph and wire in an LLM, you must be able to answer "where is it declared", "who calls it", "which branch changed it", and "does the same problem exist in neighbouring repositories" — quickly.
Sourcegraph and Zoekt
Sourcegraph Code Search is the practical choice for an enterprise setup where multiple hosts, branches, commits, symbol search, batch changes, and Cody on top of the index all matter. Its strength is not just string search but a single interface for cross-repo investigations. The cost of that depth is a commercial model, opaque pricing, and the need for a separate privacy review for SaaS or AI features.
Zoekt is Apache-2.0 and a sound base for an OSS-only setup. It is the trigram-based engine that sits under Sourcegraph Code Search. It searches large code volumes quickly, supports regexp, boolean queries, and symbol ranking. But Zoekt on its own is not a drop-in replacement for the full Sourcegraph platform: UI, repo sync, access control, and operational processes are still on you.
The choice is simple: if you want a finished enterprise product and the licence works for you, choose Sourcegraph; if data control matters and you have a team to build the platform, choose Zoekt.
OpenGrok and livegrep
OpenGrok remains a reasonable option for Java-heavy and legacy environments. It has long offered cross-reference, broad language support, a web UI, and an API. The trade-offs are the JVM stack, the cost of full reindex, and tuning required for very large repositories. Also verify the actual licence mix: the project is CDDL-1.0 with mixed components.
livegrep is a narrower but convenient single-tenant scenario. It is a fast regex search across gigabytes of source on top of re2 and mmap indices. It fits an internal "Google Code Search" use case, where you don't need a complex permission model or multi-repo semantics.
Hound only makes sense if you need a very small deployment footprint and can tolerate maintenance mode. For a new strategic layer I would not make it the default.
What not to do
Don't start with semantic search if you don't already have solid text and symbol search. Otherwise the LLM will confidently retell an incomplete index, and your team won't be able to verify the answer with a simple repeated query.
2. Static analysis: separate fast feedback from deep investigation
Instead of one "magical" analyzer, it helps to split the work by cost.
Ruff — a fast Python linter and formatter. It replaces several familiar tools and fits every local run and every pull request.
Semgrep — pattern-based SAST (static security testing), SCA (dependency analysis), and secrets scanning. YAML rules are easy to write and review. It is a good layer for team-specific rules and quick hunts for dangerous patterns.
CodeQL (GitHub code-as-data) — a code-as-data approach. Queries run over a relational model of parsed code and are especially useful for vulnerability hunting, data-flow, and reusing the same rule across many repositories. CodeQL lives at the boundary of STATIC and GRAPH: from the static side it acts as a deep analyzer, from the graph side it acts as a relational query engine — see the diagram at the top of this article for the layer split.
SonarQube Community Build — a broad quality-gate layer with wide language and rule coverage. It is convenient for portfolio-level reporting and unified gates, but feature depth depends on the edition.
ty — a promising fast Python type checker from the Astral ecosystem, but in 2026 it is still a beta on 0.0.x versioning. It is fine for experiments and non-blocking feedback, but it should not silently become your only production gate.
The practical layout looks like this: Ruff and local Semgrep rules give fast feedback; CodeQL and deep taint analysis (tracking untrusted data) run in CI; SonarQube collects the cross-repository quality picture. Each finding must have an owner, severity, suppression reason, and review date. Otherwise the tool just produces backlog.
3. Runtime tracing and profiling: code explains intent, production shows fact
Static analysis does not show which branch actually runs or where the time goes. For Linux-native systems the baseline remains perf events, perf, ftrace, and the Brendan Gregg toolkit. They give hardware counters, kernel tracepoints, kprobes, uprobes, and USDT with low overhead.
bpftrace is convenient when you want a quick diagnostic query without writing a separate C program: system calls, lock latency, network events, or process behaviour. It is mature, but it requires Linux with BPF support and does not replace a full continuous profiling system.
For the JVM 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. In a Java service this is usually a better starting point than trying to apply a universal agent to every language.
For continuous profiling you can choose Pyroscope or Parca:
Pyroscope fits naturally into the Grafana ecosystem and is convenient when you already have Loki, Tempo, and Mimir.
Parca uses an eBPF agent and pprof format, so it is especially interesting 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 build revision, service, environment, and time window. Otherwise the flame graph becomes a picture without causation.
4. Logs and observability: data standard first, backend second
The main observability decision is not "Loki or Elastic" but whether you preserve the ability to swap the backend without rewriting instrumentation.
OpenTelemetry belongs first in this layer. It is a vendor-neutral data model, SDK, and Collector for traces, metrics, and logs. OTel does not store data and is not a search engine, but it defines common semantic conventions, routing, and the boundary between application and backend.
Then the choice depends on the shape of your queries:
Grafana Loki — label-based aggregation, cheap storage, and tight Grafana integration. Don't expect Elasticsearch-class full-text behaviour: log bodies aren't indexed as deeply, and high-cardinality labels can hurt performance.
Quickwit — an S3-native search backend on Tantivy. It is interesting for large log and trace volumes with columnar storage. Delete tasks help build erasure flows, but on their own they don't make a deployment GDPR-compliant.
OpenObserve — a single-binary observability backend in Rust with SQL and PromQL. It is convenient for small self-hosted setups, but AGPL-3.0 and a younger ecosystem require a separate legal and operational review.
Elastic Stack — a mature choice if you need deep full-text search, a rich ingest ecosystem, and SIEM scenarios. Licensing and storage cost must be considered separately from the technical capabilities.
AGPL may be acceptable for self-hosted SaaS, but embedding a component into a closed commercial product requires a legal review. Also decide retention, data residency, erasure workflow, and log access up front. You can't promise user data deletion if the chosen storage cannot reliably find and delete all copies.
5. LLM-assisted investigation: the model is not a source of truth
An LLM is useful when it shortens the path from question to verifiable fact. It is dangerous when it delivers confident explanations on top of an incomplete index.
Aider is the strong OSS option for terminal-based work. It can build a repository map, work with different models, run lint and tests automatically, and commit changes to git. Its natural role is a managed CLI assistant, not an autonomous production deployer.
Cursor is convenient as an enterprise IDE, and Sourcegraph Cody makes sense for a team already using Sourcegraph that wants to feed the model its code graph. Both have a cost: commercial dependency, vendor-specific indexing, and the need to verify where source, prompts, snippets, and search results actually go.
Continue.dev can no longer be recommended as a base for new installations: according to its official documentation, the repository is no longer maintained and has been left read-only after the final 2.0 release. It can still be studied as a historical reference for prompt patterns, but it is not a current deployment target.
The minimum guardrail for any LLM tool:
the model receives only allowed repositories and branches;
every answer carries links to files, symbols, commits, or runtime traces;
changes land in a diff first, not straight 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 can't show evidence, that's a hypothesis, not a finding.
6. Knowledge graph: different graphs answer different questions
The phrase "knowledge graph (relational model of code) of code" gets 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 regex replacement, the tool understands program structure and can re-apply a recipe across multiple repositories. Parser maturity varies by language: Java is usually better covered than newer directions.
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. This is especially useful for security and cross-repo search for vulnerable patterns.
SCIP (Sourcegraph's index format for symbols and links between them; lets different language indexers feed a common format.) (Sourcegraph index format) is the Sourcegraph Code Intelligence Protocol index format. It's useful as an interoperability layer: separate language indexers can emit symbols and relationships in a common format. The SCIP ecosystem, however, is still most closely tied to Sourcegraph.
Sourcegraph Code Graph is a commercial, production-grade graph for cross-repo symbol navigation and AI-grounded Q&A. It delivers the most value when you already have Sourcegraph-scale code search. For a small repository, a separate graph is almost always overkill.
The practical rule: a graph is for relations and multi-hop questions — "which services depend on this type?" or "which calls reach this sink?" — not for any string search. For local fact lookup, a plain index is faster, cheaper, and easier to verify.
Reference architecture
A minimal production layout looks 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
Sequence matters here. The LLM must not be the first link. First you build indices and telemetry contracts; then you add a model that can move from question to evidence. If you swap the order, you'll get an expensive chat with stale context instead of code intelligence.
Three practical deployment profiles
The diagram above shows the compliance-sensitive concerns as a horizontal overlay that crosses both main profiles, not as a third equal column. Compliance is orthogonal to the tool choice: you can run OSS or GitHub-centric, and you can run either in 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 (Open standard for telemetry data (traces, metrics, logs) with vendor-neutral SDK and Collector; it defines formats, not storage.), Loki or Quickwit, Aider, and OpenRewrite. This option gives control over code and data, but it puts auth, indexing, UI, retention, and upgrades on your team.
GitHub-centric enterprise
Sourcegraph or GitHub-native search, CodeQL (GitHub's code-as-data engine: code is converted into a relational model, queries are written in QL to find vulnerabilities and patterns.), Semgrep, SonarQube, OpenTelemetry, your chosen observability backend, Cody or Cursor. This is faster to start, but you have to fix the SaaS boundaries, indexing cost, and exit plan (vendor-migration plan) up front.
Compliance-sensitive production
Start with the data map, not the LLM: which repositories, logs, and profiles contain personal data, where they're stored, who has access, and how a deletion request (user data deletion) flows. Then choose self-hosted indexing, an OTel (Open standard for telemetry data (traces, metrics, logs) with vendor-neutral SDK and Collector; it defines formats, not storage.) Collector with filtering, a backend with clear retention, and only then an AI tool with a verified data-handling policy.
152-FZ (Russian personal-data law) and GDPR cannot be reduced to a single checkbox in a product. Quickwit's delete API or OpenObserve's claimed GDPR-ready status are useful technical elements, but compliance is determined by the whole system: collection, access, storage, backups, deletion, residency, and the organisational processes around them.
A four-week rollout plan
Week 1 — inventory. Lock down languages, repositories, branches, owners, investigation questions, and data classification. Pick one pilot repository.
Week 2 — static baseline. Wire up Ruff or a language-native linter, Semgrep, and one deep analyzer. Cut noise, assign owners, and seed a regression corpus.
Week 3 — runtime loop. Turn on OpenTelemetry (Open standard for telemetry data (traces, metrics, logs) with vendor-neutral SDK and Collector; it defines formats, not storage.), collect minimum traces and logs, add perf or async-profiler for one critical service. Link every event to a build revision.
Week 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 set of investigations.
After the pilot, measure not the number of tools or the token spend, but time-to-find, time-to-explain, the share of findings with reproducible evidence, false-positive rate, and the time from a runtime signal to a fix.
Exit gates
Each week in the diagram ends with an explicit exit gate. The next week 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 is named, scope is locked (which repos, which branches, which environments), and a data-classification document exists. Without this, Week 2 will optimise noise instead of 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 analyzer (CodeQL or another) is producing curated output.
Gate 3 — release-to-event link. Each telemetry event (trace, log line, profile sample) carries build revision, service, environment, and time window. A flame graph or a log query without that context cannot answer "is this our change?".
Gate 4 — cited & gated. Every model answer carries links to files, symbols, queries, or traces; every generated diff passes tests, SAST, and a human review; the LLM is connected only to the pilot scope. Without this, Week 4 produces a chat instead of a workflow.
If a gate fails, fix the gap before continuing. 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 doesn't replace permission model, retention, reindex cost, and operational ownership.
Building the graph before basic search. A graph will complicate the system without fixing a bad index.
Treating the LLM as a source of truth. A model answer without links to code, queries, or traces is only a hypothesis.
Connecting SaaS to sensitive code without review. Indexing and prompts can carry more data than the UI suggests.
Treating AGPL (Copyleft free-software licence with a network-use clause: if you run an AGPL service for users, you must publish the source. Usually a legal blocker for embedding in a closed commercial product.) as a technical detail. For an embedded closed-source scenario it is a separate legal decision.
Putting a beta tool in a blocking gate. For ty you need an observation mode and an explicit version pin until it leaves 0.0.x.
Storing logs without an erasure plan. You cannot promise privacy without describing raw data, replicas, backups, and deletion deadlines.
Running every analyzer on every commit. Separate fast feedback, PR checks, and nightly / full scans.
Building new core on a maintenance-mode project. Hound and the archived Continue can be useful references, but not a strategic foundation.
What to re-check before publication
Code intelligence moves fast, so this article cannot be treated as a permanent catalogue:
commercial pricing and roadmap for Sourcegraph, Cursor, Cody, and Devin need re-checking before any external update;
Quickwit is still on 0.8.x, so breaking changes require pinning and a tested upgrade path;
ty should be revisited after a 1.0 release or once a production benchmark against mypy / Pyright appears;
Continue.dev stays an archived reference until the official upstream announces a new maintenance model;
licensing and AI-indexing terms need to be re-read from the current text before any enterprise rollout;
regulatory claims must be checked against the legal team, not against an observability vendor's marketing page.
A re-fetch every six months is reasonable for this space, and before any meaningful change for commercial AI tooling and compliance-sensitive deployments.
CodeQL (GitHub's code-as-data engine: code is converted into a relational model, queries are written in QL to find vulnerabilities and patterns.): https://codeql.github.com/docs/