Local First: How to Build Software Which Still Works after the Acquihire

2026-08-0129 min read

Almost every collaborative application you use is held up by a bespoke server that somebody has to keep running, keep paying for, and keep maintaining. Alex Good's argument in this talk is that this arrangement is not a technical necessity but an accident, and that it makes software fragile in a specific and avoidable way: when the company is acquihired, the funding runs out, or the maintainer moves on, the application stops working even though the user's own device is perfectly capable of running it. His central thesis is that if we invest once in generic, application-independent synchronization infrastructure — a Git-like graph of fine-grained changes with deterministic automatic merging — then collaboration becomes a small increment on top of a local application rather than a distributed systems project, and the servers become a commodity you can swap out or do without.

Good works at Ink & Switch, which he describes as a research lab focused on building software that is good for creative thinking, under the "tools for thought" banner. He is a full-time maintainer of Automerge, the open source library the talk's examples are built on. InfoQ's speaker bio adds that he spent a large part of his career building and maintaining distributed systems of ever increasing complexity — the background behind his claim that distributed systems expertise is a separate discipline from client development. InfoQ's recording runs 43:58 and is dated April 8, 2026, recorded at QCon London.

Throughout these notes, claims are the speaker's unless explicitly marked as writer's note, which is background I have added for readers who have not met a term before. The talk has a substantial audience Q&A, and I have folded that material into the relevant sections rather than reproducing it separately.

What You Will Learn

  • Why adding a second device to a simple application usually forces you into building and operating a distributed system, and what that costs in engineering effort, user experience, and user agency.
  • The concrete definition of local-first software and the specific properties it demands, including the ones cloud collaboration gives up and the ones local-only software gives up.
  • Why Git is already local-first, why naively storing application state in a Git repository fails, and the two changes that turn that failure into a workable design.
  • How a commit DAG plus fine-grained operations produces deterministic conflict resolution, and why every replica reaching the same resolution matters more than the resolution being correct in any absolute sense.
  • What adding sync to a plain TypeScript application looks like with Automerge: repositories, network and storage adapters, document handles, and change wrapping.
  • How keeping full change history for free enables branch-and-review workflows over arbitrary data types, including drawings, and why that is also the foundation Good proposes for trusting LLM edits.
  • The problems that are genuinely unsolved: authorization, schema enforcement and evolution, partial replication, and referential integrity across merges.
  • The engineering techniques that make this practical at all, in particular metadata compression for per-keystroke commits.

The Fragility Argument

Good opens with a personal story rather than a principle. Some years ago he built a small Android application to track his workouts. It took a few hours, used off-the-shelf Android components, and was fun to build precisely because it was a small amount of code that he could iterate on quickly. He gave it to friends. Then one friend asked to use it on a laptop as well.

That single request changed the shape of the project entirely. To support a second device he would have to design and implement a server and an API, write a whole separate piece of software to run on it, handle optimistic UI updates and synchronization inside the client, actually deploy the server somewhere and keep it running, and solve authentication and authorization. A few hours of fun had become a job. He never built it.

He uses "collaborative" throughout in a deliberately broad sense: anything where multiple devices work on the same data, whether or not multiple humans are involved. Multi-device is the same problem as multi-user.

His objection was partly aesthetic and partly structural. Conceptually there is a phone and a laptop, both sitting in the same room; introducing a third computer he would never see, purely to move data between them, felt untidy. Structurally, his friends would now depend on servers he personally rented in order to use software running on their own hardware.

Three Costs, Not One

Generalising from that story, Good identifies three distinct ways in which server-backed collaboration hurts.

Development is harder than it looks. It is not simply that there is more code to write. Distributed systems introduce design problems that are an entirely separate area of expertise from the expertise needed to write the client. You do not just have more work; you have a different kind of work.

The user experience often goes backwards. His examples are pointed. Tapping a folder in Notion shows a loading spinner even for a folder you just tapped. Writing on a plane requires having remembered to download the right extensions or enable the right option, whereas editing a document in Word twenty years ago required no thought at all. And users lose what he calls creative privacy: the common pattern of copying a Google Doc into a private duplicate so that you can make changes without everyone watching, and hand them back only when ready.

