会員数10万人のレガシーなコミュニティサイトを一から全部作り直した舞台裏

 

f:id:bm-sms:20230804175253j:plain


 

はじめに

規模の大小を問わず、レガシー化したサイトには色々な課題が存在します。課題の根本的な改善のためにソースコードをゼロから書き直してリニューアル(以後、このことをフルリニューアルと呼称します)するということは、とても魅力的な一方でリスクもあります。フルリニューアルすなわちアンチパターンとされていることも多いですね。ここでは「中規模程度のコミュニティサイトをフルリニューアル、すなわち一から全部作り直す」という選択をした背景や、その進め方について書いていきます。

なお、書きやすさのために筆者一人で思考・実行したように書いていますが、実際には事業部所属のもう一人のエンジニアもしくは二人の考えや行動をミックスしたものとなっています。

 

TL;DR

PHP 5/Ethna & Smarty/オンプレ/オフショア開発7年ものを引き継ぎ/ツライ

PHP 7/Laravel & Vue.js, Elixir(GraphQL)/AWS/内製開発/モウツラクナイ 

どんなサイト?

今回の記事の舞台となるサイトは、エイチエ(https://eichie.jp/)という管理栄養士・栄養士に特化したコミュニティサイトです。一般の人には知られていませんが、ネットを使う世代の管理栄養士・栄養士の中で知らない人はまず居ません。会員数は既に10万人を超えており、ユーザ層も学生から70代までとバラエティに富んだサイトです。

機能としては一般的なコミュニティサイトですが、運営開始は2011年6月という、それなりに歴史のあるサイトです。リニューアル前にはベースとなるOSやソースコードのレガシー化も進んでいて、機能追加や変更を行うのがなかなか大変で、これぐらいの歴史のあるサイトにおける、あるあるな状況でした。

 

システム構成

エイチエがリリースされた2011年あたりにはクラウド環境もまだあまり使われておらず、サーバの置き場所と言えば自社のiDCあるいはオフィスのサーバ室というのが普通でした。何だったらエンジニアの足下に置いてあるサーバで動かしているなんて企業も、そう珍しくもない時代でした。さくらのVPSが運用開始したのが2010年9月というと、どれぐらい昔なのか実感できるのではないでしょうか。

開発言語はPHP5で、フレームワークはEthna(http://ethna.jp/)を使用していました。WebサーバやDBサーバは Apache と MySQL(+Senna) という典型的なLAMP環境です。エイチエに限らず当時は、だいたいこういう構成のサイトが多かったですね。

OSはエイチエがリリースされた時期には CentOS 5 しか存在しなかったはずですが、リニューアル直前には VMWare 上の CentOS 6 で稼働していました。VM化とあわせてインフラチームがどこかで移行したのかもしれません。まぁとにかく、2021年の視点から見るとさすがに古すぎる環境です。

 

当時の状況

中規模程度のコミュニティサイトで一般的な機能しかなかったにもかかわらず、ソースコードの構成がやたら複雑でした。初期〜中期の開発には携わっていなかったため、これまでどのように意思決定され、どう開発されてきたのかは良くわかりませんが、「SQLは当然のように sprintf() で作られていた」と書けば、だいたい伝わりますよね?

のちほどわかったのですが、社内の別のコミュニティサイトのソースコードを流用したものをベースとして作られたという出自を持っていました。そのためソースコードは言うまでも無くDBにも使っていないテーブルはもちろんありますし、0や1など謎の値が格納されるけど実はどこからも参照されていないカラムなど、ソースコードの可読性や保守性を妨げるお楽しみ要素が満載でした。

 

同じ事をやるロジックは都度コピペをするのが温かみのある実装と言わんばかりにあちこちに点在し、何ならサイトのサイドバーのテンプレートファイルが複数存在していました。バナー1つを貼るだけでも複数ファイルを更新する必要があり、あるページにはバナーがでているけど、あるページには無いという事故もよくありました。ページによってサイドバーに表示する内容が少し違っていたので、当時、テンプレートファイルを分けたのでしょうね。わかります。一度複数に分かれてしまっているテンプレートをあとから一つに統合するよう修正するのは、簡単そうにみえて実は結構大変でした。


色々な動作も遅く、PCには存在するのにSPには無いか、あっても正常に動作していないフシのある機能(あるいはその逆)ということもありました。もちろん最初からそういう状態ではなく、機能開発を日々積み重ねていくことで徐々に問題が蓄積していったのでしょうが、その状況でも日常的に使い続けて頂いていたユーザさんには本当に感謝しています。

そうそう、状況をさらに悪化させる要因がもう一つありました。開発当初のエイチエでは内製での開発は基本的には行わず、全て外部に発注するという方針で運営されていました。今でこそDXという言葉とともに内製化の重要性は広く知られていますが、当時はまだ本格的な内製化には踏み切っていませんでした。

その結果として、ソフトウェア開発についていじれるパラメータとしては「発注先をどこにするか」ぐらいしかなく、当時少しずつ流行り始めていたオフショア開発のトライアルとしてオフショア開発をしていました。当時のエイチエには、納品物であるソースコードの品質を適切に判断できる体制も不足していたため、不安な状況でした。

 

オンプレ→AWS移行の話が来た

運用開始から数年経ったあたりから、エイチエの開発体制が段々と内製化にシフトし始めていました。それまで外部に発注していたソースコードも、内部で巻き取りはじめました。その過程で、ボーイスカウトルールの精神で、発見された問題点に少しずつパッチを当てていってはいました。ただ、根本的にどうにかするには全体にばっさり手を入れないとどうにもならないなーと感じつつも、日々の忙しさの中でなかなかその一歩を踏み出せずにいました。

2018年の8月頃に、そのような状況が変わる機会がありました。エイチエを運用開始した2011年から7年が経過し、世の中はすっかりクラウドファーストになりました。オンプレ環境は持たずにすべてAWSやGCPだけで運用するという構成も一般的になり、珍しくなくなりました。弊社もかなりの台数のサーバをデータセンターに抱えていたので、オンプレのインフラチームはハードウェア故障や機器増設などの障害時対応で時間外労働をすることも多く、当時は大変だったようです。働き方や時間外労働、また世の中のトレンドなども鑑みて今後のインフラ構成・運用像を検討した結果、オンプレ環境を全廃して全てAWSに集約するという意思決定がなされました。


このときの要件は、オンプレで動作しているサーバ群をAWSへ移行することだけでした。幸いにもその時の環境は VMWare 上で動いていたため、VMごとAWSに持って行けば移行作業は終わりではありました。

ところで筆者が所属している「ヘルスケア事業部」という部署は、非常に多くの事業やサービスを担当しているのが特徴です。エイチエ以外にも、モダンな環境のサービスも、そこそこの数があります。そのため、仮にエイチエを現状のVM構成そのままで持っていって稼働することにしたとしても、実は一日中そのレガシーコードだけに向き合うわけではありません。エンジニアのモチベーションという意味では、レガシーコードをいじる時には無の境地でこなし、他の事業やサービスに力を入れるという選択肢も取れなくはありません。

しかしこの話を聞いたときに、「これはリニューアルをするチャンス」だと思いました。幸いにも無停止移行は求められていないため、「一時的にサイトが止まる」というイベントが社内にもユーザさんにも周知されることになります。このタイミングを逃すと、次にリニューアルに着手するチャンスは無い。いや、やろうと思ったらいつでもやれたかもしれないし、今後もできるかもしれません。ただ日々の忙しさの中にいると、何かきっかけが無いとスタートを切るのは心理的に難しいというのが正直な気持ちでした。

今回リニューアルに失敗したとしてもそれを捨ててVMでそのまま移行をすればいいだけなので、最悪の時の逃げは保証されています。一方でリニューアルに成功したときのメリットは大きい。エンジニア間で相談し、「フルスクラッチで書き直す」ことに決めました。

 

リニューアルのための期間を確保

「現行のシステムへの機能追加・機能改善はそのまま継続しながらも、リニューアル版のシステム開発を行う」ということができれば理想的なのでしょうが、事業部で稼働できるエンジニアは2人しかいませんでした。またその2人も、関わっている事業はこのエイチエだけではなかったため、残念ながら「リニューアル作業中は、現行システムに機能追加をしない」という選択肢をとらざるを得ませんでした。

機能追加が停止になる期間をどう確保しようか悩んでいたのですが、企画担当者や事業責任者と話してみると予想に反してすんなりと受け入れられました。それには、このような背景がありました。

  • 色々な機能がよくわからないことになっていたので、一部はまだオフショアに開発を出していた。リニューアルして全てを内製にするとその費用が削減できるし、開発のリードタイムも短くなる
  • 「ある箇所に広告(あるいは何らかの機能)を出したはずなのに、実は出ていないページが後からから見つかる」ことや、「ログアウトしていると広告や機能が出ていない」など、作りの問題による機会損失がなくなる
  • 応答速度が改善することによりサイトのPV増や直帰率の低減が期待でき、またHTMLも作り直すためにSEO面での改善も期待できる

当たり前のことが当たり前にできるようになるだけですが、これまでの状態を見てきた企画担当者や事業責任者には、かなり魅力的に見えたのかもしれません。

いろいろと議論した結果、リニューアル期間として半年間の時間を確保することができました。内製開発なのでかかるコストはPL上の内部人件費と、HTMLマークアップの外注費ぐらいです。ちなみにHTMLやCSSのマークアップはわりと時間が取られることが多いので、(LPなどの一枚物ならともかく)一から書くときには外部にお願いしてしまうことがわりと多いです。

全ての問題点が解決された理想のシステムとまでは言えなくても、リニューアル前に比べれば格段に良くなるはず。リニューアルの勝算は大いにありました。仮にリニューアルの開発の進捗が悪かったとしても、既存環境のVMをそのまま移行して稼働すればいいだけです。その場合でもOSのサポート期限が2020年11月あたりに来るのでそれまでにはどうにかしないといけなかったですが、まぁどうにかなるだろう、とわりと楽観的に考えていました。

 

こんな感じで進めていった

個人的にリニューアルプロジェクトの最大のアンチパターンだと思っている「良くわからないから、全ての機能を移行しましょう」をいかに避けるかというのが、成否のキモだと思っていました。

まずは機能ごとの利用数を把握するために、Google Analytics や Apache などのログとのにらめっこを始めました。幸いエイチエはコミュニティサイトなので、ユーザの生の声は掲示板の書き込みを見ていれば把握することができます。

 

1) 現行システムから機能を削って、様子を見る

リニューアルの対象となる機能を減らすために、アクセスログなどを参考にして、明らかに使われていない機能や、そもそも正常に動いていない機能を少しずつ画面から削除して見えなくしました。ユーザの反応を掲示板で確認し、特に問題無さそうであればもうその機能のことは忘れて、他の機能へ取りかかります。

当時のエイチエには、当時流行った「一定のポイントがたまったら、何かいいものに交換できる」というポイント機能もありました。確かクオカードに交換できるようになっていたため、定期的にクオカードの棚卸しや追加発注などをしている様子を見たことがあります。その運用の負荷もそれなりにあったようで、無くすということで企画担当者とすぐに合意でき、ユーザに告知の上でリニューアル前に廃止をすることができました。廃止理由が交換のための出費を減らしたいとか、今あるポイントが使われる前に廃止してしまいたいというような後ろ向きかつ不誠実なことではなかったので、「告知から廃止まで、かなり長めの期間を取ろうね」と企画担当者と話したことを記憶しています。

幸いなことに機能廃止に関するネガティブな反応はほぼ0でした。実は使ってたんだという機能をリニューアルのタイミングで廃止してしまうと不満もあがりますし、追加開発の優先順位が狂ってしまいます。仕事の優先順位や取捨選択を他人の手に委ねると仕事に追いまくられるようなことになってしまうので、「現行システムの時点で機能を削減していく」というやり方は、なかなかうまくいったなと感じています。

 

2) あるべき動作で仕様を定義

