技術選定から値段交渉まで Strapノート機能刷新に向けた試行錯誤記録

この記事は、Goodpatch Advent Calendar 2025 18日目の記事になります。


こんにちは。グッドパッチでStrapの開発を担当しています、エンジニアのちげです。すき焼きが美味しい季節になりましたね。

product.strap.app

 

皆さんは、プロジェクトのアイデアを練る際、どのようなツールを使っていますか? グッドパッチが提供しているStrapは、チームのコラボレーションを加速させるオンラインホワイトボードツールです。無限のキャンバスに図解や付箋を配置できるのが特徴ですが、実はその中に「ノート」という、文章を構造的にまとめられる機能が存在します。

先日までこのStrapのノート機能の改善案の策定・調査に取り組んでいました。

今回は、ノート機能をよりモダンで快適なものにするために、私たちが裏側でどのような調査・検証を行い、どのような壁にぶつかったのか。その奮闘の記録をテックブログとしてお届けします。

Strapのノート機能とは?

現在のStrapノートは、割と前に作られた機能で、以下のような基本的な装飾機能を備えています。

  • 見出し(H1, H2, H3)

  • 太字(Bold)

  • リンク

  • 画像挿入

また、現時点で共同編集ができない仕様(排他制御)になっています。誰かが編集している間は他の人が触れない「ロック制」になっており、リアルタイムに複数人で書き込む体験には至っていませんでした。

そこで、今回の調査の目標は、以下の3点について検討することです。

  1. よりリッチなスタイル表現(リスト、引用など)

  2. Markdown記法のショートカット対応#+スペースで即座に見出しになるなど)

  3. 複数人での同時共同編集

1. 既存実装のアーキテクチャと「diff変換」の苦悩

現状、ノート機能のフロントエンドには ProseMirror を採用しています。

ProseMirrorとは? リッチテキストエディタを構築するためのツールキットです。非常に自由度が高く、複雑なエディタを作れる反面、設定やカスタマイズには高度な知識と工数が必要になります。

prosemirror.net

バックエンドには Firestore を利用しているのですが、Strapのノートは設計が少しユニークです。「ノートの1行がFirestoreの1ドキュメントに対応する」という構造になっています。

理論上、この設計であれば「別の行」を編集している分には競合が発生しないため、楽観的更新による共同編集が可能なはずでした。実際、排他制御を外しても、ある程度動くように作られています。

今回の調査にあたって、まずは手始めに既存のProseMirrorを使って「リスト機能」の自作に挑戦してみました。複雑な仕様に頭を抱えながらもなんとか実装自体は完了したのですが、その開発過程を通じて、なぜこの機能が共同編集を実現できないまま開発が止まっていたのか、その根本的な理由が明確に浮き彫りになってきました。

一番の原因は開発優先度ですが、エンジニア視点で語るとすれば、データ安全性の担保変換ロジックの開発工数 でしょう。

データ安全性の担保

行単位でドキュメントを分けて楽観的更新を行う設計には、データ整合性の観点で大きな欠点がありました。

  1. 順序制御(Index)の競合 各行は「何番目の行か」というインデックス情報を持っています。複数人が同時に行を挿入した場合、同じインデックスを奪い合ったり、挿入後のインデックスの振り直しがクライアント間でズレたりすることで、行の入れ替わりや消失が発生しやすくなります。

  2. 「見ているもの」と「保存されるもの」の乖離 楽観的更新はユーザーに即座に反応を返しますが、Firestoreへの書き込み順序や他のユーザーの更新とのマージ結果が、必ずしもエディタ上の表示と一致する保証がありません。これを解決するには、クライアント側で極めて複雑な衝突解決ロジックを自前で実装する必要がありました。

このようなことが懸念されるため、この問題に対する具体的な対策を講じるまで、安全性を優先して排他制御をかけるという判断に至っていたのでしょう。

変換ロジックの開発工数

ProseMirrorで文章が更新されると、内部では Transaction という差分情報が生成されます。これをFirestore側に反映するために、自作の関数を使って「added, updated, deleted, recovered」の4つの操作に振り分けていました。

ところが、ProseMirrorが発行する操作は必ずしも「行単位」ではありません。文章全体を横断するような変更が走った際、それを「行ごとのCRUD操作」に無理やり変換する処理が非常に複雑になり、開発の大きなボトルネックとなっていました。

2. Tiptapという救世主(?)の登場

