Architecture Maps

Linear

Architecture map of Linear's local-first sync engine, offline-capable data model, and multi-region infrastructure — compiled from public technical talks and blog posts.

Public Sources Only Stack: TypeScript + React Sync Engine: Local-First Updated: Mar 2026
01

Architecture Overview

Linear is a project management tool built on a local-first sync engine. The client stores a near-complete copy of workspace data in IndexedDB. Mutations happen locally first, then sync asynchronously to the server. Network latency is eliminated from the user interaction path entirely.

~3
Infra Engineers
40+
Model Types
4th
Sync Engine (CTO)
0ms
Perceived Latency
High-Level Architecture
graph TD
    subgraph Client["Client (Browser / Electron)"]
        UI["React + MobX
Observer Components"] OG["Object Graph
(in-memory)"] OP["Object Pool
(normalized store)"] TQ["Transaction Queue"] IDB["IndexedDB
(local database)"] end subgraph Server["Server (GCP)"] API["GraphQL API
(Node.js)"] PG["PostgreSQL
(primary)"] MDB["MongoDB
(sync cache)"] WS["WebSocket
Server"] end UI --> OG OG --> OP OP --> TQ TQ --> IDB TQ --> API API --> PG API --> MDB WS --> OP style UI fill:#181818,stroke:#5588ff,color:#f0f0f0 style OG fill:#181818,stroke:#0055ff,color:#f0f0f0 style OP fill:#181818,stroke:#0055ff,color:#f0f0f0 style TQ fill:#181818,stroke:#0055ff,color:#f0f0f0 style IDB fill:#181818,stroke:#9966cc,color:#f0f0f0 style API fill:#111111,stroke:#00bbdd,color:#f0f0f0 style PG fill:#111111,stroke:#9966cc,color:#f0f0f0 style MDB fill:#111111,stroke:#cc8844,color:#f0f0f0 style WS fill:#111111,stroke:#22cc44,color:#f0f0f0
02

Core Tech Stack

Linear's stack is TypeScript end-to-end, confirmed by CTO Tuomas Artman. A single model definition with decorators generates the database schema, GraphQL types, and client models simultaneously.

React + MobX

Frontend framework with observable reactivity. Components wrapped with observer auto-re-render on changes.

Client

Node.js + TypeScript

Backend services sharing the same type definitions as the frontend.

Server

PostgreSQL

Primary relational database for authoritative data storage.

Data

MongoDB

Caching layer for serialized model objects and delta sync packets. 3-4x faster than BigTable for this use case.

Data

IndexedDB

Browser-side persistence. Treated as a real database, not a cache.

Client

Electron

Desktop shell wrapping the same React web app. No separate codebase.

Client

Y.js (CRDT)

Added for collaborative rich-text editing of issue descriptions. Linear's only CRDT usage.

Collab

GCP + Terraform

Google Cloud Platform infrastructure managed as code. Cloudflare Workers for multi-region routing.

Infra
Technology Layers
block-beta
    columns 1
    A["Presentation — React + MobX observers, keyboard-first UX"]
    B["State — Object Graph + Object Pool (MobX observables)"]
    C["Persistence — IndexedDB (client) + Transaction Queue"]
    D["Transport — WebSockets (real-time) + GraphQL (mutations)"]
    E["Server — Node.js + TypeScript, shared model definitions"]
    F["Storage — PostgreSQL (primary) + MongoDB (sync cache)"]
    G["Infrastructure — GCP, Terraform, Cloudflare Workers"]

    style A fill:#181818,stroke:#5588ff,color:#f0f0f0
    style B fill:#181818,stroke:#0055ff,color:#f0f0f0
    style C fill:#181818,stroke:#9966cc,color:#f0f0f0
    style D fill:#111111,stroke:#22cc44,color:#f0f0f0
    style E fill:#111111,stroke:#00bbdd,color:#f0f0f0
    style F fill:#111111,stroke:#cc8844,color:#f0f0f0
    style G fill:#0a0a0a,stroke:#cc8844,color:#f0f0f0
                
03

Sync Engine Architecture

The sync engine is Linear's defining technical achievement. CTO Tuomas Artman built four sync engines before Linear (gaming portal, Groupon POS, Uber mobile). The engine has two jobs: get the user up to date with the current state, and keep them up to date in real time.

Three Primary Layers

Sync Engine Internal Architecture
graph TD
    subgraph ObjectGraph["Object Graph (MobX)"]
        OBS["Observable Properties
via Object.defineProperty"] REACT["React observer()
auto-re-render"] end subgraph ObjectPool["Object Pool"] ML["modelLookup Map
(ID → Model)"] HYD["updateFromData
hydration"] REF["attachToReferenced
Properties"] end subgraph TxQueue["Transaction Queue"] TX["Transactions
(reversible ops)"] SID["syncId
(monotonic counter)"] PERSIST["IndexedDB
persistence"] end OBS --> REACT ML --> OBS HYD --> ML REF --> ML TX --> PERSIST TX -->|"async"| SERVER["Server"] ML --> TX style OBS fill:#181818,stroke:#0055ff,color:#f0f0f0 style REACT fill:#181818,stroke:#5588ff,color:#f0f0f0 style ML fill:#181818,stroke:#0055ff,color:#f0f0f0 style TX fill:#181818,stroke:#9966cc,color:#f0f0f0 style PERSIST fill:#181818,stroke:#9966cc,color:#f0f0f0 style SID fill:#181818,stroke:#cc8844,color:#f0f0f0 style SERVER fill:#111111,stroke:#00bbdd,color:#f0f0f0

Data Flow

Write Path (Local Mutation)
graph LR
    SAVE["model.save()"] --> OG["Object Graph"]
    OG --> OP["Object Pool"]
    OP --> TQ["Transaction
Queue"] TQ --> IDB["IndexedDB"] TQ -->|"async"| SRV["Server"] style SAVE fill:#0055ff,stroke:#5588ff,color:#fff style OG fill:#181818,stroke:#0055ff,color:#f0f0f0 style OP fill:#181818,stroke:#0055ff,color:#f0f0f0 style TQ fill:#181818,stroke:#9966cc,color:#f0f0f0 style IDB fill:#181818,stroke:#9966cc,color:#f0f0f0 style SRV fill:#111111,stroke:#00bbdd,color:#f0f0f0
Read Path (Remote Update)
graph LR
    SRV["Server"] --> WS["WebSocket"]
    WS --> OP["Object Pool
+ IndexedDB"] OP --> MOB["MobX
Reactivity"] MOB --> OG["Object Graph"] OG --> RE["React
Re-render"] style SRV fill:#111111,stroke:#00bbdd,color:#f0f0f0 style WS fill:#111111,stroke:#22cc44,color:#f0f0f0 style OP fill:#181818,stroke:#0055ff,color:#f0f0f0 style MOB fill:#181818,stroke:#0055ff,color:#f0f0f0 style OG fill:#181818,stroke:#0055ff,color:#f0f0f0 style RE fill:#181818,stroke:#5588ff,color:#f0f0f0
Offline-First

The network layer is not required for the app to function. The save() method on any model object triggers the entire sync cycle. Changes are applied instantly to the local data, then synced when connectivity is available. "The entire process is just one method call away."

04

Model System

Models extend a base Model class using TypeScript decorators. A single definition serves the database schema, GraphQL schema, and client model simultaneously, eliminating separate schema maintenance.

Load Strategies

Strategy Behavior Examples
instant Loaded during bootstrapping Teams, users, projects, labels
lazy Loaded all at once when first needed Secondary configuration data
partial On-demand loading of subsets Issues, attachments
explicitlyRequested Loaded only when explicitly requested Comments, history
local Stored only in client-side IndexedDB UI preferences

Property Types

Standard Properties

Persisted directly to the database. Mapped to both GraphQL fields and IndexedDB columns.

References

E.g., Issue.assignee resolves to a User object, but only assigneeId is persisted.

Back-References

E.g., User.assignedIssues is a computed one-to-many collection derived from the reverse side.

Model Registry & Store Types
graph TD
    subgraph Registry["ModelRegistry"]
        MR["Model Metadata
(load strategy, props)"] end subgraph Stores["Store Types"] FS["FullStore
(complete collections)"] PS["PartialStore
(on-demand subsets)"] end subgraph Models["Model Hierarchy"] BASE["Base Model Class"] ISSUE["Issue"] USER["User"] PROJ["Project"] COMMENT["Comment"] end MR --> FS MR --> PS BASE --> ISSUE BASE --> USER BASE --> PROJ BASE --> COMMENT FS --> USER FS --> PROJ PS --> ISSUE PS --> COMMENT style MR fill:#181818,stroke:#0055ff,color:#f0f0f0 style FS fill:#181818,stroke:#9966cc,color:#f0f0f0 style PS fill:#181818,stroke:#9966cc,color:#f0f0f0 style BASE fill:#111111,stroke:#00bbdd,color:#f0f0f0 style ISSUE fill:#111111,stroke:#5588ff,color:#f0f0f0 style USER fill:#111111,stroke:#5588ff,color:#f0f0f0
05

Bootstrap & Delta Sync

The bootstrapping process loads workspace data in stages. Returning users skip full bootstrap entirely, needing only a delta sync to catch up on changes since their last visit.

Bootstrap & Sync Flow
graph TD
    START["App Start"] --> CHECK{"Schema hash
match?"} CHECK -->|"Yes"| DELTA["Delta Sync
/sync/delta"] CHECK -->|"No"| FULL["Full Bootstrap
/sync/bootstrap?type=full"] FULL --> STORES["StoreManager creates
FullStore / PartialStore"] STORES --> PARSE["Parse newline-delimited
ModelName=JSON"] PARSE --> IDB["Persist to
IndexedDB"] IDB --> HYD["Hydrate instant
models to memory"] HYD --> WS["Establish
WebSocket"] DELTA --> REPLAY["Replay SyncActions
since lastSyncId"] REPLAY --> WS WS --> PARTIAL["Partial Bootstrap
(comments, history)"] style START fill:#0055ff,stroke:#5588ff,color:#fff style CHECK fill:#181818,stroke:#cc8844,color:#f0f0f0 style FULL fill:#181818,stroke:#9966cc,color:#f0f0f0 style DELTA fill:#181818,stroke:#22cc44,color:#f0f0f0 style WS fill:#111111,stroke:#22cc44,color:#f0f0f0 style IDB fill:#181818,stroke:#9966cc,color:#f0f0f0

SyncActions

Immutable records of data changes broadcast to all clients via delta packets. The server may produce additional side effects, so delta packets can differ from original transactions.

Field Description
ID Unique integer, monotonically increasing
Model Name Which model type was affected
Model ID The specific instance identifier
Action Type I (Insert), U (Update), D (Delete), A (Archive)
Data Optional payload with changed fields
Schema Hash Validation

A __schemaHash combining model names, versions, and property names detects mismatches between the app code and locally cached data. If the hash differs, a full bootstrap replaces the local database. This enables fast startup for returning users while ensuring correctness after deployments.

06

Offline-Capable Data Model

Linear treats the client's IndexedDB as a real database, not merely a cache. The server is "just another client to sync with" rather than the exclusive source of truth.

Local-First Writes

Changes happen locally first, then sync asynchronously. The UI never waits for server confirmation.

Near-Complete Copy

Each client maintains a nearly-complete local database copy of the workspace data.

Zero Network Dependency

Network latency is eliminated from the user interaction path entirely. Filtering, searching, and navigating are instant.

Schema Migrations

Database module manages connectivity, table creation, and schema migrations via the hash-based validation system.

IndexedDB Storage Architecture
graph TD
    subgraph IDB["IndexedDB (Browser)"]
        SCHEMA["__schemaHash
(version check)"] FSTORE["FullStore tables
(teams, users, projects)"] PSTORE["PartialStore tables
(issues, attachments)"] TXLOG["Transaction log
(pending mutations)"] end subgraph Runtime["Runtime (Memory)"] POOL["Object Pool
(modelLookup)"] GRAPH["Object Graph
(MobX observables)"] end FSTORE --> POOL PSTORE -->|"on demand"| POOL POOL --> GRAPH GRAPH -->|"propertyChanged"| TXLOG TXLOG -->|"async"| NET["Network Layer"] style SCHEMA fill:#181818,stroke:#cc8844,color:#f0f0f0 style FSTORE fill:#181818,stroke:#9966cc,color:#f0f0f0 style PSTORE fill:#181818,stroke:#9966cc,color:#f0f0f0 style TXLOG fill:#181818,stroke:#0055ff,color:#f0f0f0 style POOL fill:#181818,stroke:#0055ff,color:#f0f0f0 style GRAPH fill:#181818,stroke:#5588ff,color:#f0f0f0 style NET fill:#111111,stroke:#00bbdd,color:#f0f0f0
07

Collaboration & Conflict Resolution

Linear uses two distinct strategies for collaboration: last-writer-wins for structured data, and CRDTs (Y.js) for rich-text documents. The server establishes authoritative ordering for all transactions.

Conflict Resolution Strategy
graph TD
    CHANGE["User Change"] --> TYPE{"Change type?"}

    TYPE -->|"Structured data
(status, assignee, etc.)"| LWW["Last-Writer-Wins"] TYPE -->|"Rich text
(descriptions)"| YJS["Y.js CRDT"] LWW --> SERVER["Server orders
all transactions"] SERVER --> BROADCAST["Broadcast delta
to all clients"] YJS --> MERGE["Automatic merge
(no conflicts)"] YJS --> CURSOR["Real-time cursor
visibility"] YJS --> SNAP["Version snapshots
+ undo/redo"] BROADCAST --> OPT["Optimistic update
or rollback"] style CHANGE fill:#0055ff,stroke:#5588ff,color:#fff style TYPE fill:#181818,stroke:#cc8844,color:#f0f0f0 style LWW fill:#181818,stroke:#0055ff,color:#f0f0f0 style YJS fill:#181818,stroke:#22cc44,color:#f0f0f0 style SERVER fill:#111111,stroke:#00bbdd,color:#f0f0f0 style MERGE fill:#181818,stroke:#22cc44,color:#f0f0f0
Not CRDTs (Mostly)

Linear did not use CRDTs until recently. The collaboration model for structured data aligns more closely with Operational Transformation (OT) than CRDTs, relying on a centralized server for ordering. Y.js was added specifically for collaborative editing of issue descriptions.

Optimistic Updates

Changes apply instantly to the local Object Graph and IndexedDB. If a transaction fails server-side, it is reversed on the client. The UI never shows a loading spinner for write operations.

08

GraphQL API Design

Linear's public API is the same GraphQL API used internally. A notable architectural decision: mutations return only a lastSyncId, with actual data updates arriving through the WebSocket sync channel.

API Architecture & Protocol Split
graph TD
    subgraph GraphQL["GraphQL (Queries & Mutations)"]
        Q["Queries
(read data)"] M["Mutations
(write data)"] INTRO["Full introspection
+ schema discovery"] end subgraph Sync["Streaming REST (Sync Protocol)"] BOOT["/sync/bootstrap
(full + partial)"] DELTAE["/sync/delta
(incremental)"] end subgraph Realtime["WebSockets (Real-Time)"] PUSH["Delta packet
push to clients"] end M -->|"returns lastSyncId
only"| PUSH BOOT --> IDB["IndexedDB"] DELTAE --> IDB PUSH --> IDB subgraph External["External Integrations"] WH["Webhooks
(server-to-server)"] end style Q fill:#181818,stroke:#0055ff,color:#f0f0f0 style M fill:#181818,stroke:#0055ff,color:#f0f0f0 style BOOT fill:#181818,stroke:#9966cc,color:#f0f0f0 style DELTAE fill:#181818,stroke:#22cc44,color:#f0f0f0 style PUSH fill:#181818,stroke:#22cc44,color:#f0f0f0 style WH fill:#111111,stroke:#cc8844,color:#f0f0f0 style IDB fill:#181818,stroke:#9966cc,color:#f0f0f0
Aspect Detail
Rate Limiting Complexity-based: 250K points/hr (API key) or 200K points/hr (OAuth)
Mutation Response Returns only lastSyncId; data flows via WebSocket
Bulk Data Streaming REST endpoints, not GraphQL (performance)
Bootstrap Format Newline-delimited plain text: ModelName=<JSON>
Webhooks Programmatic registration for server-to-server integrations
Error Format Standard GraphQL errors with extensions for context
09

Multi-Region Infrastructure

Linear expanded from single-region to multi-region to address GDPR and data residency requirements. Rather than sharding databases, they replicate the entire production deployment per region.

Multi-Region Request Flow
graph LR
    CLIENT["Client"] --> CF["Cloudflare
Workers Proxy"] CF -->|"extract auth,
get JWT + region"| AUTH["Global Auth
Service"] CF -->|"forward with
signed headers"| REG["Regional
Backend"] REG --> PG["Regional
PostgreSQL"] REG --> SRVS["Full Backend
Stack (isolated)"] AUTH -->|"Pub/Sub"| REG style CLIENT fill:#0055ff,stroke:#5588ff,color:#fff style CF fill:#181818,stroke:#cc8844,color:#f0f0f0 style AUTH fill:#181818,stroke:#0055ff,color:#f0f0f0 style REG fill:#111111,stroke:#00bbdd,color:#f0f0f0 style PG fill:#111111,stroke:#9966cc,color:#f0f0f0 style SRVS fill:#111111,stroke:#00bbdd,color:#f0f0f0

Cross-Region Data Sync

Three patterns for shared tables (users, workspaces) across regions:

Operation Flow
Creating Auth service first (enforces global constraints), then regional database
Deleting Same order as creating, with Postgres triggers creating audit logs
Updating Regional service updates propagate asynchronously via internal API calls
Deployment Strategy

Multi-region was rolled out with feature flags, initially only for Linear engineers. System timezone determines default region selection. Background tasks periodically validate all synced records for consistency between services.

Infrastructure Components

Cloudflare Workers

Proxy layer for multi-region routing. Caches authentication signatures to avoid repeated round-trips.

Infra

Google Pub/Sub

Inter-service messaging for async task scheduling and one-way data flow from auth to regional services.

Infra

Terraform

Infrastructure-as-code. The entire setup is managed by approximately three people.

Infra
10

Performance Philosophy

Linear's core philosophy: "the tool should never be slow." Speed is treated as a feature, not a metric. Architectural decisions are made with perceived latency as the primary constraint.

Local-First Eliminates Latency

All user interactions operate against local data. Network is never in the critical path.

Lazy/Partial Loading

Avoids loading hundreds of thousands of objects upfront. Issues and attachments load on demand.

De-Duping Loader

Centralized loader coalesces multiple concurrent UI requests for the same data into a single fetch.

MobX Reactivity

Only observed properties trigger React re-renders. No unnecessary component updates.

Delta Sync on Return

Returning users skip full bootstrap. IndexedDB persistence means only recent changes need syncing.

MongoDB Sync Cache

Serialized model objects and delta packets cached in MongoDB for 3-4x faster sync compared to alternatives.

Developer Experience

Frontend engineers interact only with local data structures. No manual network calls, no loading states for cached data, no manual UI updates. MobX handles reactivity automatically. The result: developers write code as if the app were entirely local.

11

Keyboard-First UX

Linear's interface is designed keyboard-first. The sync engine enables this: because all data is local, filtering, searching, and navigating are instant operations against IndexedDB rather than server round-trips.

Shortcut Action Design Principle
Cmd/Ctrl+K Global command palette Fuzzy search across all actions
C Create issue Single-key for common actions
E Edit issue No modifier keys for frequent ops
J / K Navigate list Vim-inspired traversal
/ Filter Instant local filtering
Contextual Hints

Shortcut hints appear on hover after a brief delay, teaching users faster workflows progressively. Every action in the interface is accessible via keyboard, from creating and moving issues to filtering, assigning, and changing status.

12

Glossary

CRDTConflict-free Replicated Data Type
GCPGoogle Cloud Platform
GDPRGeneral Data Protection Regulation
IDBIndexedDB (browser storage API)
JWTJSON Web Token
LSELinear Sync Engine
LWWLast-Writer-Wins (conflict strategy)
MobXObservable state management library
OTOperational Transformation
Pub/SubPublish/Subscribe messaging pattern
SyncActionImmutable record of a data change
syncIdMonotonic counter tracking data freshness
Y.jsCRDT framework for collaborative editing
WSWebSocket (real-time transport)
Diagram
100%
Scroll to zoom · Drag to pan · Esc to close