【アーキテクチャConference 2025】 Qiitaアーキテクチャの15年史:モノリスRailsから巨大化するサービスの技術負債をどう解消するか
2025年11月20日・11月21日に、ファインディ株式会社が主催するイベント「アーキテクチャConference 2025」が、ベルサール羽田空港にて開催されました。
20日に登壇したQiita株式会社の千葉 知也さんは、約15年にわたり開発を続けてきたRailsアプリケーションの複雑化という課題に直面していました。その課題のためにトライした「Service層」と「モジュラーモノリス」の導入の結果は、一勝一敗。勝負を分けたのは、導入手法の違いにあったと言います。本セッションでは、失敗例と成功例から見えてきた、技術負債の解消における適切なアプローチとその戦略について解説していただきました。
■プロフィール
千葉 知也
Qiita株式会社
プロダクト開発部共通基盤開発グループ シニアエンジニア
2013年よりアルバイト、後に新卒エンジニアとして Increments (現 Qiita株式会社) に入社。一度退職し、2022年より Qiita株式会社に再度入社。現在は Qiita, Qiita Team の開発から基盤の設計や刷新まで広く取り組んでいる。
100万記事を支えるQiitaアーキテクチャ
Qiitaでリードエンジニアをしている千葉 知也です。本日はよろしくお願いいたします。
はじめに、サービスについてご紹介します。Qiitaはエンジニアのための技術情報共有サービスです。エンジニアに関する知見を記事として投稿するだけでなく、質問したり、アドベントカレンダーを作成することもできます。「Qiita Team」という社内向けの情報共有サービスも提供しています。
QiitaとQiita Teamのアーキテクチャはこちらです。

QiitaとQiita Teamは、単一のモノリシックなRailsアプリケーションとして動作しています。もう少し詳しくお話しすると、フロントエンドはReact中心、インフラはAWS、MySQL、Redis、Elasticsearch、あとは推薦基盤もあります。比較的オーソドックスな構成だと思います。
サービス開始15周年、100万記事を支えるRailsの底力
Qiitaは、サービスを開始してから15周年を迎えました。最初はとても小さいサービスでしたが、今では100万人以上のユーザーが100万件以上の記事を投稿してくれるほどに成長しました。
そんなQiitaが当初から使用しているRailsの公式キャッチフレーズは「Ruby on Rails scales from HELLO WORLD to IPO」です。

つまり、Railsとは「HELLO WORLD」から IPOまで支えてくれるフレームワークだと。QiitaはIPOをしていませんが、15年にわたってサービスを継続し、かつここまで成長させられたのは、Railsのおかげだと考えています。
複雑化するサービスに対し取られた2つの改善策
とはいえ、15年も続けていると、当然ながらサービスは複雑化していきます。ユーザー数の増加、フィードバックへの対応、事業拡大、最近ではAI事業にも対応してきました。
そして、そうやって複雑化するサービスの改善にも取り組んできました。改善の取り組みとして、失敗したものもあれば、成功したものもあります。
今回は、私たちの失敗と成功の事例として、「Service層の導入」と「モジュラーモノリスの導入」をご紹介します。ちなみに、失敗したのはService層の導入で、モジュラーモノリスの導入には成功しました。
この勝敗を分けたのは、設計手法の良し悪しではなく、導入手法の違いにあると考えています。ここからは「どのような導入方法が成功に繋がるのか」というお話をします。

【失敗事例】なぜService層の導入は「迷い」を生んでしまったのか?
「Service層の失敗」についてお話しする前に、Service層についてご説明します。
Railsは、基本的に MVC(Model/View/Controller)の3つで設計されています。Controllerはユーザーからのリクエストを受け付け、Modelはユーザーや記事、質問、ストックなどのデータベース(以下、DB)とのやり取りを担い、ViewはHTMLを生成します。最近ではJSONを生成することもありますね。

これらの設計において、ControllerとModelの間に追加するのがService層です。

Service層は「DDD」や「PoEAA」などに登場する概念で、主に操作やビジネスロジックを担当する役割を担っていると言われています。
Rails文脈においては、サービスの成長に伴い、Fat Modelがつらくなった際の新たなレイヤーとして導入を検討するパターンがよくあります。

Fat Modelがつらくなるのは、アプリケーションが複雑化していく中で、あらゆる操作やロジックがModelに集約され、特定のユースケースと密結合してしまうからです。
例として、サンプルコードを持ってきました。極端なFat Modelのアンチパターンとして、バリデーションとコールバックの肥大化が挙げられます。

Railsのモデルは、1行で入力チェックを行ったり、保存後にメールを送信したりできる仕組みがありますが、仕様が増えるにつれて「このケースではメールを送りたいが、別のケースでは送らないでほしい」「この操作では本文が空でもいいけど、通常の投稿では許可しない」といった矛盾するパターンがModelに混在し始めます。
このように、どんどんModelが複雑化してメンテナンスが困難になっていくため、そのロジックをService層に切り出します。