Users depend on machines they do not control or know about. Here he reaches for Leslie Lamport's definition — Lamport being, as Good notes, the inventor of the Paxos protocol among other things — "A distributed system is one in which the failure of a computer you didn't even know existed can render your own computer unusable" — and points out that it now describes ordinary consumer software. If he stops paying for the DigitalOcean droplet, his friend loses access to his own workout data. If the workout tracker became a startup, got sold, and ended up on the list of shut-down products, the users have nothing. This is the acquihire in the talk's title.

The Repair Argument

Good frames a broader concern that motivates the rest of the talk: engineering needs a culture of repair and maintenance rather than disposal. Custom server software is hostile to repair because expertise in it is concentrated in one person or one small team, and when they leave, nobody is standing by to take over. His analogy is plumbing where every plumber used a different gauge of pipe and built a custom boiler for each house — maintenance would be far more expensive and far more likely to simply not happen.

The structural consequences he draws from this are that the high cost of building collaborative software prevents it from scaling down to small communities that do not constitute a viable market, and that it concentrates an undue amount of control in the hands of whoever runs the servers. He adds an efficiency argument: we carry powerful computers and barely use them, spinning up large clusters elsewhere to do the actual work while the local machine mostly renders pixels. His conclusion is not merely that this wastes hardware but that it imposes a major performance cost on the user — the same cost the "no spinners" principle later targets.

What He Wants Instead

The target architecture Good sketches is deliberately boring. There should be a generic protocol and a set of interchangeable servers that the application developer does not have to think about — a commodity you buy, and can point somewhere else if it disappears. The application holds little more than a URL saying where its sync server is. Crucially, that server should be optional: the application works without it, and devices should be able to talk to each other directly where possible.

Researching this, he found the Ink & Switch essay on local-first software, whose principles he presents as the requirements list. The ones he draws out are:

  • No spinners. The data is on your device most of the time, so switching folders has no latency.
  • Multi-device. Expected from cloud software, but genuinely hard for local-only software, which forces you to email documents around or park them on a file-sharing server.
  • The network is optional. Turning off Wi-Fi should not stop your word processor.
  • Collaboration is seamless. You should not be manually copying changes between files; it should be built into the application.

He notes there are further principles in the essay that he is not covering — they are, in his words, "less what I'm going to talk about, except towards the end," which is where the closing material on user agency and LLM review workflows picks them back up. He offers a compact restatement of the whole set: local-first is a design principle in which the data on the user's device is the primary source of truth, and copies on servers or other devices are secondary.

Git Is Already Local-First

To show that this is achievable rather than utopian, Good points at a system everyone in the room already uses. Git is local-first in exactly his sense: the data is on your machine, you can always make progress locally, turning off GitHub makes no difference to your local repository, you can use any server, and you can sync directly between two of your own machines if you have to. He is explicit that he is not proposing anyone use Git as an end-user tool — he is borrowing its structure.

Writer's note: Git's history is a directed acyclic graph (DAG) of commits. Each commit names its parent or parents, so branching and merging are recorded in the graph itself rather than in metadata alongside it. This DAG is the object Good carries over into the design that follows.

The Deliberately Terrible Version

He then walks through a thought experiment he flags upfront as a bad idea, because its failure modes are what motivate the real design. Suppose you write an application whose storage is a Git repository containing a single JSON file of application state. The user never sees Git. Every change the application makes becomes a commit. Each device keeps its state on its own branch; to sync, you push and pull all the other side's device branches and merge them into your own.

Take a todo list. On the laptop you delete the first entry; on the phone you mark the second entry as done. Two commits now exist on two machines, and syncing surfaces two separate problems.

The first is ordering. Git requires that the state of the repository at any point be a single commit, so the two commits must be linearised — laptop-then-phone, phone-then-laptop, or via a merge commit. Unless every device independently arrives at the same ordering, every sync produces a merge conflict whether or not anything genuinely conflicts.

The second is that even with the ordering settled, you get a textual merge conflict in the JSON file. The application would have to rebase to reorder the commits and then inspect two versions of a file and work out what happened, on every sync. Good's assessment is that this would obviously be very hard to build.

Fix One: Stop Insisting on a Single Commit

The first change is to relax the requirement that the repository's state is one commit. Instead, keep the commit DAG exactly as the users created it. When two commits do conflict, the system arbitrarily chooses the commit with the lowest hash as the state, and exposes an API through which the application can examine the losing states.

The property that makes this acceptable is subtle and worth dwelling on: because each device individually produced valid JSON, the arbitrarily chosen winner is always valid JSON that the application can render. The user therefore keeps making progress and can inspect and resolve conflicts when they choose, rather than being blocked at sync time by a document in an unusable intermediate state.

