2D map design
Status: implemented in M0; since M1 it consumes snapshots generated by codarch analyze.
Prototype: design/map-mockup.html.
Render: codarch render / codarch analyze.
Screenshots: risk · structure · changes · migration
Implemented: 2D server-side treemap (viewBox 1400×900), Structure / Risk / Metric lenses,
search, inspector, file and folder selection, and outgoing/incoming edges of the selected node. The
HTML is a single file, makes no network requests and is generated in ~35 ms for 2,003 files.
The render does not analyse code: it consumes a cc.json or the snapshot.cc.json produced by
codarch analyze. The prototype’s Changes and Migration lenses are not rendered yet: they
need diff and history. It does not invent PR findings either; the top strip only shows real
hotspots, cycles and counts from the snapshot.
The ten-second test
Section titled “The ten-second test”A screenshot of the map, without interaction, has to answer:
- Where is the mass of the code?
- What is hot?
- What has changed?
- What depends on what?
- What breaks the architecture?
If the legend needs explaining, the design has failed.
1. Treemap as the main view
Section titled “1. Treemap as the main view”- Area = RLOC. Not LOC: comments add no risk.
- Colour = one metric, the active lens’s.
- Nesting = folders. Each group has its header with name and total.
A force-directed graph would be more “architecture”, but it is a worse product: it is not deterministic, it rearranges on every run, it stops being readable past ~200 nodes and it cannot be compared between two commits. The treemap is stable, comparable and fits in one screenshot.
The cost, plainly: the 3D city encodes three metrics at once (area, height, colour). 2D encodes two. We compensate with lenses, delta badges and inspection. It is a deliberate trade: we lose visual density and gain precision, comparability and speed.
2. One lens at a time
Section titled “2. One lens at a time”| Lens | Encoding | Question | Status |
|---|---|---|---|
| Structure | categorical colour by folder | how is it organised? | M0 |
| Risk | sequential, beige → red | what hurts? | M0 |
| Metric | sequential according to the chosen metric | where does it concentrate? | M0 |
| Changes | diverging, blue → neutral → red | what is moving? | M2 |
| Migration | categorical ts / bridge / rust | how much is left? | M4 |
Never two metrics competing for colour. The lens switch is the product’s grammar: the same geometry, different questions.
3. Edges only from the selected node
Section titled “3. Edges only from the selected node”Drawing all dependencies produces a tangle. Here:
- Outgoing: solid line.
- Incoming: dashed line, pointing at the selected node.
- Layer violation: red, thicker.
- Limit: 8 + 8 edges. The rest, in the inspector.
Selecting store.ts shows the cycle with http.service.ts in red without the rest of the map
disappearing. That is what a global graph cannot achieve.
Self-loops are excluded from the drawn edges; the inCycle / cycleSize metrics still mark the
file.
4. Findings go at the top
Section titled “4. Findings go at the top”1 layer violated · 2 new cycles · 3 hotspots moved is a fixed strip, not a hidden panel. It is
the product’s wedge (PR/CI) showing in the interface. Each chip leads to the evidence.
In M0/M1 the strip only shows real data: hotspots, cyclic edges, files and dependencies. There are no PR findings until M2.
5. Colour with semantics
Section titled “5. Colour with semantics”- Sequential for magnitude (risk).
- Diverging for delta (growth/decline).
- Categorical only for structure and migration, with a muted palette.
- Never a rainbow. Colour is data, not decoration.
- Gamma on risk:
raw^1.7. Without it, almost everything came out orange and the map stopped informing. A map where everything is hot says nothing.
Composite risk: 0.5·complexity + 0.32·churn + 0.18·size, normalised by p95. If a signal is
missing (for example, git is unavailable or the analysis runs with --no-git), the weights are
renormalised so the maximum is still 100/100 and not an artificial 68/100.
6. Labels
Section titled “6. Labels”Two lines when the block allows it: base name and extension (store / .ts). One line if not. No
label below the threshold: hover resolves it. Text density is a treemap’s biggest aesthetic risk.
7. Inspector, not dashboard
Section titled “7. Inspector, not dashboard”Metrics, incoming and outgoing dependencies, and the violation if it exists. No decorative charts. The inspector answers “what about this file?”, not “do you want to see a donut?”.
8. Implementation notes (M0/M1)
Section titled “8. Implementation notes (M0/M1)”- Area:
rlocby default; if missing, each file gets area 1 so it does not disappear. It is a legibility decision, not an invented metric: the inspector still shows the real value. - Hierarchy: folder chains with a single child collapse into one header
(
app/codeCharta/features, not three empty headers). - Tiny folders: when the inner space cannot draw files with one pixel, the folder stays as an aggregated block. In the real sample, 1,894 of 2,003 cells are drawn; the rest are still available in the data, the search and the inspector. The web app’s zoom will solve it.
- Geometry: integer coordinates in the
viewBox, shared borders andshape-rendering: crispEdgesso the SVG does not produce subpixel cracks. - Labels: two lines (name + extension) only if they fit; below the threshold no text is drawn and the information stays in the hover. The real sample produces ~100 labels, not 2,000.
- Edges: at most 8 outgoing + 8 incoming; incoming ones are dashed. The rest are listed in the inspector.
- Payload: unique directories, compact rows per file, cells with rects and integer coordinates. The real sample’s HTML stays under 500 KB.
9. Comparison with CodeCharta (M0)
Section titled “9. Comparison with CodeCharta (M0)”The same samples/codecharta_visualization.cc.json.gz was compared with CodeCharta Visualization
2.9.0 (area=rloc, height=complexity, color=complexity) and with CodArch (area by rloc,
colour by complexity).
| Signal | CodArch | CodeCharta |
|---|---|---|
| Files | 2,003 | 2,003 |
rloc | 103,577 | 103,577 |
| Edges | 3,727 | 3,727 |
| Structure | app 102.4k · features 51.2k | app 97% of files · features visible |
| Main hotspot | dependencyMap.component.spec.ts | dependencyMap.component.spec.ts |
Conclusions:
- The totals match. The area difference is only the minimum of 1 we give to the 79 files
without
rloc; the inspector still shows the real value. - The hotspots match. Both maps highlight
dependencyMap.component.spec.ts,threeSceneService.ts,metricColorRangeDiagram.component.tsandfileValidator.ts. Our risk selector picks the same file CodeCharta labels (rloc 1,053, complexity 9, risk 100/100). - The hierarchy matches.
appconcentrates 102.4k rloc and 1,935 files;features~51.2k. - Deliberate differences: 2D versus 3D, a neutral ramp in the Metric lens versus CodeCharta’s green-red, and composite risk by default. No data or render defects were found.
- CodeCharta’s explorer percentage is the share of files, not of area.
No CodeCharta code or design was copied; the comparison was made on the same cc.json.
What we deliberately do not do
Section titled “What we deliberately do not do”- 3D. A later lens, never the default view.
- Force-directed as the main view. Unreadable and non-deterministic.
- Layout animations. The map must be comparable between runs.
- An icon per language in each block. Noise.
- Dark mode in v1. Yes, later.
The second view: layer lanes
Section titled “The second view: layer lanes”The treemap is for exploring. It is no good for the PR comment: what must be shown is the violation, not the city.
Proposal for M2: a lanes view where each layer is a horizontal band, modules are placed inside and edges that go up are drawn in red. It is deterministic, reads in a 600 px screenshot and turns a finding into an actionable image.
Treemap to explore. Lanes to decide.
Screenshot checklist
Section titled “Screenshot checklist”Open capture criteria live in the roadmap backlog (folders without the legend, dominant red, findings without zoom, selection without colour, black and white).
Open questions
Section titled “Open questions”- Composite risk or raw complexity by default? The composite (complexity + churn + size) finds real hotspots, but it is a formula that must be explained and trusted. Raw complexity is immediate and honest, but ignores change.
- Treemap or lanes as the initial view? The treemap tells the project better; lanes tell the problem better.
- Incoming edges by default? They add context (who uses you) and noise. In the prototype they are on; perhaps they should be opt-in.