世界を動かす技術を、日本語で。

Linearが私をローカルファーストの迷宮に導いた

概要

Linear を使い始めたことで、 local-firstアーキテクチャ に興味を持ち、従来のWebアプリ開発の常識が覆された体験。 JazzElectric SQL など、最新のlocal-firstツールやエコシステムの現状を調査。 local-first のメリット・課題・適用場面について実体験を交え解説。 今後の local-first の発展性と、開発者体験の変化への期待。 自身の経験や知見を共有し、読者にも新しいアーキテクチャへの挑戦を推奨。

Local-Firstアーキテクチャとの出会い

  • Linear は驚くほど高速なプロジェクト管理ツール
  • クリックやステータス更新が 即時反映 される体験
  • 従来型Webアプリ との違いに困惑
  • ネットワーク遅延や競合処理、オフライン対応の疑問
  • LiveStore のデモがlocal-firstの理解に最適

技術的な深掘りのきっかけ

  • 雨の週末に技術調査へ没頭
  • Linearの同期エンジン のリバースエンジニアリング記事
  • CTO Tuomas Artmanによるアーキテクチャ解説
  • Figma のマルチプレイヤー技術も参考
  • IndexedDB を本格的なDBとして活用
  • 変更はまずローカルで発生し、GraphQLやWebSocketで同期
  • local-first はUX戦略・データ哲学の両面を持つ
  • クライアントが自前DBを持ち、サーバーは同期先の一つという発想

従来型 vs local-firstの構造比較

  • 従来型 :サーバーが唯一の信頼ソース
    • Client → HTTPリクエスト → Server → DB → レスポンス → Client
  • local-first型 :各クライアントがほぼ完全なDBを保持
    • Client → ローカルDB → UI更新
    • ↓(非同期で)
    • Syncエンジン → Server → 他クライアント
  • ネットワーク遅延排除 による体感速度の向上

実装の難しさと課題

  • Linear方式 の模倣は非常に困難
  • オフライン/オンライン切替、競合解決、部分同期、スキーマ移行、分散環境でのセキュリティ等の複雑性
  • 既存のlocal-firstソリューションの必要性

2025年のLocal-Firstエコシステム

  • Electric SQL :Postgresベースの同期エンジン
  • PowerSync :エンタープライズ向け
  • Jazz :シンプルさで注目
  • Replicache :先駆者(サービス終了)
  • Zero :Replicacheチームの新アプローチ
  • Triplit :TripleStore型同期
  • Instant :開発者体験重視
  • LiveStore :Electric等のリアクティブレイヤー

Jazzの詳細解説

  • Jazz は「ローカル状態の更新と同じくらい簡単にlocal-firstアプリが作れる」と謳う
  • Collaborative Values(CoValues) :リアルタイム協調用データ構造
  • JazzとZodでスキーマ定義
    • 例:
      import { co, z } from "jazz-tools";
      const ListOfComments = co.list(Comment);
      export const Post = co.map({
        title: z.string(),
        content: z.string(),
        comments: ListOfComments,
      });
      
  • 取得・更新はフックで
    • 例:
      const post = useCoState(Post, postId)
      const setTitle = (title: string) => {
        post.title = title // 自動同期
      }
      
  • APIルートやDTO不要、オブジェクト操作だけで同期

Jazzの内部技術

  • 自動一意性 :全データにユニークID付与
  • イベントソーシング :全変更をイベントとして保存、差分のみ同期
  • エンドツーエンド暗号化 :クライアントで暗号化、サーバーは暗号化データのみ保持
  • グループによる権限管理
    • 例:
      const group = Group.create()
      group.addMember(alice, 'admin')
      group.addMember(bob, 'writer')
      Post.create({ title: "a new post"}, { owner: group });
      

Jazzのトレードオフ

  • サーバーはデータ内容を読めない (暗号化のため)
    • プライバシー強化だが、モデレーションや不正防止が難しい
  • イベントソーシングによる「削除不可」問題
    • 全変更履歴が残り、GDPR対応等が課題
  • ストレージ消費の増加
    • 小規模なら問題なし、大規模SaaSではコスト増
  • Passkeys認証のローカル開発時の煩雑さ
    • HTTPS必須、証明書管理や外部Auth連携に工夫が必要
  • 今後Better Auth統合で改善予定

それでもJazzは魅力的

  • 開発体験の生産性・楽しさ
  • 早期段階だが今後の成長に期待

