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

リニアはなぜこんなに速いのか?技術的分析

概要

Linear の高速なUI体験は、 ローカルデータベース非同期同期エンジン など、基礎から積み上げた設計と細かな最適化の積み重ねによるもの。 IndexedDB 活用によるネットワーク依存の排除、 バンドラ刷新 による初回ロード高速化、 サービスワーカー によるキャッシュ戦略が重要。 UI応答性 を最優先し、ネットワーク遅延をユーザーに感じさせない工夫が徹底。 クライアントサイドレンダリング のシンプルな構成を活かし、パフォーマンスと開発体験を両立。 この記事では、Linearの主要な高速化手法と、その設計思想を解説。

Linearの高速UIを支える設計思想

  • Linear は、従来のCRUD型Webアプリと異なり、 UIが直接IndexedDBを参照 するローカルファースト設計

  • ユーザー操作 はまずローカルで即時反映し、その後バックグラウンドでサーバーと同期

  • ネットワーク遅延 によるスピナーやローディング画面を極力排除し、 体感速度 を最大化

  • MobX のようなオブザーバブルストアでインメモリデータを管理し、UIを即座に再描画

  • WebSocket 経由で他クライアントともリアルタイム同期を実現

    • 例:
      • 従来型:APIリクエスト→レスポンス待ち→UI更新→スピナー表示
      • Linear型:ローカル即時更新→非同期でsave()→UIは待たない
  • オプティミスティックUI の思想を徹底し、失敗時のみロールバック

  • ネットワークをボトルネック と捉え、可能な限りリクエスト自体を隠蔽

Linearのスタックとシンプルな構成

  • フロントエンド

    • React + react-dom(UIランタイム)
    • MobX(オブザーバブルグラフ)
    • TypeScript(フルスタック単一言語)
    • Rolldown-Vite(バンドラ、過去はRollupやParcel)
    • ProseMirror + y-prosemirror(リッチテキスト、Yjs CRDTでライブコラボ)
    • Radix UI primitives、Emotion + StyleX、Comlink、idb、graphql-request、Sentry、Inter Variableフォント
  • バックエンド

    • Node.js + TypeScript
    • PostgreSQL(Cloud SQLで300分割)
    • Memorystore Redis(イベントバス・キャッシュ)
    • turbopuffer(類似課題検出用ベクタDB)
    • Kubernetes(GCP上)
    • Cloudflare Workers(エッジプロキシ)
  • その他クライアント

    • Desktop: Electron
    • Mobile: Swift(iOS)、Kotlin(Android)
  • マーケティングサイト

    • Next.js(静的)、styled-components、インラインSVG
  • CSR(クライアントサイドレンダリング) を基本とし、サーバー/クライアントの切り分けの複雑さを排除

  • レガシーブラウザ非対応 による大胆な最適化とシンプルなアーキテクチャ

初回ロード高速化のための工夫

  • バンドラ遍歴 (Parcel→Rollup→Vite→Rolldown)で、 出荷JS/CSSの最小化 を追求
    • モダンブラウザ限定・デッドコード削除・徹底的なコード分割
    • 50%のコード削減、30%の圧縮後サイズ減、初回ロード最大59%短縮、メモリ消費70〜80%減少
  • コード分割 により21MBのJSを数百のチャンクに分割、必要時のみロード
  • modulepreload を活用し、全チャンクを 並列プリロード
    • <script type=module crossorigin ...>と<link rel=modulepreload ...>で依存チャンクを先読み
    • ネットワークのウォーターフォール遅延を解消 し、初回アクセス時に全体をキャッシュ

サービスワーカーによるキャッシュ・オフライン対応

  • サービスワーカー が未訪問ルートやアセット(約1200ファイル)をバックグラウンドでプリキャッシュ
  • 2回目以降のナビゲーション はHTTPキャッシュすら経由せず、即座にサービスワーカーから応答
  • オフライン対応 も実現し、常に高速なUXを維持