Service層では、一般的にCallなどで一連の処理を実行します。Modelが抱えていた複雑なバリデーションや条件分岐をサービス側に移譲することで、Fat Modelを解消するというイメージです。
「ドメインモデル貧血症」とコード冗長化のジレンマ
Qiitaでは、将来的にFat Modelになるのを防ぐため、大型の新機能開発でService層を導入しました。ControllerからModelを直接使わずサービス経由にする形にしたのですが、先ほどもお話しした通り、これはうまくいきませんでした。開発で迷いが生じてしまい、導入を中止したからです。
先ほどService層でできることをご紹介しましたが、そもそもService層の導入には、いくつか難しい部分があります。その1つが、ロジックをServiceとModelのどちらに書くのかという境界線を決めることです。Service層に処理を寄せすぎると「ドメインモデル貧血症」に陥りやすく、データの整合性が保ちにくくなったり、似たような処理をあちこちに書かないといけなくなったりします。
また、Serviceというレイヤーが増えるため、コード量も増加します。本来ならControllerからModelを呼び出すだけのシンプルな実装が、Serviceを経由することで、倍以上の行数に増えてしまい、開発がつらくなってしまいます。
失敗の原因は、導入タイミングのミスマッチ
私たちの失敗の原因は「ちょうどいいService層」をつくるための共通認識が、チームで揃っていなかったことにあると考えています。
具体的には、2つの反省点があります。1つ目は、新機能の開発タイミングで、社内に知見がないままService層の導入を始めてしまったことです。2つ目は、新機能でまだFat Modelが起きていない状態だったこと。解決すべき課題が目の前にない状態で導入したため、どこまでやるべきか基準がつくりにくく、設計に迷う時間ばかりが増えてしまいました。
いきなり全ての操作に導入しようとしたのも良くなかったと思います。ノウハウの蓄積やプロトタイプによる検証が不十分なまま走り始めてしまったため、メンバーそれぞれが正解を模索し、足並みが揃わなくなってしまいました。
【成功事例】イベント実装を分割する「モジュラーモノリス」の戦略的導入
次は、成功事例として、モジュラーモノリスの導入に関するお話をします。

モジュラーモノリスとは、Railsアプリを小さく分割する手法です。図の左側が一般的なRailsアプリで、右側が「アドベントカレンダー」と「Qiita Team」として分割しているものです。
フォーマルに言うと『モノリスからマイクロサービスへ』という書籍でも取り上げられているモノリスの派生形であり、モノリスという巨大な塊をモジュールという単位に分割していくものです。
よく比較されるマイクロサービスとの違いについても触れておきます。

マイクロサービスは、分割した単位ごとに独立したサービスとDBを持ち、デプロイも個別に行います。しかし、モジュラーモノリスはあくまで「1つのアプリケーション」「1つのサーバー」として動作させるのが特徴です。

ちなみに、Railsでモジュール化を実現するには、packs-railsとpackwerkを活用して別ディレクトリに移動したり、モジュール間のアクセスを制限したり、依存関係を制限したりするという方法があります。
モジュラーモノリスの導入目的と成功要因
Qiitaでモジュラーモノリスを導入する背景には「イベント関連の実装をモジュール化して本体から分離する」といった目的がありました。
導入にあたっては、いくつかの試行ステップを経て、まずは小規模な開発範囲から運用を開始しました。その結果、最終的にはうまく運用できるようになりました。
なぜ、モジュラーモノリスの導入がうまくいったのかというと、Service層での失敗を再現しないように意識していたからです。導入をする前にゴールを定め、試行リファクタリングを行い、「迷うポイント」を少人数のうちに発見できるよう動きました。
「捨てる前提」のリファクタリングでハマりどころを予見する
Qiitaでモジュラーモノリスを導入すると聞くと「ユーザー」「いいね」「ストック」「記事」「コメント」「タグ」といった単位で分割するイメージを持つ人が多いのではないでしょうか。
しかし、実際は下図のように分割しました。

