10年モノのサービスをアーキテクチャから再設計─はてなブックマークがScalaとDDDを使う理由

10年以上運用されているサービスには、さまざまな技術的な負債が発生しています。今後の継続的な改善のため、いったん新規開発を止めて4年かけて全面的なリニューアルを実施した「はてなブックマーク」の開発者に、プロジェクトの課題や解決する手法などを聞きました。

10年モノのサービスをアーキテクチャから再設計─はてなブックマークがScalaとDDDを使う理由

インターネットの自由な情報流通を促進した「Web 2.0」とほぼ同時期、2005年に生まれた「はてなブックマーク」は、コメントを介してユーザーどうしのコミュニケーションを支援する、日本最大のソーシャルブックマークサービスとして提供され続けています。しかし、動きの速いWebの世界で、10年以上運用されているサービスを改善し、最適化し続けていくのは困難なことです。

はてなブックマークの開発チームでは、10周年を迎えた2015年、次の10年を見据えて完全にリライトし直すことを決定し、約4年をかけてプロジェクトを完遂させました。部分的なリファクタリングではなく全面的に書き直した狙いや、リニューアルプロジェクトで直面する課題や解決する手法について、アプリケーションエンジニアの伊奈林太郎さんと谷口力斗さんに聞きました。

1

伊奈 林太郎(いな・りんたろう) 2oarat 3tarao(写真右)
京都大学大学院情報学研究科修了。日本学術振興会特別研究員を経て、2013年株式会社はてな入社。2015年よりテックリードとして「はてなブックマーク」リニューアルプロジェクトを主導する。ブログ:貳佰伍拾陸夜日記
谷口 力斗(たにぐち・りきと) 4tanishiking 5tanishiking(写真左)
京都大学工学部卒。学生時代からアルバイトとして、また2017年の新卒入社後も一貫して「はてなブックマーク」リニューアルプロジェクトに携わる。現在はサービスプラットフォーム部に所属。ブログ:たにしきんぐダム

改善1つに数カ月かかるなら全てを書き換えられないか

──リニューアルを決定した時点で、サービスの開発はどういう状況だったのでしょうか?

伊奈 はてなブックマークのソースコードは、2008年に一度大きく書き変えられていますが、それ以降はモノリシックなコードを少しずつ拡張しながら使い続けていました。2005年に初めてローンチした際に書かれたコードも、管理画面の一部に残っていましたね。

そのため、ここ数年の大きな課題は、ちょっと改善のために手を加えるだけでも、けっこうな開発工数がかかってしまうことでした。パフォーマンスを改善するのに根本的なところを直さなくてはいけなかったり、何か新しい機能を追加するときにも、古いコードに手を付けないで実現できることは限られていたり……。

谷口 既存の機能に関係するところでコードを改変するとリグレッションが起きる恐れもあったため、できるだけ既存のコードに触らず、新たにコードを書こうという空気が強かったですね。

伊奈 その結果、似たような処理を行うメソッドを別に生やして……となるので、さらにコードが肥大化することになっていました。

谷口 新しくプロジェクトに加わった人が、複数の似たようなメソッドのうち「どっちが本物?」と悩むこともありましたね。はてなブログなど社内の別のサービスからも、APIなどではてなブックマークの機能を利用したい場合などに「もうちょっと何とかならないか」という声が上がったりもしていました。

2000年代にトレンドだった開発手法の負債

伊奈 開発サイドとしても、常にサービスをよりよいものにしていこうという思いがあります。ただ、10年以上運用してきたサービスに影響を与えずに実現しようとすると、どうしても時間がかかってしまうのも事実でした。

──どういったところに時間がかかる要因があったのでしょう?

谷口 当時のソースはPerlで書かれていましたが、コードベースが約40万行と非常に大きい上に、社内で独自に作成したWebフレームワークやO/Rマッパーを利用していたこともあって、コードの構造も独特でした。新たに「こんな機能を作ろう」となっても、関連するコードを探すだけで時間がかかっていました。

伊奈 独自フレームワークが悪いわけではなくて、2000年代半ばのトレンドとしては、それが便利で高速に開発できる方法でした。けれど10年~15年とたって、ソフトウェアの開発手法も変わっていく中で、それを継承するのにちょっと失敗してしまったのかなと反省しています。

過去の開発意図を探る考古学的手法

谷口 ただ、はてな社内には日記文化が根強くあるので、「どういう経緯でこうしたのか」「どういう理由でこうなっているのか」といったことは、みんな社内Wikiなどに書いてあるんですね。なので、Gitのコミットログを見てだいたいの時期を特定して、既に退職してるエンジニアの当時の社内ブログを読んだりしていました。

伊奈 そういった作業を僕らは「考古学」と呼んでますが、サービスの立ち上げ当初にはWebエンジニア界隈を巻き込んだ技術的な議論がはてなダイアリーなどでも繰り広げられていたので、それも追いかけました。

何かを作り直すときには、そうやって過去の経緯を調べてまとめなおし、当該の日記やコメントを参照しながら「こういう経緯なんだから、現代ではもう忘れていい事柄なんじゃないか」と議論することもありましたね。

──コードの意図を探るだけでも手間がかかっていたんですね。ただ、完全なリニューアルではなく、部分的なリファクタリングを積み重ねて改善していくこともできたのではと思いますが?

