Understand system architecture
Architecture questions cross boundaries that no single repository shows: which service calls which, who writes a table, what a queue connects, where each piece runs and what fails with it. This page covers the operations an architect or tech lead runs to build that picture from indexed code and scanned cloud resources, check the uncertain parts, and write it down with citations. Onboarding one newcomer is covered separately in Understand and onboard to a codebase, and security exposure review in Review security and exposure.
When you'd do this
- You are designing a change that crosses services and need to know every consumer, producer and data store involved before you write the plan.
- Someone claims "only the orders service uses that database" and you need to check it before a migration, split or decommission.
- The architecture diagram on the wiki is two years old and you have been asked to replace it with one you can defend.
- You suspect two services are tightly coupled, or that dependencies run in a circle, and want evidence before proposing a boundary.
- You are reviewing a design proposal and want to check its claims about callers, data ownership and runtime placement against the real system.
- A data engineer needs to know which services read and write a table before changing its schema.
Before you start
- CodeZero installed in VS Code with your coding agent (Claude Code, Codex, GitHub Copilot or Cursor), and the organisation's repositories connected and indexed. The index covers committed code on each repository's default branch, including repositories you have not cloned.
- For the runtime side, Vörr connected to Claude, ChatGPT or another MCP assistant, with your repositories and cloud accounts (AWS, Google Cloud or Azure, read-only) connected. Observability connectors add live service-graph and health data but are optional for this activity. ChatGPT needs a paid plan with Developer mode turned on.
- Repositories mapped to workspaces and environments in Vörr, so code can be tied to where it runs. An admin manages connections and the service map; Vörr answers from the organisation's connected sources, and workspaces label what comes back.
- A recent successful cloud scan. Open Scan results first; conclusions are only as complete as scan coverage.
If any of this isn't set up yet, start here:
Operations
The operations below build on each other, but each can be run on its own. CodeZero supplies the code side: modules, imports, packages, inferred service edges, the data layer, routes and config. Vörr supplies the runtime side: cloud resources, environments, exposure, dependencies and blast radius. The agent does the reasoning and writing over what both return.
1. Build a service map from the graph
A good result lists every repository, separates direct relationships (imports, packages) from inferred ones (HTTP, deployment config, queues), and gives file:line evidence for each. Check it by opening a sample of the cited files, and by comparing the list of repositories against the Files view so none are missing.
- CodeZero: open Graph and keep All repositories selected. Start in Modules view with a shallow grouping so each repository collapses to a few nodes.
- CodeZero: turn on third-party packages if you want shared libraries and frameworks on the map; leave them off for a service-only view.
- CodeZero: select each repository node and read Depended on by and Depends on. Note which edges stay inside the repository and which cross into another one.
- Agent: ask Chat for the same picture in text, so you have file references for every edge rather than a screenshot.
- You: increase the grouping depth, or switch to Files, only for the areas where the coarse view hides something you care about.
Give me a service map of the organisation from the indexed code. For each repository, list what it depends on in other repositories and what depends on it, separating imports and shared packages from inferred service calls. Cite a file for every edge.Which repositories have no inbound edges from any other repository, and which have no outbound ones? Tell me whether that is because nothing references them or because the evidence is not in the index.2. Find and verify inferred service-to-service edges
A good result gives, per edge, the calling file and line, the matched route or config key, the confidence, and a verdict with a one-line reason. Treat unverified low-confidence edges as hypotheses in anything you write down.
- CodeZero: in Graph, look for dashed edges. These are HTTP and config inferences: an HTTP call matched to a route declared in another repository, or a service named in Kubernetes, Helm or Compose config. Queue edges run solid through a queue node, from producer to consumer.
- CodeZero: select an edge to see the file:line evidence behind it. The panel shows the confidence of HTTP edges (high, medium or low); ask the agent for a config edge's confidence (high or medium). Low confidence usually means the path matched routes in more than one repository and the target is a guess.
- Agent: for every HTTP edge your design depends on, use Verify. The agent reads the call site and records a verdict: confirmed, wrong, or retargeted to the repository the call really reaches.
- You: review the verdicts. They are kept for the whole organisation, so the next person sees a checked edge instead of a guess.
List every inferred edge into the payments repository with its confidence and evidence. For each low- or medium-confidence edge, read the call site and tell me whether it really reaches payments, reaches another repository, or is not a call at all.Which services reach billing-api through deployment config rather than a hard-coded URL? Show the setting, where it is set, and where the code uses it.3. Trace queue producers and consumers
A good result names the topic, every producer and consumer with a file reference, and calls out one-sided topics rather than hiding them. Topic names built at runtime or held only in infrastructure config may not match; say so where it applies.
- CodeZero: filter Graph to the repositories that use a broker and look for producer-to-consumer edges between them.
- Agent: ask Chat which code publishes to each topic or queue and which code consumes it, with file references.
- Agent: for each topic, check for producers without consumers and consumers without producers; these are either dead paths or evidence outside the index.
- Agent: check the queue edges you will rely on by opening the producing and consuming code at the file and line each edge cites. Verify verdicts apply to HTTP edges.
For every queue or topic you can find in the index, list the producing and consuming code, by repository and file. Flag any topic with a producer but no consumer, or the reverse.If I change the shape of the order.created message, which consumers in which repositories would need to change? Show where each one parses the payload.4. Trace data flows through the data layer
A good result has more than one writer called out explicitly, since shared writers are the usual source of data-ownership problems. Verify by opening the cited queries. For schema changes, follow up with Refactor or upgrade safely.
- CodeZero: in Graph, turn on the data layer. Data nodes show tables, collections and other stores next to the code that reads or writes them.
- CodeZero: select a table node and read Depended on by to see every module, in every repository, that touches it.
- CodeZero: open Functions for the feature you are designing and read Reads / Writes on its map to see which data that one flow touches.
- Agent: ask Chat to split readers from writers and to point at the query or model for each, so you can see who owns the data and who only reads it.
Which code reads the orders table and which code writes it, across all repositories? For each, give the file, whether it reads or writes, and the query or model it uses.If we add a non-null column to customers and rename email to primary_email, which repositories would break? Separate code that selects the column by name from code that uses the whole row.5. List each service's interface: routes, config, dependencies and owners
A good result is a table per service with sources. Routes come from parsed framework declarations, so a route registered dynamically may be missing; check the router file if the count looks low.
- CodeZero: for each repository, read the declared HTTP routes (NestJS, Express, Fastify, Flask, FastAPI, Spring, Rails, .NET and Go routers are recognised), the settings the code expects and where each is set (the code default, .env.example and deployment config), and the dependencies declared in its manifests.
- CodeZero: read the owners view for the repository or path to see who has changed it most, by commit history. Ownership is based on the indexed (shallow) history; full history needs a local checkout.
- Agent: put the routes next to the inbound HTTP edges from operation 2. A route with no inbound edge is either called from outside the organisation's code (a browser, a partner, a cron job) or unused.
- Agent: put the settings next to the deploy-config edges. Settings that hold service URLs are the service's real outbound dependencies.
For the inventory service, list its declared HTTP routes, the settings it expects, its manifest dependencies and its most frequent committers. Mark which routes have a matched caller in another repository and which do not.Using Vörr, list the HTTP routes and config of the inventory repository, and which environments it is mapped to.6. Map code to runtime
A good result carries a source and match quality (explicit, identity, exact, name, suggested or unmapped) on every fact and states the scan coverage behind it. An unmapped resource is a gap to fix in workspace mapping, not something to guess at. For a full exposure review, use Review security and exposure.
- Vörr: ask which environments each repository is mapped to, and which branch deploys to which environment.
- Vörr: for each service, find its cloud resources by name, tag, type or workspace mapping, and read each resource's detail: configuration, environment, exposure class (public, private, isolated or unknown), one-hop neighbours with confidence, and whether it is Terraform-managed.
- Vörr web app: open Architecture for a visual pass through the same dependencies. Filter to Needs mapping to find resources not yet tied to a workspace.
- Vörr: if observability is connected, ask for the service-graph callers and callees from traces and compare them with the code-side edges from operation 2.
- Agent: reconcile the two views. An edge in code with no runtime counterpart may be dead or environment-specific; a runtime edge with no code edge points to configuration, infrastructure or a repository that is not indexed.
Using Vörr, for the checkout service in production: which repository and branch deploy it, which cloud resources belong to it, what each one depends on, and its exposure class. Name the source and match quality for each fact.Using Vörr, list the resources in the production workspace that are not mapped to any repository or service, and tell me the scan coverage for the services they belong to.7. Measure blast radius across code and cloud
This is where a single-repository answer is most often wrong. An agent that searches one checkout finds the service that owns a database and stops; the reporting job, the export worker and the service that reads a replica live elsewhere. A good result lists direct and transitive dependents separately, with true counts rather than a sample.
- Vörr: for the resource you plan to change (a database, a cache, a queue, a load balancer), ask for its upstream and downstream blast radius. The counts are precomputed and transitive, so they include dependents of dependents.
- CodeZero: for the same store, read the data-layer edges and the inbound service edges to find which code, in which repositories, depends on it.
- Agent: join the two. Runtime dependents with no code counterpart usually mean an unindexed repository, a scheduled job or a managed integration; code dependents with no runtime counterpart may be dead or point at a different environment.
- You: decide what the change actually touches, with both lists in front of you.
Using Vörr, what is the full downstream blast radius of the orders database in production? Give the true counts by resource type, list the direct dependents, and say whether scan coverage is complete for each service involved.Which code in any repository reads or writes the orders database, directly or through another service's API? Follow inbound service edges one hop out from every module that touches it.8. Find coupling hotspots and circular dependencies
Neither product labels hotspots or cycles for you. The agent finds them by reading the graph it gets back, which means the answer is reasoning over evidence and should be checked like any other analysis.
- CodeZero: ask for the dependency graph at module level across repositories, or for one repository at file level.
- Agent: count inbound and outbound edges per node. Nodes with many inbound edges from many repositories are coupling hotspots; nodes with many outbound edges are fragile.
- Agent: look for paths that return to their start, for example A depends on B, B on C, and C on A. Cross-repository cycles through HTTP or queues matter most, because no compiler catches them.
- Agent: for each candidate, open the edges involved, Verify the HTTP edges and open the cited line for config and queue edges before reporting it.
From the module-level dependency graph across all repositories, which ten modules have the most inbound edges from other repositories? For each, say what kind of edges they are and cite two examples.Read the cross-repository edges and tell me whether any repositories depend on each other in a cycle, directly or through one intermediate. Show the edges that form each cycle and their confidence, and verify the inferred ones.A good result shows the edges that make up each hotspot or cycle, with their kind, evidence and (for HTTP and config edges) confidence, so a reader can disagree with the conclusion from the same evidence.
9. Write an architecture document or diagram description
A good document has no uncited edges. The second prompt turns an existing diagram into a checklist of drift.
- Agent: gather the outputs of the operations above: service map, verified edges, queues, data ownership, interfaces, runtime placement and blast radius for the critical stores.
- Agent: write the document in the repository where your team keeps architecture docs. In Planned mode, or with /plan, CodeZero proposes this as a plan, which you approve; the agent cannot approve its own plan.
- Agent: cite a file and line, or a Vörr source, for every edge and claim. Mark inferred and unverified edges as such.
- Agent: if you want a diagram, have the agent write a diagram description (for example Mermaid or a plain node and edge list) generated from the same evidence.
- You: review the document against the graph and the cited files before it is merged.
Write docs/architecture.md for the commerce domain from the indexed evidence: services, how they call each other, queues, which service owns which tables, where each runs, and the blast radius of the orders database. Cite a file or source for every edge, mark unverified inferred edges, and include a Mermaid diagram generated from the same edge list.Compare docs/architecture.md with the current indexed code. List every edge in the document that the code no longer supports, and every edge in the code that the document does not mention.10. Keep the picture current
- Re-ask rather than re-read. When code changes, the index updates and inferred edges are derived again from the new code, so the same question returns the current answer.
- Verdicts persist. Edges your team has confirmed, rejected or retargeted keep that verdict, so checking is not repeated.
- Feature names generated in Functions are shared with the organisation, so everyone reads the same map with the same names.
- On the runtime side, each new cloud scan refreshes resources, neighbours and blast radius. Check Scan results after permission changes.
- Run the drift prompt from operation 9 after significant changes, and before any design review that relies on the document.
Without CodeZero or Vörr
Claude Code with several repositories cloned can do a respectable job of this. It can grep imports, URL strings, client constructors, queue names and SQL table names across every checkout, read manifests and router files, and draw a Mermaid diagram from what it finds. With cloud CLIs installed and credentialed it can list resources and read security groups. ChatGPT or Claude chat can reason well over exported diagrams, Terraform and pasted config.
What it costs and misses is specific. It only sees repositories you cloned, so a consumer in a repository you did not know about is invisible and looks like absence. Service-to-service links are not references in source: a call to ${BILLING_URL}/invoices does not name billing-api, and the value lives in Helm values or Kubernetes config in another repository. Matching every outbound call to a route in another service means reading every router, which is expensive in context, so agents stop early and generate the rest. There is no confidence on its edges and no record of which were checked. Runtime topology needs separate credentials per cloud and tool, and the agent has to reconcile service names that differ between code, cloud and monitoring. And an empty grep is indistinguishable from "not cloned" or "not scanned".
| Operation | Claude Code / ChatGPT on their own | With CodeZero and Vörr |
|---|---|---|
| Service map | Grep imports and URLs across cloned repositories; draw a diagram | Graph across every indexed repository, cloned or not, with direct and inferred edges separated |
| Service-to-service edges | Match URL strings to routes by hand; env-var indirection and deploy config are easy to miss | HTTP calls matched to routes and Kubernetes/Helm/Compose references, with confidence, evidence and Verify |
| Queues | Search for topic names; one-sided topics look the same as unscanned ones | Producer and consumer edges across repositories |
| Data flows | Grep table names and ORM models in cloned code | Data layer: tables and collections with the code that reads and writes them; Reads / Writes per feature |
| Interfaces | Read router files and manifests in each repository | Declared routes, config, manifest dependencies and owners per repository |
| Code to runtime | Cloud CLIs per account with local credentials; names stitched by hand | Vörr: repository-to-environment mapping, resource detail, exposure and neighbours from read-only scans, with match quality |
| Blast radius | Follow references one hop at a time; transitive dependents are usually missed | Vörr: precomputed transitive blast radius with true counts; CodeZero: data and service edges in code |
| Hotspots and cycles | Possible over a hand-built edge list, if the list is complete | Reasoning over the organisation-wide graph, with file:line evidence on each edge and a confidence on HTTP and config edges |
| Architecture document | Written from whatever was found; no coverage signal | Written from cited evidence, through an approved plan, and checkable for drift |
With CodeZero and Vörr
The two products are used side by side: CodeZero in VS Code with your agent, Vörr from Claude, ChatGPT or the Vörr web app. You carry findings between them.
- Organisation-wide code view (CodeZero)
- Every indexed repository is visible, including ones nobody has cloned, so a missing consumer means missing evidence, not a missing checkout.
- Inferred edges you can check (CodeZero)
- HTTP, deployment-config and queue edges come with file:line evidence, and HTTP and config edges also show a confidence. Verify asks the agent to read the call site, and the verdict is kept for the organisation.
- Data ownership (CodeZero)
- The data layer shows which code reads and writes each table or collection, and Functions shows the reads and writes of one flow.
- Service interfaces (CodeZero, also through Vörr)
- Declared routes, config, dependencies and owners per repository.
- Runtime placement (Vörr)
- Repositories mapped to environments; resources with configuration, exposure, neighbours and Terraform status; the Architecture graph in the web app.
- Blast radius with true counts (Vörr)
- Precomputed upstream and downstream dependents, bounded by stated scan coverage.
- Combined
- CodeZero says which code depends on a store or service; Vörr says what depends on it at runtime. The differences between the two lists are the findings that single-view answers miss.
Worked example: "only the orders service uses this database"
A tech lead is planning to move the orders database to a new cluster and split the order history tables into their own store. The design proposal says the orders service is the only consumer. An agent asked in the orders-service checkout agrees: it searched the repository, found the connection string and the models, and found nothing else.
The lead checks the runtime side first with Vörr.
orders-prod (RDS PostgreSQL, production, Terraform-managed: modules/orders-db)
Exposure: private
Scan coverage: rds, ec2, lambda, ecs completed; glue access denied
Downstream blast radius: 11 resources
direct (4):
ecs service orders-service confidence: high
ecs service reporting-worker confidence: high
lambda nightly-order-export confidence: medium
rds read replica orders-prod-ro confidence: high
via orders-prod-ro (3):
ecs service analytics-api confidence: high
...
Note: Glue jobs could not be scanned (access denied); dependents
in Glue would not appear in this count.Using Vörr, what is the downstream blast radius of the orders-prod database? Give true counts, the direct dependents, and scan coverage.Worked example: the code side
The lead then asks CodeZero Chat which code reads and writes the order tables across every indexed repository, and turns on the data layer in Graph.
Data layer
orders written by orders-service src/orders/repository.ts:41
read by reporting-worker jobs/daily_revenue.py:18
read by analytics-api app/queries/orders.go:63
order_history written by orders-service src/history/archive.ts:22
written by support-tools admin/refunds.rb:107 <- second writer
Inferred edges into orders-service
checkout-web POST /orders high src/api/orders.ts:12
support-tools PATCH /orders/:id medium admin/client.rb:30 (via ORDERS_API_URL in helm/values.yaml)
Not found in the index: nightly-order-exportWhich code reads or writes orders, order_items and order_history across all repositories? Separate readers from writers, cite files, and list inbound service edges to each of those modules.Worked example: conclusion and verification
- Conclusion: the database has at least four direct consumers and a second writer to order_history. The split needs reporting-worker, analytics-api and support-tools in the plan, and the history store needs a single owner decision.
- Verify: the lead asks the agent to Verify the medium-confidence support-tools edge; it reads admin/client.rb, confirms the call reaches orders-service through ORDERS_API_URL, and records the verdict as confirmed.
- Coverage gap: nightly-order-export appears in Vörr as a medium-confidence dependent but not in the code index. Its source is in a repository that has not been connected; the lead asks an admin to connect it before the plan is approved.
- Coverage gap: Glue access was denied in the scan. The lead asks for the Vörr role to be extended and re-checks the blast radius after the next scan.
- Follow-up: the agent writes the findings into the design document with citations, and the cross-repository change goes through Ship a cross-repository change.
Verify and follow up
- Provenance: every edge should cite a file and line (CodeZero) or a source (Vörr). Open a sample of them, and every one your design depends on.
- Confidence and verdicts: Verify medium- and low-confidence HTTP edges, and open the cited line for config and queue edges, before relying on them. Wrong and retargeted verdicts are findings too.
- Match quality: on Vörr facts, treat name matches as needing a check and unmapped ones as gaps in the service map.
- Coverage: check that every repository you expect is in the index and has been indexed recently, and that the cloud scan completed for the services involved. A zero is only a zero when coverage is complete.
- Cross-check: compare code-side edges with Vörr's runtime dependents and, if connected, the service graph from traces. Disagreements are the interesting part.
- Next: plan the change with Ship a cross-repository change or Refactor or upgrade safely, and record design decisions with Make engineering decisions.
Limits
- CodeZero's relationships are heuristic, not compiler-accurate. References match by name, so same-named symbols can appear; callers and callees are approximate.
- Inferred edges are inferences. URLs built at runtime, service discovery, gateways that rewrite paths and topic names held only in infrastructure may produce low-confidence, wrong or missing edges.
- The index covers committed code on the default branch of connected repositories. Work on other branches is only visible through your local checkout, and unconnected repositories are not visible at all.
- Symbols are parsed for TypeScript, JavaScript, Python, Go, Java, Ruby and C#. Other languages are searchable as text only.
- Neither product detects coupling hotspots or cycles automatically. The agent derives them from graph output, and its reasoning needs checking.
- Vörr's runtime picture is bounded by the connected cloud accounts and the latest scan; services that were denied, throttled or failed are reported as such, not filled in.
- Vörr is read-only from the assistant. It describes the infrastructure; it does not change it.
- Neither product draws your final diagram. The agent writes a document or diagram description from the evidence, and a person reviews it.
Related
- Understand and onboard to a codebase
- Review security and exposure
- Explore cloud infrastructure
- Ship a cross-repository change
- Refactor or upgrade safely
- Make engineering decisions
- Keep context across the team
- Answer system questions without the IDE
- Explore the Code Graph
- Trace a product feature
- Work across repositories
- Explore your cloud architecture
- Ask Vörr