Graph thinking
& primitives.
Why Jane Street builds in nodes, edges, and small composable parts — and what changes about your reasoning when you do the same.
The standard frame.
Most engineers learn graphs as a data-structures topic and then largely forget them outside of leetcode. That is the frame we have to leave behind first.
Open any algorithms textbook and a graph is a pair G = (V, E): vertices and
edges, possibly directed, possibly weighted. You learn traversals, you learn shortest
paths, you learn flows. You learn that a function is a thing that takes inputs and
returns outputs, and that this is roughly orthogonal to the graph chapter.
This is a perfectly good frame for a class of problems — routing, scheduling, network analysis, dependency resolution. It is incomplete as a way to build software. The textbook treats the graph as the object of study: something you are given, something you query. Jane Street treats the graph as the shape of the program itself. That is the move.
What the textbook leaves implicit
Every program is, secretly, a directed graph. The dependencies between values form edges; the values and intermediate computations form nodes; the functions are the rewriting rules that say how outputs are derived from inputs. In imperative code we hide this graph behind statements, mutation, and control flow. In functional code we expose it — and once exposed, we can reason about it, optimize it, and incrementalize it.
Why this matters before we go further
The standard frame produces engineers who write code in lines, debug in stack traces, and reason locally. The elevated frame produces engineers who write code as expressions, debug by reading types, and reason about dependencies. Jane Street's entire stack — language, libraries, review culture, even the trading systems themselves — is organized around the second.
Graph as computation.
The single biggest conceptual move at Jane Street is to model the running program
as a self-adjusting computation graph. This is what their Incremental
library is.
A trading system, a risk dashboard, a pricing engine — all of these are long-running computations over rapidly changing inputs. The naïve approach is to recompute everything when any input changes. The clever approach is to recompute only what depends on what actually moved. Doing this by hand is tedious and bug-prone. Doing it with the wrong abstraction is worse. Doing it with a computation graph is, it turns out, the cleanest thing in the world.
Self-adjusting computation, developed academically by Umut Acar and collaborators
in the early 2000s, became Incremental at Jane Street under Yaron Minsky.
The model is small enough to hold in your head:
- An input node holds a mutable value you can poke from the outside.
- A bind or map node holds a function whose inputs are other nodes.
- Edges express dependency: "this node's value is derived from those nodes."
- When an input changes, the system walks the affected subgraph in topological order, recomputing only the nodes downstream of the change, and stops propagating wherever a value happens not to have changed (a cutoff).
The win is not theoretical. Incremental is the substrate for production
pricing tools, real-time risk monitors, and the firm's UI framework. Their distributed
platform Webs uses the same idea to scale incremental recomputation to
billions of nodes and edges, sitting somewhere between a spreadsheet and a pure
functional DSL.
Self adjusting computations are essentially graph-structured computations that can be updated efficiently when their inputs change. Yaron Minsky, Jane Street blog
What this changes about your mental model
Once you internalize the graph-as-computation frame, the question "how do I update X when Y changes?" stops being an algorithm problem and becomes a graph construction problem. You describe the relationships once, declaratively, and the runtime handles propagation. This is the same move React made for the DOM, but generalized to every value in your program — and grounded in a type system strong enough that you cannot lie about your dependencies.
(* sketch of an Incremental computation, OCaml-flavored *)
let price = Incr.var 100.0
let qty = Incr.var 50
let fee = Incr.var 0.001
let notional = Incr.map2 price qty ~f:(fun p q -> p *. Float.of_int q)
let cost = Incr.map2 notional fee ~f:(fun n f -> n *. (1. +. f))
(* Move only `fee`. Only `cost` refires. `notional` is untouched. *)
Incr.Var.set fee 0.002
Notice what isn't here: no event handlers, no observers, no manual cache invalidation, no dirty flags. The dependency graph is the program. That is the elevation.
The primitive discipline.
The second half of the ethos: instead of one big abstraction that bundles every concern, build small, sharp primitives that compose on demand.
The instinct in mainstream software engineering is to reach for a framework. A framework gives you a Component, which has state, which renders, which handles events, which probably has a lifecycle, which may also be reactive — all of it welded into one shape. Jane Street's instinct runs in the opposite direction: separate these concerns into independently composable primitives, and let the engineer assemble exactly the abstraction they need at the call site.
Their UI library Bonsai makes the contrast explicit. Most frameworks
"lump together state, incrementality, and rendering into a single abstraction, the
UI component." Bonsai instead exposes state, incrementality, and rendering as
primitives you compose à la carte. The same primitive that prevents a re-render
of a panel can be the primitive that caches an expensive business calculation on a
live data feed. The primitive does not know it is for a UI; it knows it is for
incremental state.
AThe framework instinct
One abstraction bundles state, rendering, lifecycle, effects, and incrementality.
Composition is by inheritance or by living inside the framework's tree.
You learn the framework's mental model before you learn your problem.
The framework decides what you can express; you negotiate the rest with workarounds.
BThe primitive instinct
Each concern is a small, total, type-safe primitive.
Composition is by function application: combine primitives like Lego.
You learn small parts, and the whole assembles from honest interfaces.
Whatever you can describe as a value, you can build, because primitives don't reach into you.
Why this is a moral position, not just a stylistic one
A framework asks you to surrender vocabulary. It says: speak in my nouns — Component, Effect, Store, Route — and your problem will be representable. A primitive asks you to keep your vocabulary and only borrow verbs from a well-designed library. The composition belongs to you. This matters because, in a trading firm, the cost of a bad abstraction does not show up in developer convenience surveys; it shows up in the moments when the abstraction is wrong and you cannot reach under it.
A self-adjusting computation.
Move the sliders. Watch only the affected nodes recompute. Compare this against "rerun the whole program on any change," which is what most code does by default.
The graph below evaluates a tiny pricing expression: cost = (price · qty)
· (1 + fee). The leftmost nodes are inputs; everything else is derived.
Each time you change a slider, the system highlights exactly the nodes that
actually had to refire. Pure functions plus dependency tracking buy you this
automatically.
The savings counter is the entire economic argument: a real Jane Street computation has thousands or millions of nodes, and the fraction that fires on any single tick is typically tiny. The graph encodes the structure once; the runtime exploits it forever.
The ideology, examined.
Now the question that this whole brief exists to answer: why do they choose this over the obvious alternatives? It is not aesthetics. There are four reasons, in descending order of how often you will hear them said aloud.
- Risk control through readability. A trading firm can be ended by a tight loop making the wrong decision at speed. The earliest Jane Street response to this was that the founders themselves read every line of code into production. To make that scalable, code had to be readable. OCaml's type system, pure functions, and algebraic data types make the shape of a program legible at a glance; mutable, framework-laden code is not legible at a glance. The choice is not philosophical, it is operational.
- Composability multiplies a small team. Their technology group is small by design. Small teams cannot afford to reinvent abstractions, and they cannot afford the maintenance tax of monolithic frameworks. Composable primitives mean that each engineer's good idea becomes a piece every other engineer can reuse without negotiation. The economics of a primitives library compound; the economics of a framework do not.
- Incremental recomputation is the natural model for real-time data. Markets do not stop. Inputs change constantly. A program that recomputes everything on every tick is wasteful; a program that hand-rolls change-propagation is brittle. A self-adjusting computation graph is the right data structure for the job, the way a hash map is the right data structure for a lookup. Once you have the right structure, the engineering becomes ordinary.
- Types are a cheaper form of verification than tests. A type that says "this function takes a price and returns a quantity" rules out an infinite number of bugs at compile time. A test merely confirms that one specific input behaves correctly. In a domain where the cost of a missed bug is catastrophic, the asymptotic advantage of types compounds. Functional languages with strong, expressive type systems are simply more efficient at producing correct code per engineer-hour.
The reasons you will not usually hear them say
Two more, which are equally true: this style of engineering attracts a certain kind of mind — mathematically inclined, fluent in abstraction, comfortable with rigor — and that selection effect is itself part of the moat. And: building your own primitives means owning your dependencies, which means surviving the bit-rot of other people's libraries for the multi-decade timescale on which a trading firm actually operates.
Their artifacts.
The libraries below are the public face of the ethos. Each is worth studying as an answer to the question "what does it look like to build this idea seriously?"
Incremental
A library for self-adjusting computations. Define values as nodes in a directed graph; change inputs; the runtime recomputes only the affected subgraph. The substrate everything else stands on.
Bonsai
Their declarative UI framework, built on Incremental. State, incrementality, and rendering as separate composable primitives instead of one fused Component abstraction. Compiles a description into a running computation.
Webs
Distributed spreadsheet meets pure functional DSL. Billions of nodes and edges, incremental recomputation under the hood, strong types on top. Used for pricing, risk, and dynamic analysis across the firm.
JSQL
SQL-flavored query language with a uniform interface over in-memory OCaml data and database tables. The in-memory engine sits on Incremental, so queries stay current as data changes. Composability all the way down.
Async
A monadic concurrency library. Cooperative, deterministic, threaded through the type system. Concurrency as a value you compose, not a runtime you negotiate with.
Core / Async kernel
An alternative to the OCaml standard library, designed for the firm's needs. Total functions, clean signatures, no surprise mutation. The vocabulary the rest of the stack speaks.
Thinking in this idiom.
If you walk in with the standard frame, you will write standard answers. The following is how to translate, in real time, from leetcode-shape thinking to the shape Jane Street rewards.
When a Jane Street interviewer hands you a problem, they are not just checking that you can produce a correct answer. They are checking whether the shape of your thinking matches the shape of their codebase. The cheapest way to communicate that match is to talk in terms of values, dependencies, types, and composition — not loops, mutations, and ad-hoc state machines.
"I'd use a for-loop" → "I'd express this as a fold or a map over the input collection."
"I'd cache that with a flag" → "That dependency should be its own node; I'd let the graph cut off propagation when the upstream value is unchanged."
"I'd inherit from a base class" → "I'd factor the shared behavior as a primitive and compose it where needed."
"I'd check that at runtime" → "I can encode that invariant in the type."
"I'd add another field to the component" → "I'd lift state out of the component and let it be addressed by a primitive."
The five questions to ask yourself, aloud, during the problem
- What are the values, and what are the dependencies between them?
- What is the smallest type that makes illegal states unrepresentable?
- Which parts of this need to be recomputed on which kind of change?
- What primitive am I implicitly assuming, and can I name it?
- If this needed to run for a year without restarting, what would break?
None of these will sound out of place. All of them communicate the right thing: that you are not just coding, you are reasoning about a system as a structure of dependencies, with types as the dominant tool for ruling out classes of failure.
A worked reframing
Suppose the prompt is: "given a stream of trades and a stream of mark prices, maintain the current PnL." The standard answer is a loop over events with a mutable PnL variable. The Jane Street answer is: model the trade stream and the mark stream as inputs; PnL is a derived node whose value is a function of the current positions (themselves a fold over the trade stream) and the current marks. Now ask which inputs cause which downstream nodes to refire; that is your incremental update story. You have not yet written code — and you have already communicated the entire mental model.
Read the originals.
Everything in this brief is paraphrased from public Jane Street material. To deepen, go to the sources directly — they are written carefully and they reward rereading.
- Functional Programming at Jane Street — index of internal libraries and applied research. janestreet.com/functional-programming/
- Technology at Jane Street — broader strokes on the engineering culture. janestreet.com/technology/
- "Introducing Incremental" — Yaron Minsky's announcement of the library. blog.janestreet.com/introducing-incremental/
- "Seven Implementations of Incremental" — tech talk on the history of the library. janestreet.com/tech-talks/seven-implementations-of-incremental/
- "Self Adjusting DOM" series — the move from Incremental to UI. blog.janestreet.com/self-adjusting-dom/
- Bonsai documentation — primitives-à-la-carte UI framework. bonsai.red
- "OCaml for the Masses" — Minsky's CACM essay on why Jane Street chose OCaml. dl.acm.org/doi/fullHtml/10.1145/2018396.2018413
- "A Consistent Semantics of Self-Adjusting Computation" — Acar et al., the academic foundation. arxiv.org/pdf/1106.0478
- Signals and Threads podcast — long-form conversations with Jane Street engineers. signalsandthreads.com
- Real World OCaml — the closest thing to a canonical text for the firm's idiom. dev.realworldocaml.org