---
title: Local-First Patterns for the Server
synced_from_vault: true
vault_source: 03-living-docs/patterns/Local-First-Patterns.md
public: true
type: pattern
category: engineering
tags:
  - pattern
  - engineering
  - architecture
  - local-first
  - mobile
  - sync
created: 2026-03-08T00:00:00.000Z
origin: 'Dave Paola, blog draft (2024); Ink & Switch local-first memo'
---

| | |
|-|-|
| **Category** | Engineering / Architecture |
| **Origin** | Dave Paola, blog draft (Sep 2024); Ink & Switch local-first memo |
| **Surfaced in OS** | Mar 8, 2026 (imported from ZK) |

---

## Core Concept

Six server-side patterns that enable a small team to ship local-first native apps with offline capability, superior performance, and data ownership. The client app is built as if the cloud doesn't exist — sync is a separate, loosely-coupled module.

---

## The Six Patterns

### 1. Distributed Entities (UUIDv7)

In a local-first world, every client can create data. The server is demoted — it is no longer the only source of truth. **UUIDv7** as primary key gives you:

- Monotonically increasing values (like sequential integers) via embedded Unix epoch timestamp at millisecond grain
- No practical possibility of collision across any environment
- Works as cursor for cursor-based pagination — use last ID as cursor with consistent sort

UUIDv7 enables record creation in any environment with guaranteed primary key uniqueness, making sync much simpler.

### 2. RESTful API

Don't use GraphQL. It makes query patterns essentially unknown and performance optimization nearly impossible for a small team. Each distributed entity gets Create, Read, Update, and Destroy endpoints.

### 3. Delta Sync

Clients query the server for all records updated or touched "as of" a certain timestamp. Without this, they must walk the entire database on every sync. Implement as a standard `/sync` endpoint for all distributed entities. Enables clients to download both updated records and records created on other clients.

### 4. Optimistic Locking

Keep a `lock_version` integer column on each distributed entity. Increment on every update. Enables clients to detect stale data and the server to reject it.

### 5. Soft Delete

Clients need to know when data has been deleted. If a record simply disappears from the API response, the client must manually find and remove missing records. A soft-delete column lets the client simply update the record. Garbage-collect soft-deleted records later.

### 6. Separate the App from Sync

Client apps use fully native data storage and UI patterns. Sync is implemented as a standalone module that interfaces with the data layer via pub/sub. The client app can literally be built as if there is no sync. This keeps client code simple, fast, and provides superior UX without interweaving synchronization with business logic.

---

## Architecture Summary

- **Client apps:** Basic native apps using vendor-supplied data layers (SwiftData on iOS, Room on Android)
- **Sync engine:** Loosely coupled, pub/sub with data stack
- **API:** Implements the six patterns above

---

## On CRDTs

CRDTs (Conflict-free Replicated Data Types) attempt to compress sync complexity into the data types themselves. Admirable approach, but not yet mature enough for sophisticated use cases. Business logic sometimes dictates different conflict resolution in different scenarios. Hence: implement your own synchronization logic.

---

## On Multiple Codebases

Swift/SwiftUI and Kotlin/Jetpack Compose have drastically compressed the complexity of native development since 2014. Both platforms take remarkably similar declarative-UI approaches (lessons from React). The overhead of separate codebases is more than worth the trade-off for native superpowers and superior UX.

In DHH's words: "It's fun to be competent. Don't be scared of native code."

---

## Why It Matters

In a world where shipping better software at higher velocity wins, local-first is a competitive edge. These patterns are deliberately simple — no exotic dependencies, no framework lock-in. A small team can implement all six and get offline-capable, high-performance native apps.

---

## Related Patterns

- [Protocol Over Tool](/patterns/protocol-over-tool) — the sync protocol is the product, not any particular client implementation
- [Gall's Law](/patterns/galls-law) — these six patterns evolved from simple systems that worked, not designed from scratch
- [Integration vs Composition](/patterns/integration-vs-composition) — separating app from sync is a composition choice

---

## Cross-References