Electric SQLとZeroの比較

  • Electric SQL/Zero はPostgres等既存DBを活用する漸進的アプローチ
    • 例:通常通りPostgresテーブル作成
    • Electric はリアクティブクエリで部分同期
      • 例:
        import { useShape } from '@electric-sql/react'
        function Component() {
          const { data } = useShape({ url: `http://localhost:3000/v1/shape`, params: { table: `posts` } })
          return (<pre>{ JSON.stringify(data, null, 2) }</pre>)
        }
        
  • LiveStore追加 でJazz並みの生産性も視野
  • TanStack DB も候補
  • Zero はElectricに近く、直接ミューテーションもサポート

Local-Firstが適する場面・難しい場面

  • 最適なケース
    • クリエイティブツール(デザイン・執筆・音楽)
    • 協調作業アプリやその一部
    • オフライン対応必須なモバイルアプリ
    • 開発者向けツール
    • 個人向け生産性アプリ
  • 課題が多いケース
    • サーバーサイド業務ロジックが重い場合
    • 厳格な監査要件
    • 大規模分析処理
    • 既存システムとの深い統合
    • サーバー側で頻繁にリクエスト拒否が発生する場合(楽観的更新が困難)

今後の展望・まとめ

  • local-first はアプリ開発の根本的転換
  • ユーザー体験の劇的向上 (Linearが証明)
  • アーキテクチャのトレードオフを見極める重要性
  • Jazz で個人アプリ開発中、抽象化の「綻び」も注視
  • エコシステムはまだ若いが、今後の成熟に期待
  • データをローカルに保つことで高速体験 が得られる本質は変わらない
  • 新規開発で制約に対応可能なら、 local-firstを試す価値大
  • 最悪でも新しい設計思想を学べる、最良ならユーザーに「異次元の速さ」を提供可能
  • 体験談やフィードバック歓迎、意見や経験はぜひ共有してほしい

Hackerたちの意見

ElectricSQLとTanStack DBは素晴らしいけど、なんでウェブでローカルファーストにそんなにこだわるのか不思議だな。他のプラットフォームに比べて、モバイルがローカルファーストの主要なユースケースだと思うんだよね。だって、インターネットが常にあるわけじゃないし。ウェブブラウザを使ってる時は、普通はネットに繋がってるしね。それに、前者の技術は理論上はローカルファーストだけど、コンフリクト解決がないとすぐに崩れちゃうことが多い。これは、ローカルファーストが必要なモバイルアプリを作った経験から来てるんだけど、そのためにCRDTを使うことになったんだ。

ウェブ技術でローカルファーストを構築するのは、ネイティブアプリのツールキットで構築するよりも無限に難しい。ネイティブアプリはデフォルトでオフラインでインストールされて使えるけど、ウェブサイトはAppManifestやServiceWorkerを使うために変な手順が必要で、オフラインで使えるようにするためのパーツみたいなものなんだ。ネイティブアプリはファイルを作って、30年前のCコードでファイルを読み書きできるけど、そのファイルはストレージに残る。ウェブだとIndexedDB(本当に面倒)、localStorage(真剣なスケールには全然足りないし、同時書き込みが落ちる)、またはOriginPrivateFileSystemをいじらなきゃいけない。ユーザーは定期的に訪問する必要がある(少なくとも月に一回?)じゃないと、Appleがローカルのブラウザ状態を消しちゃう。JavaScriptを使ったり、WASM用にEmscriptenでビルドするまでCコードを叩いたりしても、非同期のウェブAPIを待つために同期Cを扱うのは難しい。Appleは2015年からCoreData + CloudKitを提供していて、バックエンドなしで同期するローカルアプリのための完成したファーストパーティのソリューションだよ。Googleのファンじゃないけど、Firebaseがその代わりかな?よくわからない。

この場合、製品を全く使えないというわけではなく、ものすごく速くて反応が良い製品を使う喜びが大事なんだよね。だから、ローカルファーストで使いたくなる。

ウェブアプリはウェブブラウザで動くから、ローカルファーストのプラットフォームとは真逆なんだよね。ローカルファーストは実際にはどんなネイティブアプリでもデフォルトだよ。

初心者の方に説明すると、Linearは信じられないほど速いプロジェクト管理ツールです。問題をクリックすると、すぐに開きます。ステータスを更新して、別のブラウザで見ると、ほぼリアルタイムで更新されます。読み込み状態もページのリフレッシュもなしで、ただ瞬時にインタラクションが行われます。低遅延のクリックアクションが「信じられないほど速い」と評価されるなんて、ウェブは本当にひどくなったな。これは馬鹿げてる。