Fix Two: Make Changes Finer-Grained Than a Snapshot

The remaining annoyance in the todo example is that the two edits are not really in conflict at all — one deleted an entry, the other toggled a different entry — and they only appear to conflict because Git considers two commits to conflict when they touch the same file. You could put each todo in its own file, but then you have to solve ordering across files.

The better answer is to abandon filesystem snapshots as the unit of change. Rather than editing text files on disk, the system records operations on general-purpose data structures — lists and maps, the things programmers already work with. A single Git commit that adds a todo becomes a sequence like: create a list; insert an object into that list; set the value of that object's id to 1; add the characters "Buy milk" to the object's text attribute; set done to true.

With that granularity, the two concurrent edits become "delete the object at todos/0" and "set the value of todos/1 to true", which can both be applied. Good is careful not to overclaim: conflicts still happen and still need resolving, but this makes them much rarer.

What the Two Fixes Buy

The resulting system tracks changes as a graph of commits, uses operations on a generic data structure instead of filesystem snapshots, and applies a default automatic conflict resolution. Good singles out one property of that default as the important one: every node holding the same commit DAG reaches the same default resolution. Two people who are in sync are looking at the same thing. Without that guarantee, collaborative systems become very confusing to reason about. Writer's note: a data structure with these properties — concurrent operations that every replica merges to an identical result without coordination — is what the literature calls a CRDT (conflict-free replicated data type). Automerge and Yjs are both CRDT libraries, and the commit DAG and fine-grained operations described here are how Automerge implements one. Good does not use the term in this talk.

The payoff is a mechanical, application-independent sync layer that serves both real-time and asynchronous change. He notes that Git itself does not give you this: asynchronous collaboration goes through a pull request, and real-time collaboration means getting on a Zoom call. Here both run on the same substrate. The consequence for the developer is a change of job description — from managing a distributed system to doing version control over their domain data.

Architecture and Data Flow

Labelling note: the following diagram is my redrawing of the pieces Good describes in the Automerge walkthrough, not a slide reproduced from the talk. The components and their relationships are his; the visual arrangement is mine.

flowchart TD
    subgraph DeviceA[Device A]
        AppA[Application logic] -->|docHandle.change| DocA[Automerge document]
        DocA -->|change event| AppA
        DocA <--> RepoA[Repo]
        RepoA <--> StoreA[(Storage adapter: IndexedDB or filesystem)]
    end
    subgraph DeviceB[Device B]
        AppB[Application logic] <--> DocB[Automerge document]
        DocB <--> RepoB[Repo]
        RepoB <--> StoreB[(Storage adapter)]
    end
    RepoA <-->|network adapter: WebSocket| Sync[Generic sync server, fungible and optional]
    RepoB <-->|network adapter: WebSocket| Sync
    RepoA <-.->|network adapter: PeerJS, direct peer-to-peer| RepoB

Two things the diagram adds that the prose does not. First, it is the storage adapter, not the network adapter, that keeps a device functioning in isolation: everything inside a device box is reachable with the network edges cut. Second, the dashed peer-to-peer edge is a genuine alternative path rather than a fallback — both edges hang off the same repository object, so a device is not degraded by taking one instead of the other.

Building It Today: The Automerge Walkthrough

Good demonstrates with Automerge because that is what he maintains, and names Yjs as the other major implementation of similar ideas. His example is roughly 80 lines of TypeScript implementing a todo list, shown first as a purely local application and then with sync added, so the increment is visible.

The local version is unremarkable: application state on window, a div with an input and an add button, a list rendered from the state, a renderTodos function called after every change, and two event handlers — one that reads the input value and pushes it into the state, one that toggles done.

Adding Sync

The first step is creating a repository, whose interesting parameters are its network and storage components.

The network adapter in the demo connects over WebSocket to a public sync server that Ink & Switch runs, and Good stresses it could equally be one you run. It does not even have to be a sync server: there is a broadcast adapter that uses the same mechanism to push changes between tabs in the same browser, and a PeerJS adapter for talking to other devices peer-to-peer. His summary of the interface is that anything which can provide a stream of bytes can implement it.

The storage adapter in the demo is IndexedDB, so that reloading the page restores the document, which is what makes it local-first — you do not need the network. Outside the browser you use the filesystem instead; it is a generic interface.

