Just do IT

思うは招く

技術的負債返済の道のり:1年かけた挑戦と学び

これはフィヨルドブートキャンプAdvent Calendar 2023(Part 2)、9日目の記事です。

adventar.org

私は英語塾「Catal」で生徒が使う学習システムを開発しています。2022年秋から2023年秋にかけて、技術的負債を返済しました。

この記事では、何が起こっていたか、どのように返済したか、学んだことなどを書きたいと思います。 (1年かけたと言うとかなり大げさな感じになりますが、チームメンバーが私含めて2人なので、通常の開発やバグ対応などを含めながら進めたため1年かかりました)

どのような状況だったのか

サービスのメインとなる情報が格納されているあるテーブルに、似ているようで違った概念が混ざり込んでいました。

また、AとBの違いを判別するのに、2つ先の関連先モデルで条件分岐して評価するという記述になっていました。

例:

if @some_instance.animal.dog?

これがモデルやViewなど複数の箇所で書かれており、結果的にコードが冗長になっていました。

「なぜ最初からテーブルを分けなかったのか」と思われるかも知れませんが、開発当初は非常に似た概念だったため、テーブルを分けずに実装されたようです。私はこのとき在籍していませんが、もし私がいたとしてもこの方法で実装していたかもしれません。しかし、開発を進めるうちに、この2つの概念の違いが顕著になっていきました。

「今すぐ解決しなければコードベースが爆発する!」というほど深刻な状況ではありません。しかし、このままいけば間違いなくカオスなコードが増えていくことが予想できたため、CTOやメンバーと合意し、返済に着手しました。

なにをしたのか

結論から言うと、別々の概念なのでテーブルを分けました。

やったことが多いため、とてもざっくりお伝えします。

  • AとBの概念を分けるため、テーブルを分けた
  • もともとAに関連していた別テーブルも、B専用のテーブルを作った
  • それに伴って、Bに関連するテーブル群も作った
  • Model、Viewファイルもイチから作った
  • もちろんテストも書いた
  • 最終的に、Bやその関連テーブルに元のデータを移行した

さらっと書いてますが、実際にはとても根気のいる作業でした。新機能をイチから作っていくような感じです。

何に気をつけたか

実装中に気をつけたことです。

既存実装に引っ張られない

既存の実装にとらわれないように、新たに機能を作り直す気持ちで向き合いました。

  • 「既存の命名はこうなっているけど、本当にこれでいいのかな?」
  • 「Reduxを使ってるけど、本当にこれ必要なの?別のライブラリを使えばもっと短くシンプルに書けるんじゃない?」

といったように、既存実装に引っ張られないように、今できるベストの選択をするように心がけました(結局ReduxではなくSWRを使いました)。

プルリクエストは可能な限り細かく分けて本番にがんがんマージしていく

プルリクはコンテキストごとに小さく区切り、本番に影響がなければマージしていきました。こうすることで、大きな負債の返済でもレビュアーの負担を軽減できます。例えば、CRUD機能を実装するならすべての機能を実装してプルリクを出すのではなく、C、R、U、Dといったように小さく分けていきました(もちろん場合によって分けないほうがいいこともあるのでそこはちょうどいい塩梅で)。

また、プルリクをリズムよくマージできることで、開発者の精神衛生にも良い影響を与えたと思います。

結果どうなったか

冒頭で紹介した条件分岐をいろんな箇所から消すことができ、コードの見通しがとても良くなりました!

# こういった記述を削除できてスッキリ
if @some_instance.animal.dog?

また、そもそもの概念を分けたため、修正や新機能を入れやすくなりました。他のメンバーがその箇所に修正を加えたとき、「変更を入れやすくなった!」と喜んでもらえたのが最もうれしかったです。

感想

負債を返して思ったことです。

似ている概念を疑う

たとえ似たような概念であっても、将来的に異なる機能が実装されるのが予想できるなら、最初から分けて実装することを検討してもいいと思います。実際の開発だと実行するのは難しいものですが、数個先の関連テーブルを見て違いを判別するような場合、要チェックかもしれません。

人的リソースがない中でも技術的負債を返済しておくと開発スピードは上がる

私が所属するチームは、私を含めて2人のエンジニアで開発をしています。正直、このような小規模チームで大きな技術的負債の返済をするのはかなり大変でした。

しかし、将来的にカオスになることは誰が見ても明らかだったので、開発リソースを集中投下しました。結果、大きな技術的負債はなくなり、変更を入れやすくなったので開発速度が上がりました。人的リソースが足りない状況だったので1年かかってしまいましたが、返済してよかったと思います。

経験不足の分野は気軽にペアプロを申し込む

Railsあたりの変更はそこまで苦労なくできたのですが、Reactを使ったフロントエンドの部分で経験が浅かったため、CTOにペアプロをしていただきながら進めました。弊社にはペアプロ文化があります。

ペアプロ文化があるのとないのとでは作業速度が変わり、ひいてはチーム全体、会社全体の効率にかかわってくるものだなとあらためて思いました。 気軽に聞ける、ペアプロできる文化があると、社員・企業双方にとって有益なものになると思います。

エンジニアとして自信につながった

負債返済というより、大きな機能をまるまる作り直すような開発になりました。それまでも新機能を追加した経験はありましたが、テーブルを10個ほど追加しての新機能開発は初めてでした。バックエンドはRailsで、フロントエンドはReactでそれぞれ実装し、大きな開発を経験できたのは自信になりました。

おわりに

1年かけて技術的負債を返済したお話しでした。もし負債の返済をしている、またはこれから取り組むときに参考になれば幸いです。

余談ですが、技術的負債の返済中に住宅ローンを組みました。ようやく2重ローン生活から抜け出せたので、これからは住宅ローンの返済を頑張っていきたいと思います(35年)。

Obsidianで素早く現在の時間を入力したい

「Natural Language Dates」というコミュニティプラグインをインストールすると、日時情報を簡単に取得できる。

たとえば、現在の時間を表示したいときはエディタで@timeと入力するといろいろ候補が出てくるので、nowを選択する。

他にも日付とか入力できるし便利〜