持って行くと決めた機能は覚悟を決めてリバースエンジニアリングをしていきましたが、実装の謎の依存関係や動作をほどくだけで、リリース予定日が過ぎてしまいそうな感じでした。

仕様の現状把握をしようにも、今となってはなぜそういう仕様/動作/実装になっているかまったくわからず、また合理的とは思えない仕様だったところも多かったので、多くの箇所で「この機能のあるべき動作」を定義して、それに寄せていくようにしました。

たぶん後付けで機能追加されたからだと思うのですが、サイトに質問を投稿をするときに、PCには無くてSPには存在する入力項目があったり、あるいは片方には確認処理があるけどもう一方には無いみたいなことが普通に発生していました。多くの場合、PCとSPのどちらの動作があるべきなのかは自明だったのですが、どちらが正しいのか判断が難しいような箇所は、関係者一同でえいやで定義したところも少なからずありました。

あまり使われていない機能ではあったんですが、一部に Flash とかを使っているところもあったんですよねえ。IEを使っているユーザさんも多かったので全く機能が使えないということはなかったんでしょうが、それを発見したときには「Flashかぁ。。。」と感じたことを思い出しました。

 

3) 実装が重そうな機能は二次開発へ

いらないと思われる機能のリストラが終わった後には、機能の優先順位付けをしました。使われてはいるけど利用者が少なくて、かつ実装やデータの変換・移行にちょっとだけ時間がかかる機能をピックアップし、二次開発に回すことに決めました。その中でも大きな機能の一つは「献立レポ」という、献立の画像を投稿できる機能でした。