データセンターへのウェブリクエストは、非常に速いバックエンドサーバーがあっても、8ms(120Hzディスプレイ)や16ms(60Hzディスプレイ)を超えるのは難しいんだ。ナビゲーションの次のフレームを描画するための予算がそれだからね。データはデバイスにローカルで、理想的にはメモリに既にある必要がある。そうしないと8msのナビゲーションは無理だよ。

ありきたりな意見だね。著者は「ウェブがどうなったか」とは関係ない行動について言ってるんだ。特に、複数のタブでIndexedDBのような共有リソースを使うことで可能になる行動についてだよ。ネットワーク越しに似たようなことをするには、次のフレームの締切までの時間がある。つまり、8-16msのRTT。出て戻るのに4ms、処理に0msの予算しかない。頑張って!

これを読んで驚いたのは、Linearが私にはちょっともっさりしていると感じていたから。確認のためにプロファイルを取ってみたよ。M4 MacBook Proで、「Inbox」と「My issues」タブの間をクリックするのに約100msから150msかかる。問題を開いたり、問題から問題リストに戻ったりするのに約80msかかる。それぞれのナビゲーションには、メインスレッドを50msブロックする関数呼び出しが含まれている - おそらくReactのレンダリング関数かな?Linearはネットワークアクティビティを最適化するために非常に良い仕事をしているけど、パフォーマンスのボトルネックが別のところに移ってしまったみたい。彼らはすでに現状よりも印象的な改善をしている(ほとんどの動的コンテンツで約500msから1500ms)、だからその最後のギャップを埋めて、単一フレームの応答性を達成してほしいな。

「信じられないほど速い」って表現には私もひるんだし、ほとんどのユーザーには伝わらない技術的な視点を指しているんだろうなって思った。私はフロントエンド開発者じゃないし、Linearを使ってるけど、速度には気づかなかった。他のウェブアプリと同じくらいの感じで動いてると思う。すごい最適化がされてるのは疑わないけど、使っているほとんどの人にはそれが伝わってないと思う。(最適化がかっこよくないとは言ってないよ)

Linearは実際に私にとってすごく遅くて、使うのが憂鬱になる。チケットが500msで読み込まれるかどうかなんてどうでもいいから、チケットをくれよ、10秒間も偽の点滅カーソルや、(遅く)再同期しようとしている間のランダムなリフレッシュなんていらない。Linearについて読むことは、私には過剰設計の叫び声のように聞こえる。ただのチケットトラッカーなのに、使うのがかなり苦痛だ。これってこの分野に特有の問題みたいで、例えばAsanaも一時期独自の言語を作ろうとしてたし。

ウェブアプリは大きくて重くなりすぎた。企業はすべてをコントロールしたがる。簡単な例で言うと、シンプルなメモ取りアプリがあって、それがデバイス間で同期しなきゃいけないらしい。彼らはあなたが取ったすべてのメモをサーバーに保存するつもりで、本当に削除したメモを削除しているかどうかは誰にもわからない。彼らはまた、あなたがメモをどれくらい訪れたかも追跡するだろう。何らかの理由でアプリが位置情報を要求することがあっても驚かないよ。これを多くのユーザーと組み合わせると、小規模アプリでは考えられないような読み込み時間が発生する。ウェブアプリはスケールダウンすべきだけど、何でもそうだけど、もっともっと大きく、良く、速くなることを求めている。

2018年に、クライアントのためにJiraを使わなきゃいけなかったことがあった。すごく遅くて、プロジェクトマネージャーは計画会議中にすべてをExcelで設定してた。会議の後、彼女は手動でJiraに移してたんだ。彼女はほとんどの時間をこれに費やしてた。インターフェースの各クリックには数秒かかるから、フローに入るのは不可能だった。

そんなに簡単なら、他のビジネスツールのパフォーマンスの良い代替品で何百万も稼げるよう応援してるよ。楽勝だろうね。

ローカルファーストと同期エンジンが未来だよ。ローカルファーストのフレームワークの全体像をフィルタリングできるデータテーブルがここにあるよ: https://www.localfirst.fm/landscape 今のところお気に入りはTriplit.dev(TanStack DBとも組み合わせられる)。他に探索したいのはPowerSyncとNextGraph。最近のLocalFirst Confには素晴らしい動画があって、今はNextGraphのやつを見てるところ(https://www.youtube.com/watch?v=gaadDmZWIzE)。