伊奈 例えば、はてなブックマークのデータを利用するアプリを改善しようとして「サーバサイドにこんなAPIが必要だ」となり、あわせて大規模なリファクタリングをしたこともありました。ただ、1つ機能を作るたびにそれと同じことが起きていたんです。

こうした悩みは以前から認識しており、過去にも何度かWebサーバやデータベースへの接続周りで大規模リファクタリングに取り組んだこともあったのですが、残念ながら部分的な改修ではうまくいきませんでした。

データセンター移行も見据えて刷新しよう

──とはいえ、実際に動いているサービスを全て書き直す判断は難しかったのではないですか?

伊奈 当時の開発ディレクターに「パフォーマンスやシステム安定性のための改善について、既存のイシューを個別に全て対応したらどのくらいの期間がかかるか?」と相談されて、率直に「どうしても3年はかかるし、その間は新機能の開発もできなくなる」と答えたことがありました。ちょうどデータセンター移設のプロジェクトが動いていたこともあって「いずれどこかでやらなければならない話ならば、ここで刷新しよう」という判断が下りました。

──少しずつ修正しても同じ開発期間とコストがかかるのなら、全て置き換えようということですね。

6 (2)

ドメインモデル設計とScalaとマイクロサービス化

──プロジェクトを開始した当時のことからお聞きしたいのですが。

伊奈 プロジェクトの開始は2015年です。全面移行を私ともう1人で進めることになり、後に谷口など2人が加わって、最終的にチームのエンジニアは4人になりました。

谷口 僕がアルバイトとして入社したのは2016年6月ですが、それ以降ずっとプロジェクトに関わってきました。

──先ほど、サービスのローンチ当時と比べて開発のトレンドも変わってきているという話がありましたが、全体を書き直すために採用した開発手法などを教えてください。

伊奈 まず、マイクロサービスです。はてなブックマークには「Webページを表示する部分」「裏側で人気エントリの計算やコメント集約を行う部分」「外部のサイトにHTMLを取りにいく部分」などいくつかのシステムがあり、それぞれ性質が異なります。毛色の違うシステムをマイクロサービスとして分けた方がいいなということは、旧システムの頃から思っていました。

コアロジックにはScalaを採用

──2015年のイベントで、開発言語にScalaを採用することを早々に発表していますね。

はてなブックマーク in ScalaScala関西 Summit 2015 発表資料)

伊奈 マイクロサービスとして分けるなら、システムごとに言語が違っていてもかまわないわけです。そこでコアロジックの部分は、リファクタリングしやすく、継続的にメンテナンスしやすく、ドメインモデルをきちんと表現できる方がいいと判断し、他のサービスで採用した実績もあることからScalaにしました。

Scalaで書いていると、壊れた書き変え方をするとコンパイルが通らなくなるので、すごくリファクタリングしやすいんですね。

一方、フロントエンドのWebページを表示する部分は、文言を変えたり、デザイナーさんが手を加えたりと、ちょっとした変更をたくさんすることになるため、ライトウェイトな言語の方がやりやすいだろうという理由で、Perlにしました。Perlなら社内に開発者もたくさんいますから。

ただ、はてな社内にはマルチリンガルなエンジニアが多いですし、1つの言語を知っていれば他の言語の習得も早いですから、特定の言語を優先するのではなく、適材適所で決めています。一部のマイクロサービスでは、Goも使っています。

きちんとしたドメインモデルによる設計と実装を継続したい

──いま「ドメインモデル」という言葉がありましたが、当初からDDD(Domain-Driven Design、ドメイン駆動設計)で進めたということでしょうか。

伊奈 はい。設計時には、ドメインモデルをきれいにするところに非常に気を使いました。古いシステムでは、例えば同じ「interest」という名前なのに、出てくる場所によってそれぞれ別の機能、違う意味を持つことがありました。

そういったことをなくしたかったので、この機能の「興味」とは何を意味するのか? それはコード上のどの名前と対応するのか? といった「語彙」をしっかりと定義しました。

また、長年開発を続けてきたコードベースがぐちゃぐちゃになってしまう理由の1つに、最初はきちんとレイヤを分けて「何のロジックはどこに書くのか」を決めていたのに、より処理しやすくしたいとか、いろいろな理由でそのルールが崩れていってしまうことがあります。

そこで移行作業の最初に、「ドメイン層とは何か」「ドメイン層とアプリケーション層(ユースケース層)とは何が違い、何を入れるべきなのか」「コントローラ層がファットにならないためにどうしたらいいか」といった事柄をかなり時間をかけて議論し、決めていきました。

新たに加わってきたメンバーにものその結果を共有し、一緒に議論しています。それを踏まえて、「こういう機能を実装する際は、ここにこういうコードを書きます」といった事柄をチュートリアル風にまとめた「開発の手引き」的なドキュメントをしっかり書いて、Gitで管理しています。

谷口 僕が最初にチームに加わった時点で既に、こういったルールやドキュメント類の整備が徹底されていたので、ドメイン知識の習得は容易でした。エンジニアが何か機能を追加しようと思ったときに、そのコードをアプリケーションレイヤに書くべきか、ドメインレイヤに書くべきかといった事柄を皆がちゃんと意識していますし、コードレビューのときも議論になったりします。

8

段階的なリリースとデータの移行という2つの大きな課題

エンジニアHubに会員登録すると
続きをお読みいただけます(無料)。
登録のメリット
  • すべての過去記事を読める
  • 過去のウェビナー動画を
    視聴できる
  • 企業やエージェントから
    スカウトが届く