約90%のメンバーが負債を感じている

岡本直樹氏(以下、岡本):では、「初コミットから3年経ったので、負債について“少し”本気出して考えてみた」ってことで、ラクスの岡本から発表しようと思います。さきほどのように、技術的な話はほぼないので、肩の力を抜いて聞いてもらえればなと思います。

まず自己紹介からです。岡本直樹と申します。2016年にラクスに入社して、「楽楽労務」開発チームには2019年から合流しました。主な仕事としては、詳細設計や実装、あとはメンバーの進捗管理やフォローをやっています。趣味は、ハイキングと歴史SLGですね。休日ごとに山登りとかに行っています。

では、「楽楽労務」についてです。クラウド型の労務管理システムで、開発開始は2018年の8月、リリースは2019年の8月ですね。開発開始から3年、リリースから2年たっています。ラクスのサービスの中では、まだまだ駆け出したばかりの若いサービスです。

とはいえ、開発から3年も経ちますと、こういう状態になるわけですね。「楽楽労務の開発/運用で負債を感じたことがありますか?」という、これは4月に開発チーム内でとったアンケートですが、約90%のメンバーが負債を感じているぞというかたちになっています。

感じたことのある負債の内容は、具体的にどんなものかを聞いてみると、「〇〇の処理が独自でスケールしない」や「依存しすぎで改修しづらい」「毎回手作業でしんどい」など、よくあるような内容がツラツラと出てきて、けっこう負債が溢れ出ています。

こういうアンケートを取ってみたので、ちょっと「楽楽労務」開発の負債との向き合い方について、しゃべれる範囲でこれから紹介しようかなと思います。

負債1:UIの仕様・実装の共通化が不十分

まずは、現在未解決の負債を紹介しようと思います。「いつ発生したのか」と「なぜ発生したのか」と「なぜそれを先送りにしているのか」を軸にして伝えていこうかなと思います。

まずは「UIの仕様・実装の共通化が不十分」。負債が発生したのはいつかというと、開発当初から継続して発生している問題です。発生した原因ですが、フロントエンドのノウハウがあまりない状態で開発チームがスタートしたので、開発の初期においてフロントエンドのUIのコンポーネント化が十分じゃなく、今でもなんですが、同じようなUIでも実装方法が違っている箇所が散見される状態です。

それを先送りにしている理由として、これはありがちなのですが、リリースが優先されているので、現在の機能規模であれば開発速度に対してそこまで致命的な問題になっていないため、ある程度実装がバラバラであったとしても、いったんリリース優先で解決が先送りされている、というのが現状です。

負債2:神モジュールがいくつか存在する

次が「神モジュールがいくつか存在する」ですね。神モジュールというのは、数百行あるクラスや、メソッドが数十個入っているクラス、いわゆるバカでかいモジュールですね。この負債が発生したのはいつかというと、これは機能が追加されるとともに、徐々にクラスが大きくなってきて発生した問題です。

発生した原因としては、これもありがちなのですが、バックエンドのコントローラークラスや、責務が多重になりがちでクラスを適切に分割できないようなものが、だんだんと神モジュール化していっています。

これを先送りにしている理由ですが、今「楽楽労務」は絶賛機能追加が頻繁に行われている状態なので、機能のドメイン理解がどんどん変わっていっている状態です。なので今、先々までを考慮して責務分割をして、クラスを適切に分割するのが難しい状態にあります。

例えば、このバージョンで分割したとしても、次のバージョンでドメイン知識がまたガラッと変わるので「実はその分割方法はいけていなかったよね」というふうになって、次のバージョンではぜんぜんまた違う改修が発生するみたいな。そういう状況になることを防ぐために、あえてそのままにして、未来の仕様を考慮するような判断はしないようにしているのが今の状態です。

負債3:コード化されていない運用作業がある

次が「コード化されていない運用作業がある」ですが、これが発生したのは、リリースしたあと。運用開始時点からも継続して発生している問題です。これについては、原因というよりも発生している内容に近いのですが、定期的に行っている運用業務の一部がコード化されていないので毎回手動で行っている状況です。手動でやるので、実施に時間がかかったり人為的なミスも起こりうる。こういう状態の作業が、いくつか残っているということですね。