Writer's note: IndexedDB is the browser's built-in transactional client-side database, and PeerJS is a library wrapping WebRTC for browser-to-browser connections. Both are ordinary web platform machinery here, not Automerge-specific.

Some boilerplate follows. Every document in the repository has an ID, so the application either creates a document or looks one up. The demo puts the document URL in the URL fragment: if a document URL is present in the hash it calls repo.find, otherwise it creates a new document. This is how he describes collaborating on someone else's document — they send you the ID and you look it up. The result is a document handle, which is the object you use both to change the document and to listen for changes to it.

The Three Changes to Application Code

With the repository in place, the collaborative version differs from the local one in three ways.

First, instead of rendering from the global state, the code awaits handle.doc, which waits until the document is available — if someone sent you a URL, this is where it gets fetched from the sync server or any other connected peer.

Second, state mutations get wrapped in docHandle.change. Good emphasises that the logic inside the callback is identical to what you would write against a plain JavaScript object; you are still just twiddling state. The wrapper captures the change, turns it into a commit, sends it over the network, and stores it locally.

Third, the explicit renderTodos calls at the end of each mutating method disappear. Document handles expose a generic way to react to changes, so the code subscribes once and re-renders whenever the document changes — whether the change originated locally or arrived over the network. Reactivity comes for free.

His summary of the demo, in which he copies a URL from one browser window into another and makes concurrent edits, is that this was a very small incremental change over a simple local application, and it delivered synchronization in a network-optional fashion with a backend that does not care about the application and is fungible. That is precisely what he wanted for the workout tracker.

He also shows a rich text editor, deliberately disconnecting the two sides to demonstrate out-of-order delivery: one side makes a span bold while the other adds a list item, and the merge produces a predictable result.

Branches, Review, and Change History

Good is careful not to leave the impression that automatic merging is the whole story. Simple applications have obvious merge behaviour; most applications are more complex than a todo list or a single rich text document, and have invariants they need to maintain.

His distinction is between real-time and asynchronous change. In real-time scenarios, merging everything usually does make sense, because you are on a call and can simply say "did you just change that?" when your edit gets overwritten. But a lot of the time you are not in real time — you are on a plane, or you are making a change large enough that merging it without review would be wrong.

For those cases, the commit DAG lets you reintroduce branches. He is precise about the difference from Git: a branch here does not have to be a single commit, it can be a set of commits.

The important claim is about cost. Because the system already tracks fine-grained change history in order to synchronize at all, building review workflows on top does not require storing anything new. His demonstration edits an RFC, creates a branch from a dropdown, adds a paragraph, views the diff, sends it to a colleague who leaves a comment, merges it, and sees the branch-and-merge history alongside. He calls this "pretty ho-hum" for text, because we already do exactly this workflow with source code.

The point of the second demonstration is that the infrastructure is not text-specific. They took tldraw, an off-the-shelf open source drawing application, and changed it to store its state in Automerge documents rather than its own JavaScript state. Diff visualisation between versions then came almost for free: the application developer's only work was telling the application how to display the difference between two versions of the data structure. They did no work to track change history, represent points in history, or synchronize any of it.

Good's framing of why this matters is that recasting application development as version control for domain data — rather than as a frontend onto a single source of truth stored somewhere else — yields generic infrastructure that protects user agency and delivers the local-first benefits. He states as his own belief that it reduces development complexity because there is no extra distributed system to manage, and reports from his own experience that removing storage and networking concerns let him move much faster on the frontend he actually cared about.

Local-First and LLMs

The final opportunity Good raises connects the two topics deliberately. LLMs are powerful but unreliable, and generally do not produce output you can trust straight away. Local-first software has to build general-purpose version control for arbitrary application data anyway, in order to synchronize — and that is exactly the infrastructure an LLM workflow needs.

His argument is that solving both at once yields applications more capable than either alone would have produced: building change management on top of a centralised application would have been a lot of work, and without change management you could not have trusted an LLM-based workflow in the first place.

The lab's prototype adds a bot tab to the RFC editor. A request goes to your configured LLM with a description of how to make changes to the document plus your prompt, and the resulting edits are presented through the same review tooling built for human edits. His verdict on the sample output, after asking it to make a paragraph more casual, is that it "has done the LLM thing of adding a bunch of adjectives" — which is the point: the review layer is there because you should expect to reject some of it.

Trade-offs and Limitations

It Is a Trade, Not a Panacea