まず、フロントの開発コストを抑えつつ機能をリッチにするため、私たちは Tiptap の導入を検討しました。

Tiptapとは? ProseMirrorをベースに構築された、モダンなヘッドレスリッチテキストエディタです。

tiptap.dev

Tiptapを利用するための基盤ができてさえしまえば、プラグインをインストールして反映していくだけで次々とリッチ機能が実装できます。開発エージェントの力も借りることで、爆速でプロトタイプ開発が進みました。

しかし、フロントエンドをTiptapに変えたからといって、共同編集の課題が解決するわけではありません。むしろ、ここからが本当の戦いです。

3. 共同編集の鍵「CRDT」とインフラのジレンマ

Tiptapで複数人での同時編集をスムーズに行うためには、CRDT(Conflict-free Replicated Data Type) という仕組みが必要になります。

CRDTとは? 複数のデバイスで同時に行われた編集を、サーバーを介さずとも(あるいは簡単な仲介だけで)数学的に矛盾なく自動でマージできるデータ構造のことです。Googleドキュメントのようなリアルタイム共同編集体験を実現するための標準的な手法として広く採用されています。

yjs.dev

Tiptapは、このCRDTを実現するライブラリ Y.js との親和性が非常に高いです。しかし、ここでStrap特有のインフラ問題が浮上しました。Y.jsを本格的に運用するには、編集内容を仲介するバックエンドサーバーが必要になります。

StrapはFirestoreを活用したフルマネージドな構成で動いており、新たに Cloud Runなどでステートフルな中間サーバーを管理・維持することは、運用コストや保守性の観点から避けたい というのが本音でした。

4. 4つの選択肢

様々な選択肢を調査した結果、私たちは以下の4つのルートを天秤にかけることになりました。

選択肢

メリット

デメリット

1. 現状維持 (ProseMirror)

既存のロジックを継承できる。

開発工数が膨大。

2. Tiptap + 排他制御

フロント側がリッチ。

共同編集は諦める。

3. Tiptap + 自前CRDTサーバー

理想の体験。自由度高。

サーバー運用コスト増。

4. Tiptap Cloud

サーバー運用不要。

SaaS利用料が高額。

 

選択肢4のTiptap Cloudについて、Strapには「データの国内保存」という重要なセキュリティ要件があります。Tiptap Cloudの通常プランは海外サーバーですが、カスタム可能なEnterpriseプランなら国内サーバー要件を満たせる可能性があることがわかりました。

「価格がわからないなら、聞くしかない!」

ということで問い合わせた結果、なんとドイツにあるTiptap社のVPoP(プロダクトの最高責任者)とオンライン面談をすることになりました。

私は英語は決して得意ではありません。しかし、Strapの未来がかかっています。前日は「How much does it cost...?」と練習し、当日は脇汗をかきながらカメラの前に座りました。

画面に現れたのは、非常にフレンドリーながらも鋭い眼光のVPoPと、マーケティング担当者。営業をかけているのはあちらなのに、2対1で就活面接みたいな感じでした。拙い英語と身振り手振りを駆使し、Strapの現状、日本でのセキュリティ要件、そして私たちがやりたいことを必死に伝えました。なかなかシビれる値段交渉ですw。

結果:やはりEnterprise、お高かった……。

機能面では完璧でしたが、今のビジネスフェーズにおけるコストバランスを考え、Tiptap Cloudの導入は一旦見送るという苦渋の決断を下しました。

おわりに

チームで何度も議論を重ねた結果、現時点では「選択肢2:Tiptap化 + 排他制御の維持」で進める方針を固めました。

まずはユーザーの皆さんに、Markdown記法やリッチな装飾といった「書く楽しさや書き心地」をいち早く届けることが重要だと判断したからです。

ただし、これで共同編集を諦めたわけではありません。選択肢2は、将来的に自前でCRDTサーバーを構築することで「選択肢3」へ移行できる拡張性を持っています。スムーズに移行できる前提の設計にするということでひと段落つきました。

現在は一旦ノートの調査を終え、別のタスクに取り組んでいますが、そちらが落ち着いたらいよいよノート機能の本実装が始まるかと思います。

Strapのノート機能が、より滑らかに、より自由にチームの思考を支えられるようになるまで私たちの奮闘はまだまだ続きます。進捗があれば、またこのブログで報告させてくださーい!

 

Goodpatchではデザイン好きなエンジニアの仲間を募集しています。 少しでもご興味を持たれた方は、ぜひ一度カジュアルにお話ししましょう!