Skip to content
All work
TomographDeveloper toolsJune 28, 2026

Reading an application in slices, from the screen to the column

A tool that reads a codebase and its database and draws one map across both - every screen, hook, endpoint, table, policy and trigger, and what connects to what. Because it is read from source rather than drawn by hand, it cannot go stale. In development, Postgres and React first.

Visit tomograph.app
QR code linking to https://tomograph.app

Scan to open it on your phone.

Five
Levels collected
Screen down to the column
81
Checks against the answer key
Graded, not eyeballed
No edge
Runtime-only table names
Refusing beats guessing
In development
Status
Postgres and React first

The challenge

Two questions come up on every codebase and neither has a reliable answer: if I change this, what breaks, and where does this thing on the screen actually come from?

The only way to answer today is to read the code and hold it in your head. That works while the app is small and you wrote all of it. It stops working when the app gets big, and it never survives a handover - or your own return to it six months later.

The tools that exist each own one horizontal band and are blind to the ones next door. Schema tools see the database. Component catalogues see the front end. Service catalogues sit somewhere in between. None of them cross, so the answer to a question that spans layers has to be reassembled by hand every time. They also skip the half of a database that governs behaviour: functions, triggers and row-level policies are where competing tools stop, in one case as a literal TODO in the connector.

What we did

The map is read, never drawn. Collectors take one level each - the Postgres catalog for tables, functions, triggers and policies; the TypeScript syntax tree for the code; React for screens and hooks - and report only what they observed.

The part that makes it a map rather than an inventory is a separate stage. A collector observes; a bridger infers. Nothing watches a `supabase.from('posts')` call in a hook and a `posts` relation in the catalog and sees one thing - a bridger decides they are the same, from a name. Keeping that boundary explicit is the whole design, because inference is exactly where a tool like this earns or loses trust.

So every inferred edge carries a confidence and its provenance, and structural edges read straight from the catalog carry neither, because they need none. A literal table name scores higher than a suffix match, and the edge says which. Where a call site takes its table from a runtime argument, nothing is drawn at all - a wrong edge is indistinguishable from a right one once it is on the canvas, which makes it the most damaging thing the system can output.

Silence is reported rather than left as absence: refused counts the call sites with no static answer, unresolved names the ones pointing at something that is not there. Where a call could reach one of several known tables, every possibility is drawn as a set and marked as alternatives, so nothing downstream mistakes them for independent facts.

It runs entirely on the developer's machine. The collectors execute locally, the server binds to loopback, source is never uploaded and the database connection never leaves the process that opened it. That is an architectural decision rather than a pricing tier: a tool that wants both your credentials and your source has to be trusted in a way a local one does not.

It is also strictly read-only. No migrations, no edits, no dragging boxes around. The picture changes because the source changed.

The outcome

Five levels are collected, stored and drawn on a canvas, with 81 checks passing against a reference application built specifically to have a measured answer key - so the collectors are graded against known-correct output rather than eyeballed.

The first two seams across levels are working: a call site joined to the relation it touches, and a fetch joined to the endpoint it reaches. The second was predicted at design time to be the awkward one, and it was.

It refuses to be general on purpose. A works-with-anything version is the reason nobody has built this; being deeply correct for one stack - Postgres and React first - is what makes it tractable, with other stacks as plugins later.

This one is ours, in development and honest about it: the vertical seam is partly built, and deep call graphs and column-level lineage are partial by nature. The interesting result so far is not a feature. It is that a map which admits what it does not know is more useful than one that quietly guesses, because the second kind cannot be checked.

Ready to build something that lasts?

Tell us what you are trying to ship. We will tell you how we would approach it - scope, stack, and timeline - before you commit to anything.