Good states this directly. The approach works really well for data that can already be thought of as a document or a media-editing artifact. It works badly for something like e-commerce, because that domain is fundamentally about consensus — what is actually available in the store, whether you actually bought it. His position is that the trade is often worth making for the kinds of applications he cares about, but he does not claim it generalises.

An audience question sharpened this by noting that in his examples the server was essentially a sophisticated backup, whereas real servers often deliver functionality — contacting third parties, checking out a purchase. Good's answer distinguishes two cases. For bad data, you always have the full history, so you can identify a bad change, ignore it, and go back. For real-world side effects you cannot: you cannot unsend an email or ask recipients to forget it. Those operations do need a server that controls access and performs authorization. His prescription is to make such servers small and constrained — governing only the specific side-effecting operations — rather than owning the whole application state.

Authorization Is the Big Unsolved Problem

Everything in the talk works well until you want authentication and authorization, which Good notes people want quite a lot. The current practice is to layer auth over the sync server so you can only push and pull where you are authenticated. The problem is that this drags the server back into inspecting changes and deciding which are allowed — exactly the application-specific work the whole design was trying to eliminate — often for something as simple as preventing a user from writing to another user's todo list.

The direction Ink & Switch is pursuing is representing users and devices as cryptographic key pairs, which lets cryptography convert the problem into a key management problem. Good's aside that key management is "famously easy to solve" is dry humour, not a claim. He does signal maturity, though: the lab thinks it has results coming on this front, so this is active engineering rather than a purely speculative direction.

Answering the audience question above, he named the project: Keyhive, with notes on the lab's website. The design layers group management over the key pairs as a commit DAG of its own — a group for an organisation, a group representing a person, a group representing the devices that person owns, forming a graph you follow. The goal he states plainly: authorization and authentication should be a data structure you synchronize, so that you only need the auth commit DAG rather than needing to ask a particular server whether you are allowed to do something.

There is also a plain infrastructure gap. There is currently no generic public layer anyone can use; in practice everybody runs their own small private sync server.

Schema Enforcement, Evolution, and Referential Integrity

The operations in the commits are generic. They enforce that the document is valid JSON, but not that a price stays below a limit or that a counter never exceeds a maximum. Such invariants are easy to police through review workflows when the data is slow-moving human content like text, and much harder to enforce automatically.

Local-first makes this worse in a specific way: you cannot assume the same schema is running everywhere. You add a field in a new version of the application and then sync with someone still running the old version. Good calls this a tough problem.

A related audience question asked how relations between data elements survive merging, when an external constraint might be broken and leave the application not knowing what to do. His concrete example is a side table listing "important" todos by ID or index; nothing guarantees those referenced items still exist after a merge. His honest answer for today is that you fudge it, and that being optimistic makes it less severe — if a referenced item is not there, ignore it. For the longer term he wants custom operation types that encode invariants, expressed as constraints on the ordering of the commit DAG so that you can never receive a change without the thing it depends on.

Partial Replication and Weak Devices

Large datasets are a problem because you do not actually want everything locally — perhaps only the last month's documents, or not everything produced by a several-hundred-person team you happen to have access to. Indexing and partial synchronization are open work.

Asked specifically about heterogeneous fleets where some devices are too weak to do the computation themselves, Good reached for a Git analogy: a shallow clone client that does not fetch and reify all history, but asks a peer for the latest state it should care about, and submits new changes as deltas against that latest state for the peer to integrate into the commit DAG and publish. He is clear that Automerge has not done this work, but points at Eg-walker as an algorithm that supports it well, whose basis is holding the latest state and reifying history only when needed.

History Size and Metadata Overhead

An audience member raised the obvious scaling worry: in a text editor the commit history gets large very quickly. Good's answer is that this is the central technical problem all real systems in this space are solving — compressing change metadata, not the content. If you create a commit per keystroke with a commit hash and a reference to the previous hash, you have at least 64 bytes of overhead per keystroke.

The mitigation is run-length encoding that exploits patterns in the data: when typing, one character normally follows another, and very few people type in reverse. So the system can encode "begin here, end here, ten characters in between" rather than materialising every commit's metadata on the wire. The compressed payload is essentially a start hash, an end hash, and the characters, with hashes recomputable if you need to verify them. Conceptually it remains a commit per keystroke.

Writer's note: run-length encoding compresses a run of predictable values into a description of the run. It applies here because sequential typing produces operation identifiers that increment predictably, so the run can be described rather than enumerated.

Where This Leaves You Today