栄養士・管理栄養士は料理の写真を見れば、よっぽど特殊なものでなければ作り方はすぐにわかるそうです。そのため普通のレシピ投稿サイトとは異なり、必要となる情報は使っている素材の種類だけで、レシピそのものはあまり必要とされません。季節やイベント時の献立をどうしようか、そのネタを探すのにいつも悩んでいるそうです。

ただ、社内の管理栄養士さんに確認してもネタ帳的に使うのが目的のため、その料理が何なのかがわかるだけで充分らしいのですが、当時のエイチエの献立レポの写真は、みなさん手持ちのスマホかなにかでフラッシュなしで撮影しているらしく、あまり美味しそうに見えないのが気になっていました。


以前どこかのサイトで、素人でも料理の写真を美味しそうに撮る方法に関する記事を読んだことがあります。その中に「素人の人が料理写真を撮ると、単なる記録になってしまっていることが多い」という記載がありましたが、まさに記録としての写真になっていました。特に美味しく見える必要は無いとはいえ、ちょっとだけ開発者のこだわりとして、投稿時に簡単なフィルタを通せるなどいくつか機能追加を行うために、
リリースを少しずらさせてもらいました。データ構造も少し複雑だったため、そこをほどくのも少し時間がかかりそうという理由もあるにはありましたが。

