KAKEHASHI Tech Blog

カケハシのEngineer Teamによるブログです。

Musubi のカイゼン 2023 冬⛄

本記事は カケハシ Advent Calendar 2023 9 日目の記事です。

adventar.org


Musubi 開発チームの加藤です。1 年ぶり ですね。 皆様は 2023 年をいかがお過ごしでしょうか。

今年 Musubi 開発チームは多くのリソースを使って技術的負債の解消を行いました! 今回はその内容の一部を報告します。

バックエンドの完全サーバレス移行🎉

2022 年から、サービス開始時の AWS Elastic Beanstalk から、AWS Lambda + Amazon API Gateway のサーバレス構成へ移行を開始しており、今年ついに完了しました!

移行のモチベーションは以下のとおりです。

  • Musubi はピークタイムと平常時でアクセス数の差が大きいため、スケールイン・アウトが高速な AWS Lambda のほうが嬉しい
  • AWS Elastic Beanstalk はデプロイやロールバックに時間がかかるため、リリース後に不具合が起きたときのロールバックを高速にするために一工夫が必要だった
  • 既にMusubiには一定規模のユーザがいるため、コールドスタートの確率は非常に低く影響は小さい

一方、移行中に以下のような問題がありました。

  1. この構成ではペイロードサイズが 6MB に限定されるため、それを超過することがある API でエラーが起きた
  2. 同時実行数(Reserved concurrency)を設定しないままでスパイクしてしまい、同一 AWS アカウント内にある同じく同時実行数を設定していない Lambda 関数と一緒にスロットリングした

Lambda へ移行した後はデプロイもシンプルになり高速化され、レイテンシも少し改善されました。

社内オペレーションを Slack で🚀

これまでは、 ユーザ様ごとの設定変更やお知らせの掲載等のオペレーションは各々がコマンドライン操作等で行っており、 ユーザ数増加(ありがたい!)に比例して作業コストが増加していました。

これはどげんかせんといけん、でも作業コスト削減のためにメンテナンスコストの高いものを入れるのはちょっと、ということで カケハシ全社で使っている Slack に目をつけました。

Slack の提供する Bolt Framework を用いることで、 /musubi-admin のような Slash command を契機にダイアログを表示することが出来るため、 そこにオペレーションの入出力を実装しました。

Bolt Framework のダイアログの UI は Block Kit で作るのですが、Block Kit Builder を用いることで 運用担当者との UI イメージが詰めやすく、普通にフロントエンドで作るよりもスムーズに進められたケースもありました。

現在は4つのオペレーションがこの Slack で作った画面の上で実行できるようになっています。

フロントエンドのテストカバレッジの改善📊

Musubi フロントエンドのコードには単体テストが不十分なものがあり、 そのためにデグレードが発生したり、あるいは手動確認に頼って納期やコストに影響したりしていました。

まずは計測、ということで GitHub ActionsJasmine + Karma + Istanbul で取得したテストカバレッジを SonarCloud へ送信して、 デフォルトブランチと PR ごとのテストカバレッジを可視化しました。

Musubi フロントエンドのファイル数・コード量は結構なものなので、以下の基準でファイルを絞り込みました。

  • 不具合が発生するとユーザ様の業務に致命的な影響が発生しうる機能の実装
  • カバレッジが60%未満のもの

また、このタイミングでテスト実装のルールやナレッジも整備しました。以下はその一部です。

プロジェクト全体の行カバレッジが 55.9% で単体で見ると 0% のファイルが複数あるようなところからのスタートでしたが この改善の結果、現在は行カバレッジが 76.4% に向上し、 カバレッジが低いファイルは影響が低いもののみになっています。

フロントエンドとバックエンドの型付けの強化🏋️‍♂️

Musubi のフロントエンドは TypeScript (Angular なので)、バックエンドは Python を用いて構築しています。

どちらも型付けに課題があり、強化を行いました。

  • tsconfig.jsonstrict オプションtrue になっておらず、型チェックを抜けて nullundefined が入ってくる余地ある
  • mypy を開発プロセスに組み込んだ時期が遅く、それ以前のコードは型ヒントが付いていない

以下のような順序で対応しました。

  1. うっかり壊しても影響の少ない単体テストコードに型ヒントを付ける
  2. テストコードでパターンを掴んだ状態で実装コードにも型ヒントを付ける

この対応順序も良し悪しがあり、例えば実装コードの戻り値に型が付いていないとテストコード側でも型が不定になるケース発生して 実装コードに型を付けた後にまたテストコードを直す、という反復が起きてしまいました。反省です。

また、既存コードで動作を大きく変えないと直せないものは諦めて @ts-ignore# type: ignore で無視するように割り切りました。 新規コードの型チェックが十分にできれば良いので、既に稼働している既存コードの振る舞いを変える危険は避けました。

この対応で型チェックがきちんと行われるファイルを大幅に増やすことが出来ました。

バックエンドを FastAPI へ移行開始🚚

Musubi のバックエンドは Python + Pyramid + typedtuple で構築されていますが、 Pyramid も typedtuple も mypy と相性が悪く、前述の型付けの強化で苦戦しました。

また、バックエンドの初期実装時の設計からのブレが大きくなってしまい 「この機能の実装はどこを探せば良い?」「この実装はどこに実装すれば良い?」 という戸惑いの声がチラホラ聞こえるようになってきました。

この状況を解決するため、既存のバックエンドをそのままに FastAPI で新バックエンドを構築し、 既存バックエンドから少しずつ API を移植していくプロジェクトを開始しました。

FastAPI の以下の点でメリットがありました。

  • フレームワーク全体で mypy を前提としており、十分に型付けができる
  • DI を備えているため、柔軟にアーキテクチャを設計できる
  • OpenAPI JSON が出力できるため、API のリクエスト・レスポンスのスキーマをクライアントと共有できる

また、今回はオニオンアーキテクチャで実装カオス化の防止を試みています。

こちらはまだまだ実装中なので、その結果は来年の何処かでご報告させてください。

最後に

思い返せば2018年の入社時には気にしていなかった負債も、ユーザ様が増えるにしたがって年々ボディーブローのように効いてきて 今年は特にそれがピークに達した年でありました。

いずれも簡単な仕事ではなく、それぞれの対応を進めてくれた他の Musubi 開発メンバにこの記事を書きながらそっと感謝していました。

株式会社カケハシでは、一緒に開発していただけるエンジニアを募集しています。 「2023 では返しきれなかった Musubi の負債をぜひ一緒に返したい!」という熱いエンジニアもこちらからお願いします!