こんにちは。技術本部 データ戦略部 Newsグループの古本です。
今回は負債と化していた旧システムを、約半年の時間をかけて返済(削除)したことをお話したいと思います。
企業向けプレミアム、あらためEight Teamについて
僭越ながら今年の始めに新システムに移行した話を当ブログに投稿させて頂きました。
buildersbox.corp-sansan.com
今回削除対象となった旧システムは、この時に移行元となったシステムです。
当時は Eight 企業向けプレミアム というサービス名でしたが、去る2021年11月15日に Eight Team へとリニューアルを行いました。
「Eight Team」は、部署・プロジェクトをはじめとするチーム内での人脈共有を推進し、営業活動を後押しするサービスです。
もし興味があればよろしくお願いします。
materials.8card.net
旧システムが負債となった経緯
使われなくなった機能なので単純に削除すればいい、そう思ってた時期もありました。
ところが実際に削除してみたら他領域も含めた大量のSpecのケースが落ち、調査を進めると想定以上の影響範囲であり簡単に消せないものであることがわかりました。
こうなると既存システムに対するデグレのリスクも高まり、迂闊に消せなくなります。
そして影響範囲が広いので消さなければ消さないで問題が生じます。
本来であれば不要な実装が発生したり、儀式的が確認作業も出てきてしまいました。
こうして使われなくなった旧システムは、徐々に存在するだけでコストとなる技術的負債と変わっていったのです。
どのように負債を返済していったか
まずは見積もり
借金のご返済は無理のない返済計画からとどこかで聞ききました。
計画を建てるためにも規模感を測る必要があり、まずはチームで見積もりを行いました。
当初は全体の影響について掴めてなかったので、一気に見積もることが出来ませんでした。
それでも削除しなければならない部分は明確だったので、削除する範囲ベースで分割を行い、それぞれに対して見積もりを行うようにしました。
具体的には、
- API (Controller)
- Model (CloudSearch)
- Model (RDS)
- Mailテンプレート
- Batch
- etc
といった具合です。
最終的に旧システム削除の作業は全部で18個のProduct Backlog Item(以下、PBI)に分割されました。
そして荒見積もりではありましたが、約1ヶ月強という見積りが得られました。
返済を進めることをチームで合意する
なんで1ヶ月以上も掛かるの? と聞かれても自分たちもビックリしたくらいなのですが、3年以上も運用されると草の根のように依存が発生し、それらを洗い出しすとこれくらいの規模になります。そりゃSpecも激落ちしますよね。
新システムは既にリリースされ稼働中なので、旧システムを削除することは事業的な価値を生みません。
そこに1ヶ月以上の開発工数を取られるともなればPOやビジネス側からの理解は得難いものです。
消さないことのリスクは理解してくれていましたが、最大の懸念は、開発が止まることでチームとして成果が出せなくなる ことです。
チームとして合意して動き出すためには、その懸念を解消する必要があります。
そこでまとめて削除を行うのではなく、分割したPBIを各スプリントのなかで少しずつ進めていく方法を提案しました。
個別の作業ボリュームはそこまで大きくないのでスプリントの中に1, 2個含ませてもチームとして成果を出しつつ返済作業を進めることは十分可能です。
それなら・・という事でチーム内で合意でき、返済を進めていけることが決まりました。
参考までに、当時の進めていたPBI一覧の一部です。
諸事情によりボカしているため見づらいですが、薄い緑色で色分けされたPBIが他のPBIの合間に入っていることがわかると思います。
このようにしてスプリントを進めることになりました。
返済方針を決める
やることと進め方は決まりました。
次は分割されたPBIをどう削除していくか、つまり優先順位に関わる方針を決める必要があります。
今回は分割して進めることが決まったので時間的なリスクより、既存に影響が出ることが最も避けるべきリスクとなりました。
そのためユーザーにより近いところから消していくという方針を決められました。
これは呼び出している部分がなければ影響がない、もしくは限定的になると思ったからです。
この方針に沿ってPBIの優先順位も決まりました。
作業もリリースもテストも分割
優先順位も決まりあとはやるだけです。
このとき意識したのはPBI単位で消したものは、そのPBI単位でリリースするようにしたことです。
分割して進めたとしても、1つのfeatureブランチに集約される続けると、差分も大きくなりますしリリース時のリスクも増えます。
旧システムは使われていないので、既存への影響が無ければ個別に消しても問題はないはず、というこでリリースも分割して行うことに決めました。
作業効率をトータルで見ると無駄が多いと思いますが、分割することで様々なリスクが減り、テストの回数は増えるが1回あたりの負担も減るメリットも大きかったと思います。
また開発初期段階では見えてなかった影響が出てきた時も、分割していたからこそ対応をすぐに行うことができたというメリットもありました。
返済で苦労した点
動き出すまで
どちらかというと技術的な話ではないかもしれませんが、今回の作業は方針決めて動き出す所まで行けた、という部分が一番苦労したと思います。
ビジネス側はもちろん開発側としても作業的にそこまで面白い作業でもなく、技術的なチャレンジも少ないのでモチベーションを上げにくいです。
それでも始めることが出来たのは、開発組織のカルチャーや技術的負債に理解のあるPOだった事も大きいですが、社内からの声を適切に届けてやることの意義を共有できたのも大きかったと思います。
具体的には、
- 動いていないシステムに対するコストに対するインフラチームからの声
- CIのBuild時間短縮のために使ってない機能のSpecは消して欲しいというアーキテクトチームからの声
このような声です。
こう書いてはこのブログもアレなのですが、他社で技術的な負債を華麗に返済したという話を見てもよそはよそ、ウチはウチの精神かあまり自分ごとにならなかった気がします。
しかし、社内から届けられる声は重みが違うので、メンバーにやることの意義を理解し、自分ごとするきっかけになった気がします。
プラスして具体的な返済方法を提案できた事で、チーム内で合意しモチベーションを持って動き出せたと思います。
社内で使う外部サービスとの連携
当初は同じアプリケーションからの参照を気にすればいいと思いました。
しかし、分析や運用等で外部サービスから連携していた部分がありました。
今回のケースだと Redash や Slack がそれに当たります。
外部サービスもビジネスが関わる部分は当初から把握してましたが、社内で使う外部サービスは意外と意識から外れていました。
これらは使わなくなったことにより、出力される数値が0になったり、投稿が行われなくなったりします。
それを消すとなると、出力されるレイアウトが変わったり、チャンネルそのものが不要になることに繋がります。
物によってはKPIに関わる部分もあり、調整が発生したので意外と苦労したポイントでした。
外部キー制約
外部キー制約は特定のテーブル間での整合性を保つために設定される制約です。
旧システムを削除する過程で関連するテーブルが消えるので、この制約も外せばいいのですがこの外すタイミングに罠があり、今回の対応の中で唯一の障害発生に繋がってしまいました。
通常であればこのような制約が掛かっているテーブル間ではdependent: :destroyのようにして整合性を保ちます。
問題となったのは退会処理で、ここは削除に対象が多く、消し残しも防ぎたいので非同期で実行されていました。
ここで作業を分割したことが裏目に出ました。
Modelやテーブルを消す前に、そこにアクセスしている箇所を潰していった結果、この整合性を保つJobの処理を消してしまいました。
その結果、非同期で整合性が保たれなくなるケースが産まれ、その後で親テーブルのデータを消そうとしたときに外部キー制約エラーが発生してしまいました。
幸いにして発見が早かったのでそこまで大きな問題になる前に対応できましたが、外部キー制約のあるテーブルに対して同期を行う処理があるという意識がなかったです。
そして、こういった事象が1個出たために他にも無いかということを追加調査することになり、一連の対応を含めると今回一番苦労した箇所になります。
返済を終えて
新システムをリリースしたのが、だいたい1年前です。
そこから3ヶ月経ってから返済を始め、返済が完了したのはそこから半年後とずいぶんと息の長い活動でした。
返済を終えたことで不要だったコードが削除され運用コストも下がり、CIビルドの時間も短縮されるなど一定の成果はありました。
一方で開発体験が大幅に向上する訳でもなく、大きく変わらない日々が続いています。
それでも負債が無くなったことにより減ったコストは確実に実在しますし、不要なノウハウを後世に残す必要もなくなりました。
そんな負のサイクルに陥らないために負債を貯めない・貯めても計画的に返済する活動を続けていくことが大事だと思います。
今回チームで進めたアプローチは他でも使えると思うので、今後も諦めず無理のない返済を続けていきたいと思います。