Why I built Rusl

A decade of strong contracts, team after team — and the one thing they always left on the floor.

I loved the contracts, not the format

I joined Square right as it was getting into protobufs. We used them to talk between internal services — defined in a shared place, generated into every language we needed. I liked them immediately.

When I asked people what they liked about protobufs, almost everyone started with the wire format: the compact binary, the efficiency. That was never it for me. The encoding was fine. What mattered was the contract — a strong, enforced agreement about the shape of the data, generated natively for each language and checked in live code at runtime.

And the wire format had a tell. You can't read meaning off the bytes unless you already know which message you're looking at. The contract carried the value; the format just carried the contract. I used the format — and JSON over the same definitions — but I was never in it for the bytes.

The trouble was that contracts lived in raw files nobody wanted to spelunk through. So I built a documentation generator on top of protoc — pages auto-linked from one type to the next — so we could explore our data by its schema instead of by grep. That was the first time I tried to make schemas legible to humans, not just compilers.

The same pattern, at scale

I brought this to Opendoor next, and protobufs were a natural fit: lots of languages that had to talk to each other, a team that had grown fast. I rebuilt the documentation site. We moved faster and with more confidence across services — gRPC natively, or over HTTP — because strong contracts meant everyone knew what was what.

But protobufs effectively stopped at the backend. Pushing them all the way to the frontend is rarely worth it, so I stood up a GraphQL layer — another strong schema standard — and generated it from the protobuf definitions. Good, but heavy. With a lot of effort we got a similar schema on the frontend and the backend, which beat letting the two drift apart.

It worked. And it still left the same hole: people knew the shape of a field, but not everybody knew what it actually meant — how it was used, what it was for. The few who did became the bottleneck. We had the contract. We didn't have the context.

Schemas all the way down — same gap

It kept happening. More gRPC and GraphQL elsewhere: heavy, but it worked. We sketched a schema system of our own once; it never shipped.

At my most recent team — a TypeScript shop — I skipped the heavy machinery and used Zod instead. I declared schemas at the bottom, in storage, then for requests, responses, and DTOs. Commands extend the base schema and add to it; specialized workflows mix schemas to assemble exactly the inputs they need. Schemas all the way down. We validate on the way into storage and trust the data on the way out, with a literal in the payload to identify it — the way __typename works in GraphQL. One change at the storage layer flows through storage, domain, and HTTP, out to OpenAPI, and into a generated frontend client — or gets hidden at any level. The same schema drives forms, displays, and storage.

It was genuinely successful. And it had the exact gap I'd been living with for years — plus a new one: agents won't reliably reuse the schemas that already exist. We'd catch an agent redefining a thing by hand instead of importing the canonical version. Generating that duplicate is the cheap part — five seconds. Then you own a second shape: its forms, its validation, its migrations, and the drift between it and the original. Everyone who comes after has to work out which one is real. We wrote skills. We put hard-and-fast rules in AGENTS.md and CLAUDE.md. They're followed most of the time. Most isn't good enough.

The thing that was always broken

Step back across all of it and there's one root cause. Shape was, more or less, a solved problem. Meaning never was. What a field means, why it exists, how you're allowed to use it — that knowledge lived in someone's head, or in a Slack thread from eight months ago. People forget it. People leave. New people re-derive it, wrong.

Strong contracts front to back always made teams better. They always left the same thing on the floor: a shared, durable understanding of what the data means.

Why this matters more now

In the age of agents, that gap stops being an annoyance and becomes dangerous — for maintainability and for correctness.

The software-quality practices we built over decades didn't expire because the author changed. Single Responsibility. DRY. Strong contracts. They don't care whether a human or an agent is at the keyboard. An agent with a canonical, annotated schema doesn't have to guess, re-derive, or redefine — it checks the shared contract and threads it through the app. Fewer mistakes, less to maintain, better outcomes when humans and agents work on the same code. That's the gap I finally set out to close.

What GitHub taught me

I've used GitHub since the day it launched, and I watched what a shared place to collaborate did to software. Code sharing and library reuse went vertical. The open-source community got vibrant. The whole field leapt ahead because there was somewhere to do the work together.

That's still true in the agent age. Good practices still apply — we just need a place to apply them to schemas, and to the meaning on top of them.

So we built Rusl

Shape has one author. Meaning has many.

Rusl is the context layer for your schemas: a collaborative schema registry, plus a place to store typed, validated annotations — meaning — on top of schema shapes.

That's the separation I'd never made cleanly before. A schema's structure is owned and versioned like code. The understanding around it — what fields mean, how they're used, what's endorsed — is contributed, reviewed, and versioned by everyone who works with it. What GitHub is to code, Rusl is to data.

We built a comprehensive CLI to author schemas, bundles, annotation types, and annotations; to give feedback on and endorse them; and to search and discover what already exists. That CLI is also an MCP server, so your agent is a first-class participant: it searches and pulls canonical, annotated schemas instead of inventing new ones, while you keep control through the UI and the CLI. And it's also a schema package manager — think npm or cargo. It resolves your schemas and bundles to pinned versions, writes a lockfile, and downloads them into your app, so you get a fixed set — and so does every other app on the same bundle.

For real teams, not just open source

Most teams can't make everything public, and shouldn't have to. That's why Rusl has personal, community, and business accounts. With a business account you declare private schemas, bundles, annotation types, and annotations — shared only with people inside your company. The CLI handles public and private transparently, using your own credentials. You decide what's shared and what stays in-house.

Rusl is built on a simple conviction, the same one behind the schema-driven manifesto: the contract comes first, and everything else follows from it.

Decide what things are. Generate the rest.