Your data is already a graph
July 2026
An explanation of graph engineering, using the pile of data I think about anyway: customer feedback.
Here is a question that sounds simple and quietly breaks most feedback systems: are the enterprise accounts complaining about setup in their reviews the same ones filing support tickets about it?
You can answer that. Someone writes a query, joins four tables, and hands back a number by the end of the day. Then the next question arrives — okay, which of those accounts renew in the next ninety days? Another query. And who owns that part of the product? Another query. Every question is a small engineering project, and none of the answers accumulate. Six months later you have a folder of one-off SQL and still no working picture of the thing everyone is describing in meetings.
That's the symptom. The cause is that the data got stored in a shape that discards the most valuable information in it: what is connected to what.
A row is a graph with the edges thrown away
Look at a single piece of feedback as it actually arrives.
Ten thousand rows like that contain a few thousand customers, a few dozen product areas, and some unknown number of real issues. But the table doesn't hold any of those as things. It holds text that happens to mention them. Every time you want to reason about a customer or an issue, you reconstruct it from scratch, and then you throw the reconstruction away.
Nodes, edges, and one real idea
Graph engineering is a small vocabulary plus one idea.
The vocabulary: a node is a thing — a customer, an issue, a review, a product area, a person. An edge is a relationship between two things — Acme wrote Review #812; Review #812 is about setup friction. Both carry properties. A customer node holds a plan tier and a renewal date. An edge holds a confidence score, a date, and a note about who decided it.
The idea: the relationship gets stored, not recomputed. In a table, "these two complaints describe the same underlying problem" is a conclusion someone reaches at query time and then discards. In a graph, it's an edge — written down once, visible, dated, attributable, and correctable. The expensive judgment survives the query that produced it.
That's the whole thing. Everything else is consequences, and the consequences are large. Questions become walks across the graph rather than joins invented on demand. A new question usually needs no new schema, because the connections it depends on are already sitting there. And because every edge is a stored object, a human can look at one and say that's wrong, which is not a sentence you can say to a SQL result.
Where the engineering actually lives
The database is the easy part. Neo4j exists; so do half a dozen alternatives, and a graph runs perfectly well on top of Postgres for a long time. The hard part is a series of judgment calls, and they're the reason this is engineering and not configuration.
Deciding what gets to be a node. The test I use: if you'd ever want to point at it, count it, own it, or attach a decision to it, it's a node. If it only ever describes something else, it's a property. Make "issue" a text label on a review and you can never ask which issues span two product areas, or who owns one, or whether this quarter's version is the same as last quarter's. Promote it to a node and all three come free. Getting this wrong is the expensive mistake, because the fix is a migration of everything.
Resolving entities. "Acme Corp," "ACME," "acme.com," and "Acme Corporation (EMEA)" are one customer or four, and the graph has to decide. This is the least glamorous work in the entire discipline and it determines whether anyone trusts the output. A graph with four Acmes is worse than a spreadsheet, because it looks authoritative while quietly lying about counts.
Making each edge mean exactly one thing. MENTIONS is not IS_ABOUT is not CAUSES. A review can mention billing while being about onboarding. If a single edge type carries all three meanings, every traversal returns a superset of the truth, and you've rebuilt keyword search with extra steps and more confidence.
Recording who drew the edge. Model-proposed, human-confirmed, or rule-derived — plus a confidence and a date. Provenance is what makes correction possible, and correction is the only reason the graph gets better over time instead of slowly rotting. It's the same principle as keeping the human in the loop: the system's job is to make judgments inspectable, not to hide them.
Deciding what not to connect. A graph where everything touches everything is fog. Restraint is design work.
The question, answered
Here's the thing from the top, drawn.
Notice what didn't happen: nobody wrote a bespoke join. The question "which enterprise accounts hit this in more than one channel, and who owns the fix" is a path through nodes that were already connected. The next question — does this issue predate the March release? — is one more hop, because the evidence nodes have dates. The structure answers questions it wasn't designed for. That's the compounding return, and it's the only real justification for the cost.
Why this got useful again
Graphs aren't new. Two things changed recently that made them worth revisiting.
Extraction got cheap. Building a knowledge graph used to mean humans tagging documents, which is why most attempts died in year two. A model now reads unstructured text and proposes the nodes and edges directly. The bottleneck moved from extraction to verification — and verification is a much better bottleneck, because a person can check a proposed edge in seconds and correcting one improves everything downstream of it.
Retrieval needs structure. Vector search finds text that sounds similar to your question. That's genuinely useful, and it is not the same as related. "What's Acme's exposure this quarter" isn't a similarity problem; it's a traversal. The strongest retrieval I've seen uses both — embeddings to surface candidates, the graph to establish what actually connects to what, and hard counts from the database so the model never invents a number it could have looked up. When a cluster says "47 reports across 29 accounts," those numbers should come from walking the graph, never from generation.
There's a third reason, which is that a graph gives an agent's memory somewhere to live. A summary preserves facts and loses structure — the same gap I ran into thinking about memory architecture. Nodes and edges preserve the shape.
When not to build one
Graphs cost. Entity resolution is never finished, edge definitions drift as the business changes, and a graph nobody queries is an expensive diagram. If your entities arrive clean with stable IDs, if your questions are one hop deep, if nobody asks second questions — use a table. You'll be happier.
The honest version of the tradeoff: reach for a graph when the relationships are the product, when questions arrive faster than schemas can, and when someone needs to correct a judgment rather than just re-run a query. And you rarely have to choose. A graph layer alongside the relational store, fed by it, is usually the right call — relational for the facts, graph for the connections between them.
The part that isn't technical
Deciding that "issue" is a first-class thing with an owner, a lifecycle, and evidence attached — rather than a label someone types on a review — is not a database decision. It's a claim about how the company should work: that problems are things you can point at, count, assign, and close.
That's the part I find interesting. Choosing what counts as a thing, and what counts as a relationship between things, is a product decision wearing an engineering hat. The graph just makes you write it down.
The related question — how much of this a machine should decide before a human sees it — is its own essay.