また工夫の一つとして、リニューアル後にその機能が無いことについて言及されないよう、リニューアル告知の時点でその機能は後日リリースで追加される旨も記載しました。もちろんそれでも言及されることはあるかもしれないでしょうが、リニューアル後の話題を少しでも、移行した機能が動く・動かない、使いやすい・使いにくいなどの話題に限定できたらなと考えていました。

 

4) 開発は得意領域で分離して、使えるものは使う

今回は学習期間を取る余裕が無かったので、「プレゼンテーション層(フロント)」「データアクセス・ビジネスロジック層(バックエンド)」で役割を分割し、各エンジニアが慣れている技術を使うと割り切って、各ソースコードをそれぞれ別々な言語で実装しました。

プレゼンテーション層の側は PHP/Laravel で、バックエンド側は Elixir/Absinthe で実装し、間の連携はGraphQLです。

GraphQLを採用した理由は2つあり、一つにはいちいち連携せずにプレゼンテーション層側が必要とするデータを柔軟に追加・変更できるようにするという、RESTじゃなく GraphQLが採用されるど真ん中の理由からです。開発中にお互いの実装を待つようなことをできる限り減らしたいと思い採用しましたが、狙い通りほとんど待ちが発生することは無く、快適に開発を進めていくことが出来ました。

GraphQLを採用したもう一つの理由は、栄養士の求人募集に関する機能の運用が、別の組織になる可能性が見えていたからでした。それを見越してフロント側は「Q&Aなどのコミュニケーション機能」と「栄養士の求人機能」とでソースコードをわけて実装されています。さすがに現代の日本において複数のソースコードが同一のDBに直接アクセスするようなことは避けたいので、組織間をまたいだ状態でもお互いが自由に開発・運用していけるようにと考えていました。コロナ禍で100%完全リモート環境であっても、お互いにいちいち同期とかを取らなくてもまったく困らずに平行開発できていることを考えると、GraphQLを選択しておいて良かったと感じています。

また、 

  • 全文検索はElasitcsearchを使い、質問やコメントなどが投稿された時点でSQS にメッセージをキューイングして、バッチで登録
  • CircleCIで自動テスト、Sentryでエラー監視、Mackerel でリソースや死活監視

など、特段珍しいものではありませんが、既にあるありものは普通に使うようにしています。

 

5) 管理画面を無くした

内部にエンジニアが居なかったからでしょうが、リニューアル前のエイチエではSQL一発で簡単に答えが出るようなものを含め、何でもかんでも管理画面に実装されていました。メニューが何階層にも入り組んでいて、やたらと機能が多かったです。

しかしこれは管理画面を使った業務を運用担当者にヒアリングし、都度必要な時点で手動対応することで問題ないことがわかりました。ダッシュボード的なものは Metabase で、ログ集計みたいなものは Amazon Athena, それ以外の定常的には発生しない業務は都度対応することにして、最初は管理画面丸ごと捨てることにしました。これでかなりの実装工数の削減になりました。

管理画面にあった機能は必要になった時点で作っていこうと思っていました。ただリリースから一年以上経過した現在でも、結局必要としたのはユーザや質問の検索と削除ぐらいだったため、非常に簡易的な管理機能を用意しただけで運用できています。

 

もうツラクナイ

その後リリース日を迎え、リニューアルしたサイトがリリースされました。特に何事も無く無事にリニューアルに成功しました・・・と言えればハッピーエンドではあるのですが、デザインが大幅に変わったことで、リリース当初は悪い意味でのかなりの反響がありました。