Good's own summary of the current state is that you will run your own sync servers and perform schema enforcement, access control, and indexing outside the sync server. He still believes this is simpler than the centralised version for a lot of document-editing use cases, but he explicitly calls it extra complexity rather than a free win. He attributes the absence of generic infrastructure today to the combination of all these open problems, and notes research is ongoing on each.

The Server Can Be Blind

One property came out only under questioning, and cuts in the user's favour. The sync protocol cares only about the structure of the commit graph, not the contents of the commits. Those contents can therefore be end-to-end encrypted, with optimisations layered on top. Good confirmed the server can be blind to the data and said this is what he is currently working on.

Distributing the Application Itself

Asked who has authority over distributing versions of the software, Good's position is that it is fine for the application to be distributed by a central entity. The property he insists on is narrower: the application must keep working without the server, because the application is to some extent under your control while a server you depend on is not. He treats fully distributed publishing as an orthogonal problem, and gestures at the ATProto ecosystem as the beginnings of good public data infrastructure, suggesting you could publish an application to a cryptographic ledger — explicitly not a blockchain, more like a PLC or certificate transparency approach.

Writer's note: certificate transparency is the practice of publishing issued TLS certificates to append-only, publicly auditable logs so that mis-issuance is detectable after the fact. The analogy Good is drawing is to verifiable append-only publication rather than to consensus-based ledgers.

Practical Takeaways

  • Ask what happens to your application when the server stops before you design it, not after. If the answer is that a working device becomes useless, that is a design decision you are making, not a constraint you are under.
  • Prototype the local-only version first, then measure the increment. The talk's central demonstration is that going from a local todo list to a synchronized one was a repository construction, a change wrapper around mutations, and a subscription. If your sync approach requires substantially more than that, the abstraction is in the wrong place.
  • Choose granularity of change deliberately. Recording operations on maps and lists rather than snapshots is what removes most spurious conflicts, so decide this before you decide anything else about your sync design.
  • Require determinism from your merge rule, not correctness. Picking the lowest hash is arbitrary and admitted to be so; what makes it usable is that every replica picks the same one and the losing values remain inspectable.
  • Keep server-mediated operations to the irreversible ones. Payment capture, email sending, and external API calls need a coordination point; document state usually does not. Draw the boundary at side effects you cannot undo.
  • Budget for metadata, not payload, in change-log designs. A per-keystroke history is affordable only with run-length encoding of change identifiers; design the encoding before you commit to the granularity.
  • Treat authorization as a data structure you replicate if you want to stay server-optional, rather than as a check a server performs. This is unfinished research today, so plan for a private sync server with an auth layer in the meantime.
  • Plan for version skew explicitly when devices hold the primary copy. Old clients will merge with new ones, so schema changes need to be additive and ignorable by design.
  • If you want LLM edits to be reviewable, build the change-tracking layer first. The review UI Good demonstrates is a consequence of having history, not a separate feature.

Key Terms

  • Local-first software — The category name for applications satisfying the principles above; the practical test Good applies is whether the application still works with the network and the vendor removed.
  • Creative privacy — Good's term for the ability to work on changes unobserved before sharing them; commonly simulated today by duplicating a shared document.
  • Commit DAG — The directed acyclic graph of changes, each naming its predecessors, that is the unit of synchronization in this design. Branching and concurrency are recorded in the graph rather than resolved away.
  • Keyhive — Ink & Switch's in-progress project representing devices and groups as cryptographic key pairs in their own commit DAG, so authorization is synchronized data rather than a server-side check.
  • Eg-walker — An algorithm Good names as supporting clients that hold only the latest state and reify history on demand, the basis for a shallow-clone-style approach for weak devices.
  • ATProto — The protocol ecosystem Good points to as having the beginnings of general-purpose public data infrastructure.

The talk ends without a grand claim. Good's own summary is that this is a trade rather than a solution, that the trade is worth making for document-shaped applications, and that the remaining obstacles — authorization, schema evolution, partial replication — are the reason the generic infrastructure he wants does not yet exist. What makes the argument worth taking seriously is the asymmetry he keeps returning to: the infrastructure you build to make software survive its maintainer is the same infrastructure that makes it work offline, makes it fast, and makes it possible to review a machine's edits before accepting them. Those are four problems with one answer.


Reference: Alex Good, Local First – How to Build Software Which Still Works after the Acquihire, QCon London, recorded April 8, 2026, published by InfoQ.