Qiitaとしてイメージする機能はモノリスのまま保ち、アドベントカレンダーやQiita Tech Festa、カンファレンス、年末年始ページなどのイベント部分を分割しました。
そもそもQiitaはアドベントカレンダー以外に、Qiita Tech Festaのようなアウトプットイベント、カンファレンスなど、様々なイベントを開催しています。それらは全て、QiitaのRails上のモノリスで展開していました。
こういったイベントが増えてくると、開発者としてはどんどんつらくなっていきます。イベント用のコードとQiitaのコアコードが混ざってしまうからです。また、イベントなどは短期的に集中して開発を進めたいのですが、実装があちこちに散らばっていて把握が難しいという問題もありました。
こうした課題を踏まえて、モジュラーモノリスを導入する前に「試行リファクタリング」を行いました。試行リファクタリングとは『レガシーコード改善ガイド』という書籍に書かれている手法であり「捨てる前提でリファクタリングを行う」ものです。マスターブランチに反映させることなくリファクタリングを試行することで、元のコードの構造や振る舞いを理解するために行われます。
この手法を活用してイベントの分割作業を一通り試した結果、分割が容易そうなところ、結合度が高く後々の課題となりそうなところ、ハマりどころを把握できました。
その上で、イベント関連の機能は分割できそうだと判断しました。一方で、Qiitaのコア機能は結合度が高い部分も多く、いきなり全てを分割してしまうと、開発メンバーに迷いが生じてしまう可能性がありました。そのため、まずはイベント実装の分割にフォーカスすることにしました。
少人数での試行が「他者が迷わない仕組み」を生む
こうした検証プロセスや試行錯誤の結果は、社内のQiita Teamにて共有しています。具体的なプロポーザルを投げ、メンバー間でディスカッションを重ねました。
実際に導入する際には、大人数のチーム開発にいきなり展開するのではなく、2人ほどの小さなチームで試してノウハウを蓄積していきました。導入者である私自身が迷う部分と、初めてその仕組みに触れる人が悩むポイントは異なります。私が理解できていても、他の人が迷うであろう要素は徹底的に排除することを意識して、検証を繰り返しました。
そうして得られた知見をドキュメント化し、少しずつ規模を大きくしていくアプローチを取ったことで、最終的には混乱なくイベント機能の分割を完了させることができました。結果として、コードの見通しも良くなっています。
余談ですが、現在は別の部分の分離を試みています。

例えば、社内共通ロジックをライブラリ化したり、管理画面のように独自性が高いページから分割したりといった取り組みを進めています。この場合も、いきなりモノリス全体を解体するのではなく、まずは独立性の高い部分から少しずつ切り離し、外堀から埋めていくといった形で段階的に進めています。
Railsの強みを活かした上で、ゴール設定とノウハウの段階的蓄積が重要
ここまでご紹介してきた失敗事例と成功事例の違いは、手法自体の良し悪しではありません。Service層もモジュラーモノリスも、適用範囲をちゃんと絞れば、有用な手法です。ただ、一気に適用すると、考えることが増えてつらくなります。

Qiitaの場合は何が勝敗を分けたのかというと、ゴールを明確にしていたことと、ノウハウをためながら段階的に進めた結果「迷うポイント」を少人数のうちに減らせたことにあると思います。
Railsのレールを降りるなら、社内に「新しいレール」が必要
少し話は変わりますが、新しい試みを導入する際に生じる「迷い」から逆算して考えると、Railsは良くできたフレームワークだと思います。Railsの素早い開発を可能にしているのは、単にライブラリが強力だからというだけではなく、ルールの存在が大きいのではないでしょうか。
MVCや設定より規約、デフォルト設定といったルールが整備されていることで、エンジニアは「レールに乗る」ことができます。つまり、余計なことを考える必要がなく、ロジックの開発に集中できると。それこそが、Railsの強みなのではないでしょうか。
Railsのレールから外れて別のアーキテクチャを導入する際にも、大切にするべきなのはRailsが持つ「良さ」から学ぶことだと考えています。ルールや「どこに何を書くか」が明確に決まっているというRailsの強みは、そのレールを外れる時にも同じことが言えるからです。
原則として、Railsの標準機能で解決できる部分はレールに乗り続け、外れる範囲を最小限に留める。レールから外れる時は「代わりのレール」を敷かなくてはいけません。開発者が実装で悩んだり、考え方で迷ったりしないよう、社内に共通認識を積み上げていくプロセスが必要です。
また、まずは少人数で試してドキュメントを残し、導入を推進するメンバーが試行リファクタリングでしっかり試すことが重要だと思います。
コミュニティの力を借りて、エンジニアの知見を繋いでいく
最後に、コミュニティの力を借りてうまく外れるというのも大切だと思います。
どんなフレームワークでも先人がいますし、先行事例が共有されています。そういった情報を参照したり人に聞いたりしながら、自分たちなりの取り組み方を考えていくことが大切なのではないでしょうか。実際、私たちの改善でも、コミュニティに助けられました。Qiitaもそうした「次の誰かを助ける場」であり続けられるように頑張りたいと考えています。
最後に宣伝させてください。Qiitaでは、一緒に盛り上げてくださるエンジニアを募集しています。カジュアル面談も対応しておりますので、ぜひお声がけください。本日は、ご清聴ありがとうございました。

アーカイブ動画・発表資料
イベント本編は、アーカイブ動画を公開しています。また、当日の発表資料も掲載しています。あわせてご覧ください。
▼動画・資料はこちら
アーキテクチャConference 2025
※動画の視聴にはFindyへのログインが必要です。