先送りした理由なのですが、これについても、追加機能の開発を優先しているので、運用に割けるリソースが最小限に抑えられているからで、運用負債の解消も最小限になっている状態が今も続いています。

ざっと3つほど見てきましたが、先送りされ続けている理由というのが、だいたい「開発速度優先」というプロダクトの事情で、絶賛開発を「どんどん進めていこう」という状況になっているので、どうしても負債が溜まっていく状態にあります。

負債を解決する3つのサイクル

この負債をいつまで先送りにするかが重要なのかなと思っていて、今、開発チームでは少しずつでも解決していくための取り込みを継続的に実施しています。

定期的なサイクルで負債の解消を実施しているのですが、大きく分けて3サイクルあります。一番短いのは単機能開発のサイクルです。ここでは技術的負債の解消、実装コードを見直すような取り組みをしています。

次がバージョンごとのサイクルですね。ここに関しては、そのバージョンにどういう機能を入れるかなど、機能単位で負債を見直しています。主にUI/UXの見直しをしています。

最後が半期サイクルですね。これは、ちょうど4月と9月の半期の終わりごとに運用や開発プロセスの見直しといった、大きめの負債を見直しています。

単機能開発のサイクル

単機能開発のサイクルでやっている取り組みは、実装コードの見直しです。単機能開発中に発生したコードで随時負債になるようなものは、TODOとFIXMEコメントを残しておいて、随時開発中に対応する・しないを検討していっています。まぁ、それは一般的にやるとは思うのですが。

そのあと機能実装が完了した時点で、TODO・FIXMEコメントが残っていないかをいったん洗い出して一覧化します。洗い出した後に、改修内容がその単機能開発のスケジュールに収まるのかをポイントとして、「じゃあ実際にそのTODOとかFIXMEコメントは見送っていいの?」というのを、最終的に判断しています。かつ、これを見送る場合はちゃんと見送った理由を書いておいて、あとでそのコメントを見直したときに、どういう理由で見送られているのかが判断できるようにしています。

実際の対応事例を紹介します。1つ目ですが、「共通の仕組みに対応できていなかったコードを修正」というところです。具体的に言うと、フロントエンドでダイアログを表示するときの共通コンポーネントを実装したときに、先ほども言ったように、ダイアログの表示が今までけっこうバラバラな実装になっていたので、それを共通化しようという話になって、共通のコンポーネントを作りました。

ただ、共通化の作業と並行して、別の開発が進んでいたので、その並行して進んでいた開発だけ共通化の実装が追いつかなかったので、一部共通化できていないコンポーネントが機能開発時に残ってしまった、という状況でした。

ここに関しては、最後見直しで修正しようという話になったのですが、見直しをしようってなった理由は、共通化できていないコンポーネントの存在が今後新規メンバーにとって暗黙知になってしまって、横展開的にコードを見て実装してくださいって言ったときに、「どっちのコードが本当のコードなんだろう?」という疑問が発生してしまう状態を考慮して、共通化はもう全部やりきってしまおうということで、優先して対応しました。

次が「定数化された環境変数を設定ファイルに切り出す」ですね。外部システムの連携時に、タイムアウト値がコード中に定数化されていて、柔軟に変更できなくなっていました。「毎回コンパイルし直さないとコードの中のタイムアウト値が変更できませんよ」みたいな感じになっていたので、これも「環境変数に切り出して、環境変数を書き換えるだけでコンパイルの必要はないようにしたほうがいいんじゃないの?」という、FIXMEコメントがついていました。

これについては、対応工数がすごく少なくて、かつコードを修正することによって、運用上コンパイルしてリリースするみたいな運用が発生しなくてもいいので、運用上のメリットもだいぶ大きく、優先して対応することになりました。これについては、「工数が少なく、かつメリットが大きい」ということがポイントになって対応しています。

バージョンごとのサイクル

では、次、バージョンサイクルでの取り組みになります。UI・UXの見直しをバージョンのサイクルごとにやっています。UI・UXと主にサービスの使い勝手に関わる負債の解消です。ユーザーフレンドリーじゃないUI・UXなど、工数の兼ね合いで対応できずにリリースしてしまったものについて、あらかじめ「これは負債だよね」っていうのを一覧化しておいて、そこの優先順位をつけるようにしています。