CRDTをベースにしたオープンソースのタスク管理ソフトをローカルファーストアプローチで開発したよ。主な動機は、コラボレーション機能がなくても個人的なタスクを管理したいからで、Linearのようなツールは私のユースケースには複雑すぎるんだ。このアーキテクチャにはいくつかの利点があるよ:1. データがローカルに保存されるから、ソフトウェアの応答時間が非常に速い 2. フルデータベースのエクスポートとインポートが便利にサポートされる 3. サーバーサイドのロジックは軽量で、パフォーマンスオーバーヘッドと開発の複雑さが最小限に抑えられ、すべてのビジネスロジックがクライアントで実装される 4. ローカルロジックの操作だけで機能開発が簡単になる ただし、いくつかの制限もある:1. テキストデータの保存にしか適していない;画像や大きなファイルにはオブジェクトストレージサービスを推奨 2. 同期関連のコードは開発時に特に注意が必要で、バグが深刻な結果をもたらす可能性がある 3. エンドツーエンドの暗号化を用いたコラボレーション機能の実装は比較的複雑 技術アーキテクチャは以下のように設計されている:1. Loro CRDTオープンソースライブラリを基に構築し、ビジネスロジックの開発に集中できるようにした 2. データ処理の流れ:ユーザーの操作がCRDTモデルの更新をトリガーし、JSON状態をエクスポートしてUIを更新する。同時に、データがローカルデータベースに書き込まれ、サーバーと同期される。3. ローカルストレージ層は、3つの統一インターフェース(リスト、保存、読み込み)を通じて抽象化され、プラットフォームに適したストレージソリューションを使用:ブラウザ用のIndexedDB、Electronデスクトップ用のファイルシステム、iOSとAndroid用のCapacitor Filesystem。4. エンドツーエンドの暗号化とインクリメンタル同期を実装。同期前に、システムはサーバーとクライアントのバージョンに基づいて差分を計算し、AESを使ってデータを暗号化してからアップロードする。サーバーはそのコンテンツとバージョン間のインクリメンタルパッチを持つベースバージョンを維持する。蓄積されたパッチが一定のサイズに達すると、システムは暗号化されたフルデータベースを新しいベースバージョンとしてアップロードし、その後のパッチは軽量に保たれる。もしこのプロジェクトに興味があれば、https://github.com/hamsterbase/tasksを訪れてみてね。

org modeファイルで整理されたチームが欲しい!Gitリポジトリを通じて。

Jazzにはすごく感心してるよ。素晴らしいDX(ほとんど同期的な命令型コードを書く感じ)と、素晴らしいUX(すべてが瞬時に感じられて、オフラインでも作業できるなど)が実現できてる。私が抱えている主な問題は、配布と持続性に関することだね。記事にも書いてあるけど、データが増える一方で(ほとんどのクライアントがそれを見る必要がないなら、大した問題じゃないけど)、もう一つ重要だと思うのは、頻繁に変わる公開インデックスの良い解決策が欠けていることだね(理論的には公開可能なIDリストを持てるけど)。でも最近、アンセルムと話したら、これらの問題には解決策が進行中だって言ってたよ。全体的に、ローカルファーストの利点は、ほとんどのユースケースには重要でない多くのコストを伴うことが多い(例えば、もっと多くの状態が必要になるとか)。でも、Jazzが従来の中央サーバーソリューションと比べた主な弱点を克服できれば、FirebaseのFirestoreのようなものに対して、ほぼすべての面で非常に良い代替品になると思う。

ローカルファーストの小さなブラウザアプリを作ってるんだけど、静的ホスティングとどう組み合わせるか悩んでるんだ。これってできそうな気がするんだけど、今のところツールはサーバーが必要って考えにとらわれてる感じ。俺の使い方は、インターネット接続があるかどうかわからないライブイベントのスコアリングなんだ。普通は一人で使うけど、時々は中央集権的なインフラに頼らずに複数人でスコアリングできたらいいなと思ってる。

昔のMeteorとこの技術アーキテクチャってそんなに違うの?もっと深く理解してる人の意見が聞きたいな。

ローカルファーストは、状態をクライアントに移すことで即座にUXを向上させるけど、その分他のことがちょっと難しくなるね。

これ、すごくクリーンで分かりやすい表現だね。最近のローカルとその役割についての議論は素晴らしくて、本当に真剣だと思う。

Linearの同期インフラやプロダクトに関わりたいなら、今人を募集してるよ。日々のデベロッパーエクスペリエンスは最高だよ。