幸い目立ったバグもなかったため、特にネガティブに受け止められている箇所を集中的に、何度も修正を行いました。半月ぐらいかかりましたが、その後はなんとか落ち着いてホッとしました。

リニューアル後はアクセス数やサイト利用者数もリニューアル後から有意に増えました。開発のリードタイムも拡張性も大幅アップしています。自分たちが作ったものなので、全体から細部まで把握しています。逆に言うと、言い訳ができないといえるのかもしれません。実施した施策がうまくいかないこともありますが、施策の試行錯誤や、場合によっては元に戻すようなこともストレス無くできるようになったので、関係者一同の喜びの総量は確実に増えたんじゃないかと思います。

  

エス・エム・エスでアーキテクトとして働くこと

話題は変わりますが、エス・エム・エスという会社は昔からワークライフバランスを大切にしてきた会社です。2010年頃は確か20:30完全退社で、現在では19:30完全退社(※現在エンジニア等一部職種はフレックス(コアタイム12:00~16:00)の勤務体系で、19:30完全退社の対象ではありません)となっています。この「完全退社」は単なるお題目ではなく、実際にオフィスから人がいなくなります。自分自身は「ワークライフバランスなにそれ?」という零細企業から転職してきたため、最初にその話を聞いたときは綺麗事だけでまったくの嘘だと思っていました。それが本当だと知って、とても驚いた記憶があります。

限られた時間の中で成果を出すには、いかに「今やることを減らす」「将来やることが増えないようにする」かがポイントになると思っています。今回のリニューアル作業であれば機能を事前に枝刈りして、いらないものは作らない。作るにしても作りすぎない。作らなければメンテナンスもいりませんし、壊れることもありません。もちろん作らないことそのものが目的では無いので、作ることを過剰に回避した結果として複雑なピタゴラ装置みたいな構造になってしまうぐらいなら、作る手間を惜しむことはありませんが。

事業部門に所属していると特にそう感じますが、事業会社は事業を前に進めることが目的です。(法令遵守とかルールを守った上であれば)その手段は問われません。実現困難なことを実現できてもあまり関係なく、逆に言うとどんなに少ない工数で楽々と実現しようと問題ありません。また、労働時間を増やして回そうという発想がそもそもありません。転職してしばらくは「もっと残業したい」といつも思っていました。いかに、頑張らずに回り続ける体制やシステムをつくるのかが重要だと思っています。何なら平常時はいつも暇でもいいとさえ思っています。

 

こういう人がエス・エム・エスにはマッチしそう

「課題解決」に興味がある人に来て欲しいと思います。課題が死ぬほどあります。事業をいかに拡大させていくかという未来の話もありますし、足下での事業運用の効率をいかに上げていくか。あるいは業務プロセスを改善するにあたり、ビジネスロジックのドキュメント化などの既にある物を可視化する仕組み作りとか。業務改善やデータ分析など、人がいればもっとできるということがたくさんあります。

色々なフェーズの事業があるので、0→1, 1→10, 10→100の経験をすることもできます。ソースコード・技術選択・運用設計など、上流でいい加減なことをやると、あとで自分の首が絞まる経験を実体験として積むことができます。自分事じゃないと本当の意味では腹落ちしませんし、理解も難しいです(アーキテクトを目指すエンジニアの最短ルート - SMS Tech Blog を読んでみると、理解がより進みます)。

手でコードを書く以外の方法で問題解決をはかり、物事を前に進めていくことが好きだったり得意な人も向いているのではないでしょうか。Excelの組み合わせで業務プロセスを作るということは普通にやりますし、作らずにあるいはシステム間を繋ぐ程度の開発やカスタマイズでどうにかなったら「勝った」と感じるぐらいです(正確にはExcelでやりきろうとすると業務が散らかるので、最初はExcelにせよ、その後に業務整理してkintoneに置き換えることが多いです)。

一方で、メッセンジャーや指示待ちの人は向いてなさそうです。みんなたくさんのタスクを持っているので、依頼や進捗確認など、自分が積極的に動いていく必要があります。事業の課題ぐらいまでは出てきますが、その要件を誰かが固めてくれるわけではないので、そこからまとめあげて解決策に落とし込める人がマッチするのでは無いでしょうか。

 

我々は常に人手不足です。少しでも事業会社でエンジニアとして働くことに興味を持ったら、お気軽にご連絡ください。カジュアル面談も大歓迎です。コロナ禍で全てがリモートになってますので、お互い、よりカジュアルにお話できるかと思います。