かつ単機能開発の設計のとき、もしくは各バージョンでどの機能をリリースするかというの検討のときに、ここのUI・UXの負債のところから棚卸しを行って、じゃあ実際これは対応しますか・対応しませんかというのをバージョンごとに検討しています。優先順位は、もちろんビジネス側の要求の強い順に並べていますが、着手の順番は、実際にそれがどれくらいかかるのかという工数を加味して、優先順位を決めていっています。

これも実際の対応事例を紹介します。1つ目ですね。カレンダーコンポーネントの入力方法の改善です。これは初回リリースからだったのですが、カレンダーを入力するコンポーネントが、いわゆるテキストボックスをクリックすると下にカレンダーが出てきて、カレンダーを選択したら年月日が入るみたいな、そういうコンポーネントがあるのですが、そのコンポーネントで手入力ができなかったんですね。

手入力ができなかったので、毎回カレンダーで選び直すというようにしていたのですが、生年月日などを入れようと思ったときに、当日が初期値になっているので、何十回もクリックして選ぶようになっていて、だいぶユーザーの利便性が悪いだろうということになっていました。

初回リリースから工数兼ね合いでずっと見送りになっていたのですが、この6月リリース予定のバージョンの機能セット検討時に、ビジネス側とも話して、優先度が高い上に工数影響もそれなりに少なかったので、これを入れようということになっています。

次がユーザーへのエラーフィードバックの詳細化です。これについて、初回リリースの時点では、複数のエラーを1つに丸めたメッセージで扱っていました。例えばメールに失敗した場合、通常だと「メールに失敗しました」というようなメッセージが返ってくるとうれしいのですが、メールに失敗したことと、データベースの登録に失敗したことが一緒くたに扱われてしまって、単純に「登録に失敗しました」みたいなエラーメッセージになる感じでした。

1つに丸めてしまっていたので、「じゃあユーザーは実際そのエラーメッセージが出てどうすればいいの?」っていうのが、ちょっと不親切だったかなっていうところが何個かありました。

このへんについては、機能が増えるにつれて、ユーザーに通知するエラーへの考察が進んできています。

初回では「このエラーメッセージはこれぐらいの粒度でいいよね」と思っていたのが、徐々に「やっぱりこれぐらいの粒度のほうがいいんじゃない?」と、機能が増えるにつれてだんだんと共通化できるようになってきたので、新規機能の設計のときにエラーメッセージが新設された場合は、既存機能も含めて見直しを行って、適切にエラーのフィードバックが行われるような見直しをかけています。

半期サイクルでの取り組み

では、次に半期サイクルでの取り組みです。運用/開発プロセスの見直しです。開発プロセスや運用手順など、短期的に解決できないような問題に半期ごとに取り組んでいます。

各期末に開発チーム内で「こういう課題があるよね」という課題の洗い出しをスプレッドシートで行って、「この課題ってチームで重要だよね」だったり「これって難しいから今できるの?」だったりを主幹のメンバーを集めて、どれをやろうというのをけっこう侃々諤々と話し合って、まぁ2~3日ですかね、けっこう長いこと話し合って決めています。

実際、そのプロセスの見直しで行った方法の対応事例です。1つがログ参照方法の改善ですね。「楽楽労務」はAWSを使っていますが、ログの参照がAWSのGUIでしかできなくて、毎回大量のログをAWSから手動でダウンロードして、かつ、ダウンロードしたあとに「目的のロゴどこなんだ?」というのを、もう1回検索するという手順を毎回踏んでいて、これがけっこう困難、検索するのが難しいという状況が、開発初期リリースから続いていました。

この改善として、AWSのログを定期的にサーバーへダウンロードして、当該サーバーにアクセスするだけで目的のログをすぐに検索できるようにする改善を行っています。さらに、AWS上ではログって細かく分割されたファイルになっていて「目的の日付のログはどこなんだろう?」「この分割されたファイルのどこなんだろう?」といった状態になるのですが、今はそれをサーバー上で結合して、その日付に対するログもすぐに検索できるような状況になっています。

