こんにちは!
今日は、とあるWebサービスのバックエンドをリプレースしたときの技術選定プロセスを書き記したいと思います。
対象システムの特性
ユニークユーザ数30万/day - CtoC系サービスで
夜トラフィックが半端ない 、特に、金曜は地獄 DBは40〜50台で負荷分散 - 1時間停止すると○百万の売上がとぶ、
原則無停止運用 - 膨大なデータを活用したAI推薦機能などを入れたいという
運営願望 - 開発は内製
こんなサービスでした。
ソシャゲほどじゃないと思いますが、かなりのトラフィックを相手に戦う現場です。
DBに適切なインデクスを張り忘れるとサービスが遅延、N+1やらかし絶対NG。テーブルはあえて非正規化せよ……!
厳しい戦いの日々でした。
レガシーシステム運用の限界
課題はたくさんありました。
- Perl
- ビジネスサイドが要求する開発スピード(週2,3は本番リリース)
- バックエンド性能劣化
- 蓄積する負債
- ノーテストコード
- ドキュメントレス
- 開発作業の属人化
- 規約のないコード管理
- コードが激しく劣化しており、もはやメンテナンス困難
挙げればキリがありませんが、おおよそレガシーシステムの運用をしているとぶち当たる問題に私も同じくハマってしまったというわけです。コードベースがもはや管理不能なレベルまで劣化してしまっており、ビジネスが要求する開発速度に全然追いつかないという状況でした。
現場でPMしていたこともあり、バックエンドの部分リプレースを決断。
次世代バックエンドフレームワークの選定
Java、Golang、Python、Ruby on Rails、PHP、いろいろ候補を検討しました。
言語特性、フレームワーク特性、現場の実力、DX(Developer Experience)。
様々な観点で比較、フィージビリティ検証を進めます。
優先順位が高い検討事項はいくつかありましたが、その中でも開発効率の改善を主軸に考えました。
リプレースした暁には、
- 誰もが笑顔でDevOpsを実践できる開発環境
- AIを活用した不正データ検知やレコメンド機能の実現
- テスト自動化による高効率開発
これら課題の改善を実現せねばなりません。
技術選定で考えたこと
■ もろもろのフィージビリティ・スタディをして残った候補
- Java Spring Framework
- Python Django
- Golang Echo
が候補として残り、最終的には以下のようなポイントを鑑みてPython Djangoを採用しました。
■ 既存システムへの適合
部分リプレースからPhaseを切ってソースコードの刷新を図っていく方針を取ったため、インフラまで一気にフル・リプレースというわけにはいきませんでした。CtoCサービスでビジネスを止めるわけにはいかないので、ここは仕方ありませんでした。それに、Webアプリケーション以外のミドルウェアやインフラ構成はそれなりにメンテナンスをしており、メス入れ対象としては優先度が高くなかったのです。
既存アーキテクチャに新しく設計洗練されたRestAPIを実装する形でバックエンド移行をする、という制約条件のもと、後入れがしやすい という点が重要なファクタとなります。
この点一番しっくり来たのが Django で、ORMが運用中のマルチクラスタDB構成に簡単に対応でき、少々カスタマイズをすれば、冗長構成DBへの master update / slave read も少労力で対応が可能だったのです。
■ 学習コスト
この点で Java は落ちました。もともと私は長らくJavaで開発していたため、Spring Frameworkも選択肢として良いかなと考えましたが、現場がLL言語以外の経験がほぼなく、Javaのあの固さは受け入れられないと判断しました。Javaで開発していた頃を思い返してみると、安定しないEclipse、開発環境の重さ、何をするにも宣言的で手続きが多いことが負の面で思い出され、とてもではないけど初心者向けではない選択肢だと思いました。
とはいえ、Javaエンジニアのチームだったら筆頭候補だったと思います。一定のレベルのJava集団であれば、静的型付け言語でインタフェース指向な設計開発ができるというのは強いですからね。
Golangは微妙な線でした。取っ掛かりは難しく感じましたが、 言語仕様の特性を掴むとプログラミングをしていて純粋に楽しいと思える良い言語という印象です。フィージビリティ・スタディの一貫で簡単なRestAPIをチームで試験実装しましたが、ポインタや構造体のような前時代的な思想を取り入れつつも、ストレスを感じさせない言語仕様と感じました。しかしながら、言語思想として逐一nilチェックを強制される、例外が存在しない、若干筋肉質すぎるところがあり、素人集団が大規模サービスにいきなり採用するには不安要素がいくつか残るという理由で採用を見送りました。個人的にはとても好きな言語でした。
LL使いのチームということもあり、Pythonはすぐに馴染みました。WebフレームワークのDjangoとFlaskで比較したところ、Flaskの簡素な設計に共感していたメンバーも多かったですが、web層以外は全て自前で何とかしなければならない点で、既存アーキテクチャにうまく適合させるにはかなりのハードルがあり、多少覚えることが多くても、柔軟に今のアーキテクチャに適合できる Django を採用しました。また、Pythonということでデータ分析やAI開発につながる人材の採用につなげたいいう裏目的も少しありました。
■ DevOps観点
CIインテグレーションして継続ビルド、テスト自動化、デプロイの簡単さ、コード規約の徹底など、開発プロセス観点でも検証しました。
Golangはいずれの観点も及第点でした。特に良かった点は、ビルド生成物が1ファイルに集約されるので、デプロイが簡単ということと、様々なアーキテクチャ向けにクロスコンパイルすることができることでした。
Javaは難があるという結論。いずれの要素も実現は可能でプラットフォーム依存も気にする必要はありません。しかし、何よりビルドが遅すぎて運用に耐えなかったのです。短期でリリースサイクルを組んでいる我々にはJavaの鈍重さは受け入れることができませんでした。
Pythonはデプロイフローを実装する際に、一部の利用ライブラリにネイティブ実装のものがあり、gccビルドしなければならない点が難所でした。ビルド生成物はアーキテクチャ依存となるため、ビルドサーバとProductionサーバのアーキテクチャを一致させる必要があります。
感触的にはGolangが一番良かったと感じます。
■ その他検討したこと
最も重視した前述3つ以外にも、当然検討材料として、
- システム要件を実現するためのプラグインやライブラリ有無
- サービスの性能要件を満たせるか
- フレームワークのメンテナンスコミュニティ
- セキュリティ
- LTSサイクル
- オンラインドキュメントの充実度
- 自動テストの組み込みやすさ
- 採用技術に明るいテックリードとなる人物がいるか
など、チームで観点出しをして調査・検討を行いました。
まとめ
最終的に Django を採用する形になりましたが、技術選定の過程で色んなフレームワークの良し悪しを勉強し、チームで一つの結論を導き出したプロセスはとても価値のあるものでした。
結果として新しいバックエンド開発は上手くいっており、本来の目的だったテスト自動化やlintツール導入によるコード規約の徹底、開発環境の洗練など周辺系の整備とともに生産性が大幅に向上したと感じます。
レガシーなコードベースが原因でなかなか実現できなかったビジネス要求に対しても比較的短時間で応えることができるようになりました。
実際の開発現場でこういう技術リプレースを推進させてもらえたことは財産になったし、開発以外の周囲の関係者にこの計画を理解していただけたことにも感謝しています。
機会があれば、Djangoを運用中のシステムに導入する時のポイントを紹介できればと思います。
それでは、今日はここらへんで。
最後まで読んでいただいた方、長文に付き合っていただきありがとうございました!