まとめ:Linearの高速化から学べること

  • UI応答性 を最優先し、ネットワーク遅延をユーザーに見せない設計

  • ローカルデータベース非同期同期 の組み合わせによる即時性

  • 徹底したコード分割・プリロード・キャッシュ戦略 による初回ロード高速化

  • シンプルなクライアントサイド設計 による開発体験とパフォーマンスの両立

  • オプティミスティックUI最新技術活用 で、ネイティブアプリのような操作感を実現

    • ほとんどのWebアプリでも、 Tanstack QuerySWR などのライブラリで オプティミスティック更新 を組み合わせるだけでも、体感速度は大きく向上可能

Linearの設計思想と手法は、現代Webアプリにおけるパフォーマンス・UX改善の最良のヒント集

Hackerたちの意見

このブログ記事の要点は、クライアント側でミューテーションを作って、うまくいったと仮定して、バックグラウンドで保存するってことだね。

同意するなぁ。人それぞれだけど、自動でUIが更新されるのはあんまり価値を感じないな。自分が見ているビューは、実際のサーバーの状態のスナップショットであってほしい。画面でリクエストが通ったのを見ても、それが実際にサーバーに送信されたかどうかを考えさせられるような混ざった状態は嫌だな。

確かに。これ、ほんと嫌だわ。会議中に何かを更新して結果が見えるけど、他のチームは見えない場合を考えてみて。数百ミリ秒の遅延は関係ないけど、更新が通らなかったらどうなるの?実際にそういうことは起こるしね。

ネイティブアプリだと永続ストレージにアクセスできるからあまり問題にならないけど、ブラウザだと永続性が保証されてないからね。

Linearにはうまくいくけど、タブが開いている状態で、タブが閉じられた場合は、タブを再度開いたときに復旧して、競合解決をすることになる。次のような場合にはうまくいかない:1. ユーザーがボタンをクリックしてタブを閉じて、トランザクションが完了したと思っている場合。トランザクションが完了することが重要。2. 将来的にクライアントが起きたときに競合解決が難しい、または不可能な場合。

これは「最終的な整合性」って呼ばれるやつだと思う。

いわゆるRelayが初めからやってることだね、ハハ。でも、実際にフルに使ってる人が少ないのが悲しい。私のチームは、私が働いてるところでは数少ない一つだよ。重い宣言的ミューテーションディレクティブに楽観的レスポンス(そして楽観的アップデータも、うちのAPIがRelayにあまり対応してないから、イライラするけど)。

こういうローカルファーストの同期ウェブアプリはすごく面白いし、実際に役立つと思うけど、前提がちょっと間違ってる気がする。「Linearでの問題更新は数ミリ秒でできる。従来のCRUDアプリだと同じことに約300ミリ秒かかる。」とか「クライアントとサーバー間で送信されるデータは数百ミリ秒かかる。」って言ってるけど、HTTPクライアントとサーバー間の大きなRTTの問題は光の速さが原因だから解決できないよね。でも、ユーザーの近くにバックエンドを置いて、速くすることはできる。例えば、ほとんどのユーザーに対して約10ミリ秒のRTTでウェブアプリのバックエンドを運用して、バックエンドが約10ミリ秒でレスポンスを返すのは全然可能だよ。つまり、同じことをするのに300ミリ秒じゃなくて、30ミリ秒で済む従来のCRUDアプリを作ることは絶対できる。

クライアントに「仲介バックエンド」を置いて、ローカルストレージに優れたミューテーションを書き込んで、バックグラウンドワーカーがそれをバックエンドに送信するようにすればいい。必要に応じて再試行もね。最悪の場合、バックグラウンドワーカーが失敗した更新についてのメッセージを出して、UIスレッドがそれを受け取って表示することになる。でも、うまくいく流れは超速いままだよ。

ありがとう。300msが速いって言ってる人たちを見て、ちょっとおかしくなりそうだった。30msがずっと目標TTFBだったのに。もしかしたら、Linearはバックエンドで正当な理由で時間がかかるのかもしれないし、フロントエンドからの助けが必要かもしれないけど、それは一般化できないし、JSの一つ一つにはコストがあるからね。

これ、全てのエッジバックエンド間で共有されるデータベースが必要なところでできる?

昨年、ある人がLinearの同期エンジンをリバースエンジニアリングして、クールな説明をつけてGitHubに公開したよ。 https://github.com/wzhudev/reverse-linear-sync-engine/blob/m...

Hacker Newsで議論の続きを見る