では2つ目です。2つ目はリグレッションテスト運用方法の見直しです。これは現在絶賛僕がやっているところなのですが。手動のリグレッションテストを、これまでずっと開発プロセスとして行っているのですが、前回のバージョンと今回のバージョンを比較して「今回のバージョンの実装で既存の機能デグレていないよね?」というようなテストになります。

これがけっこう全機能を細かい観点でテストするようなことになっていて、工数がだいぶかかっているのですが、とはいえ、リグレッションテストでそんなにいっぱいバグが見つからなくて、「バグ検知率が低いのにメチャクチャ工数がかかるよね」ということで、絶賛課題になっているところです。

今やろうとしているのが、リグレッションテストで実施する観点の再定義です。今まで全機能に対してリグレッションテストをやってきましたが、「それって本当に必要なの?」という話しがあって、1回どういう観点でテストするのかを再定義して、無駄なテストをどんどんやらない方向に倒していきましょうというのが1つ目。

もう1つは「再定義した観点をE2Eテストで自動化できるんじゃないの?」という話もあるので、自動化できるのかどうかもちゃんと考えて、積極的に自動化していきましょうというのをちょうどやっているところです。

ざっと見てきたのですが、この3サイクルで、定期的に見直しをやっているところです。

「楽楽労務」の開発チームとしては、先送りしていることをずっと認識し続けるのが重要なのかな、と思っていて。ここを認識し続けないと、おそらくそのまま放置していることが当たり前になってしまうので。まずは定期的にちゃんと棚卸しを行って、先送りしていることを認識しないといけない、というのを重要視しています。なので、定期的なサイクルで、少しずつでも負債に向き合っていく必要があると思っています。

「楽楽労務」の負債との向き合い方

まとめると「楽楽労務」の負債との向き合い方は、開発速度を優先していかなければいけない状況というのは往々にしてあるので、その場合はやっぱりすぐに負債の解決をしていくのはなかなか難しいです。なので、戦略的に先送りにしていく負債も、それなりに溜まっていきます。

とはいえ、その状態を続けていくと負債の放置が当たり前になってしまって、もうどんどん負債が雪だるま式に積み重なっていっちゃうので、定期的なサイクルでも強制的に立ち止まって、ちょっとずつでも負債を解決する取り組みをするのが重要なんじゃないかなと、開発チームでは思っています。

最後になりますが、最初に「メンバーの90%ぐらいが負債があると思っていますよ」っていうアンケートがありましたが、じゃあ開発チームが実際今の負債の量をどう思っているのかも聞いてみました。

結果はこんな感じですね。「振り返ってみて、今の負債量をどう感じていますか?」。50パーセントはもっと少なくできたと思っているのですが、その半数ぐらいは今程度が最適なのかなと思っているというのがアンケート結果としてわかりました。

というわけで、負債について“少し”本気出して考えてわかったことは、ほぼすべてのメンバーが「まぁ負債はあるよね」とは思っているというのはわかりました。とはいえ、継続的な取り組みで徐々に解決することはできているので、半数のメンバーは最適な量なんじゃないかと考えているということもわかりました。

ただ、残りメンバーは「もっと少なくできた」と回答しているので、今後もしチームがスケールアップしてどんどんメンバーが増えていくと、おそらく次は少しじゃなく、本気出して考える必要が出てきそうだなということが、今回わかりました。

というわけで、負債との戦いはこれからも続いていきます。ご清聴ありがとうございました。

質疑応答

司会者1:ありがとうございました。

司会者2:はい。みなさんのコメントもいくつか来ていますね。

司会者1:「メンバー何人ぐらいなんだろう?」みたいなところは……。

岡本:はい。絶賛募集中です。

司会者2:絶賛募集中(笑)。当社の中の開発としても少ないチームではないということは言えるかなと思いますね。

司会者1:そうですね。

司会者2:ただ、ちょっと具体的な人数というところが言えなくて申しわけないんですけれども、絶賛募集中というところと、当社のいくつかあるプロダクトの中では、少なくないメンバーで開発をしているところまではお伝えできるかなというところですね。

司会者1:「TODO・FIXMEと見送りのコメントはコード内に記載しているんでしょうか? また、別の資料で管理しているのでしょうか?」というと質問がありますね。

岡本:基本的にTODO・FIXMEコメント自体はコード中に記載していて、一覧に関しては別の資料で管理しています。

司会者1:IntelliJとかですかね。FIXMEを拾って、それを別のところに転記をしているという。

岡本:はい、そうです。

司会者1:終わったらそのFIXMEとかTODOを消していくというような感じですね。

岡本:はい。

司会者2:「どこまで社内の共有の運用を行っていますか?」という質問が来ていますね。

岡本:社内の共有、負債に関してですかね? 技術的な何かですかね? 社内共有で言うと、開発チーム内では随時、1週間ごとのサイクルで開発を行っているので、1週間ごとのサイクルで各開発チームのメンバーで成果物を持ち寄って「これぐらい今進んでいます」みたいな共有は行っています。

司会者1:「見送り理由の明記などはどのように定着させていったのでしょうか?」。

岡本:まだやり始めて3年ぐらいなので、確かに最初の頃はそこまでやっていなかったです。いつぐらいだったかな。たぶん1年ぐらい前に「TODO・FIXMEコメントがちょっと多くなりすぎだよね」という時期が開発チームの中でもあって、そのときに「このTODO・FIXMEコメントって本当に今も有効なの?」というところがあったんですね。

要はコメント自体が古くなっていて、書かれているんだけど、そのコメントはもう正しくないよみたいな、そういう状況になったことがあって、「それってさすがにまずいんじゃないの?」という話になったので、もう仕組みとして機能開発の最後にはそれをやりましょうというルールにしてしまいました。

司会者1:なるほど、そういうふうな感じでどんどん定着をしていったということですね。

司会者2:先ほど、最後のアンケートの結果で、「負債量を適切と感じているのは、古参メンバーか新規メンバーですか?」、この割合で見るとどうですかというような質問があるのですが。古参メンバーのほうは「そんなに感じない」「最適」と思いながら、新規メンバーは意外に「これは負債だ」みたいなふうに思っている割合が高いのかみたいなところ、「実際どうですか?」みたいな質問で。

岡本:どうでしょう。たぶんそんなに違いはない気がしますね。古参の方も負債が多いと感じている方も一定数いるかと思います。

司会者2:じゃあこのそれぞれに古参と新規が半分半分という感じで捉えても差し支えないかなという量ですかね。

岡本:はい、そうですね。

司会者2:ありがとうございます。あと「負債解消のためにそれ用の工数というのは確保されていますか?」という質問ですね。

岡本:はい。これに関しては場合によりますが。バージョンサイクルと半期サイクルに関しては、けっこう長い目でやるので、完全に工数を取っています。単機能の開発サイクルに関しては、工数を取っているというよりも、開発の最後に近づいたときに1回棚卸しをして、開発のおしりに間に合うようであればやるというようにやっていますね。

司会者2:なるほど。最後、この質問だけちょっといいですか。「適切な負債だと感じているのは、負債が生まれた背景・妥当性がわかっているからってことですか?」という質問。「その背景を理解しているから妥当だということがわかっているのか?」という質問ですが、どうですか?

岡本:そうですね。なので、負債を見送るかどうかはだいたい古参メンバーがやっていることが多くて、見送っていいのかはチームのリーダーレベルがだいたい見ているかたちですね。

司会者2:判断しているということなんですね。

司会者1:はい。こういった取り組みは、なんかよくありますね。開発だと「いやー、その機能作るの大変なんですよ」ってなって、営業側からしたら「そんな簡単な機能なのにそんなかかるの?」みたいな感じで。負債が溜まってくると、最終的には顧客にいいサービスが届かないので、こういった仕組みでどんどん負債をなくして、効率いい開発をやるのはすばらしいなと思いました。

司会者2:Twitterのほうからも「改善しようとする動きがあるのはめっちゃいいな」という共感の声もいただいています。

司会者1:うん、そうですね。簡単にすぐバッて作ってしまいたいですからね。

司会者2:岡本さん、ありがとうございました。