ichy’s diary

バイクとか、写真とか、アジャイルとか

リファクタリングをする勇気を育むために

リファクタリングというのは、動作を変えずにプロダクトやソフトウェアの内部構造を整理・変更する行為を指します。この作業は実際にプロダクトを作っているエンジニアにとってはとても重要であり、自らの生産性を改善や、プロダクトのエンジニアリングの認知不可を低減するために行われる作業ですが、そのリファクタリングの特性によって、実施するまでに様々な困難が待ち受けていると思います。今回はその困難を少しでも減らす方法を考えてみたいと思います。なお、リファクタリングを行うことそのものの困難さは主に技術的な問題であったり、手間の話が多いです。その点に関しては個別具体なエンジニアリング手法が多いのでこの文章では取り上げません。この文章の関心範囲はリファクタリングを行うということを決定するまでのプロセスです。

Abstract

  • すべての仕事で「この仕事をすることでどうなるか」と「この仕事を実施しなければどのような状況になりますか」という、0からプラスへ、マイナスから0への観点の質問をすべての仕事で明らかにする。
  • リファクタリングした仕事はその結果が発揮できるタイミングが必ずあるのでそれを明らかにしよう。
  • 期日や目標日を決める必要がある新規機能開発がある場合は、期日や目標日を使わず、予測日でステークホルダーに状況を伝える

人間の意思は基本的に弱い(と思う)

プロダクトやソフトウェアを成長させていくと、早かれ遅かれ初期の設計からは想定していなかった問題が起こるのは誰しも経験あることです。可能であれば、その問題の原因である設計まで戻ってやり直したいと思うこともあるかもしれませんが(世の中には3回作り直すということをやっているチームもあると聞いたことがあります)、現実的にそれを行うのはビジネス上の問題や、モチベーション、作業のコストパフォーマンスを考えてやらない選択肢をとることが多いです。やらない問題に目をつぶって前に進むこともできますが、多くの場合はリファクタリング課題としてどこかしらに記録されており、例えばプロダクトバックログに置くことが多いと思います。そして、とにかく今は機能開発をすることが重要なので、一旦ここは目をつぶって、あとでなおそうと気持ちを切り替えます。

さて、更にプロダクトやソフトウェアを成長させていくことで様々な期待が寄せられることになり、要望や、バグ修正などが多く寄せられ、チームは初期の構築段階同様に様々な作業に取り組むことになります。様々な改修が施され、まさに「全速力で走っている蒸気機関車を走りながら改善・修正」する状態です。そこで更に「ここが少しこうなってると直しやすいんだけどなぁ」となるようなリファクタリング課題が見つかることになります。しかし、今は今使っているお客さんのためにもバグの修正を優先したり、次の目標にもなるべく近づいておきたいから一旦おいといて、目標が達成したらしっかりと時間をとってなおすぞ!という気持ちになります。

さて一旦機能開発が落ち着きました。今こそリファクタリングをするチャンスです。とおもったら、次の機能開発の案件が入ってきました。バグの報告もちょこちょこ(もしかしたらいっぱい?)あったりします。開発中に出てきた優先度の低いバグをサクッと直したい気持ちもあるかもしれません。とにかくお客さんが困ってるし、少なくとも再現性のあるバグは直しておきたいよね。あと新しい機能の設計もじっくり取り組んで、機能開発までに設計に自信をもちたい、そんなことを考えてしまうと、なかなかリファクタリングに取り組むことができません。

そこで妙案を考えます。「そうだ、もうこう考えてもリファクタリングには全然取り組めないんだから、週に20%はリファクタリングとバグの修正をするための保守時間としてちゃんと確保しよう。だいたい週5日稼働日があるなら1日ぐらいはそういう時間をとってもいいよね」と。早速上司やPdMと交渉に行きます。大体の場合は「あーそれくらいの時間ならまあだいじょうぶか。その分余ったら機能の開発にも回せるし、生産性も上がりそうだな」とかそんな感じでしょうか。

さて、明確に時間を分けたおかげで問題は解決し、はれて素晴らしいリファクタリングライフの幕開けです!頑張っていきましょう!

ってわけにはいかないことが多いです。もしこれで解決できているチームがあればこの先は不要になりますが、そうならなかったチームであればこの先を読む価値は少しあるかもしれません。

ついに明確に分けられた時間を使っていざ取り組もうと思いますが、時間を分けただけで、この枠ではバグとリファクタリング、そして保守作業を一緒に取り組むことになります。もちろんバグがなくなったわけではないので依然としてバグの修正をやることになり、バグが終わったらリファクタリングをやろうとなりますが、その間に何かしらの依存しているものがEOLを迎えるなどして、また違う作業が発生したりします。そして時間を区切ったことでバグやEOL対応などが終わり切ることなく、来週、そのまた来週みたいにどんどんリファクタリングの予定がずれていきます。

時間を分ける前にできることがあるのではないか

時間を分けるという解決策は、目的に追われているチームの目的に注力する時間を強制的に削ることで、目的外のこと、とりわけエンジニアにとって関心の強い物事に関する優先度をエンジニア自身で判断し実行したいという思いを実現するために生まれたものかもしれません。そこに保守作業や、バグ対応を含めるのは一種の罪滅ぼしや、バランスを取っている、という感覚が強いように感じます。もちろん目的のための機能開発、ユーザーのためのバグ修正、運用のためのEOL対応、これらはすべてとても重要です。どれを欠いても遅かれ早かれプロダクトを生かし続けることは難しいでしょう。しかしリファクタリングはどうなんでしょうか。今までの話ではリファクタリングはこれらの要素に比べたら優先度の低い、「やれればいいや」ということなのでしょうか。プロダクトを生かし続ける原動力の根本はそれを運用する人にあると考えることはできないでしょうか。人の行為に対する障害を取り除くことは機能開発やバグ修正、運用改善に劣る優先度でしょうか。

時間を分けるという行為は、優先順位をすっ飛ばして行動することに近いと言えます。現在の優先順位では下にあるものを引き出して実施する行為だからです。しかし、その時間を分けた中でも優先順位付けが行われているのであれば、その目的は達成できていない状態です。優先順位は物事を行う上で並行で実施できない状況であれば必ず欠かすことのできない尺度でしょう。であればそもそもの問題は優先順位付けにあるといえるのじゃないでしょうか。

どうリファクタリングの優先順位をあげ、実施する勇気が育まれるのか

よくある優先順位付けとして、新規機能開発、バグ、運用、リファクタリングそれぞれのカテゴリ別で優先順位を決め、プランニングである一定の割合で各カテゴリから課題をもってくるという方法が考えられます。しかしこれには難しい点がありそうです。まず、そのような形で運用してしまうと、新規機能開発やバグが多くの割合を占め、リファクタリングの課題を「一時的に」という理由で0にできてしまうのがあります。強い意志でリファクタリングの課題を必ず一定入れるという意思決定ができるのであればおそらく問題はないでしょうが、特になにかの締め切りが近くなってくるとこの「一時的に」という言葉が頻繁に使われるでしょう。それがいつしかリファクタリングは入れられるなら入れるぐらいのおまけとしての扱いにして良いんだという認識になり、そして永遠に実施されないでしょう。もう一つの難しい問題は、せっかく付けたリファクタリングの優先順位が無意味になってしまう可能性です。リファクタリングの優先順位を決めておくのは一見良さそうに見えますが、リファクタリングの効果が発揮できるタイミングはその優先順位で並べられる順番では発生しないことが多いでしょう。リファクタリングは定常的な運用の場合にも効果を発揮しますが、リファクタリングによって変更したコードや機能に手を入れるときにも効果を発揮します。つまり、新規機能開発やバグ、運用による開発・改修を実施する前に行うことが重要になります。これは、リファクタリング課題の集めて眺めても決められることではなく、リファクタリング課題に関連する開発を一緒に見てあげる必要があります。以上のようなリファクタリングの特性により、リファクタリング課題だけで優先順位を決めて一定のリソース枠で実施することは難しい印象があります。

リファクタリング課題同士の優先順位付けは意味をなさなく、リファクタリングは関連する開発によって優先順位を決めることができるというベースで立脚するのであれば、リファクタリングを含めたすべての仕事と統一的な優先順位を判断する尺度が必要になります。この尺度は仕事の質の例外なく決定される尺度で有ることが望ましいと考えます。新規機能開発も、バグも、運用も、リファクタリングもすべてを一つのものさしで当てはめるのです。しかし物事はそんなに簡単に考えることを許しません。わかりやすい話で言えば、新規機能開発は多くの場合、スコープと期日が決定されています。この決定されている事項が新規機能開発を最優先にさせる根本的な原動力になっています。CoD(Cost of Delay: 遅延コスト)がわかりやすいですが、このCoDが小さいほどよいので新規機能は素早く出す必要があります。また、バグはユーザーの評価に影響します。バグに依るユーザーの行動のブロッキングによって発生してしまうユーザー自身の機会損失は、人間が何かを提供していると自覚していればとても強く意識してしまうでしょう。運用もEOLという期限によってプロダクトやソフトウェアが危険にさらされるという明確にわかりやすい指標があるのです。リファクタリングの問題はここにわかりやすい指標がないことにあります。なんとなく、開発しやすくなるであるとか、ちょっと早くなるとか、そういう弱い根拠を使っている状況であれば、優先順位を決める話し合いにあげる勇気さえない状況です。

優先順位を決定するときこれらの何をみて決定しているのでしょうか。チームによって違いますし、違う尺度で書かれているので多少強引に決めている部分もあるとは思います。もしくは駆け引きやギブアンドテイクで決めているところもあるかもしれません。しかし、多くの場合において「この仕事をすることでどうなるか」という質問のやり取りはやっていると思います。それは例えば、ユーザーストーリーや、受け入れ条件などでも表現されているかと思います。リファクタリングが難しいのは、この質問に対する答えが弱いところです。もちろん答えることはできますが、リファクタリングは基本的にマイナスを0に戻すような内容が多いのです。であれば、すべての仕事の内容に対して、「この仕事を実施しなければどのような状況になりますか」という質問をしてみると良いかもしれません。まず、リファクタリングを含めたすべての課題で共通して答えられる質問を用意し、記録することが重要になります。どの仕事も、この2つの質問は必ず答えられるはずです。そこから具体的な根拠を考えてみても良いのではないでしょうか。その上で、そのリファクタリングがどのようになったら効果を発揮するかを明確にしましょう。これはリファクタリングを行うタイミングを決める上で重要な情報になります。これがなければ、「リファクタリングはいつかやるもの」という状態でズルズルと後ろにずれていきます。

そして、特に期日や目標日を決める必要がある新規機能開発がある場合は、期日や目標日を使わず、予測日でステークホルダーに状況を伝える必要があります。最初の方にも述べてますが、新規機能開発を行っているときでさえ、リファクタリング課題は積まれるのです。しかし、期日や目標日が決められてしまっては、リファクタリングの時間あれば新規機能開発を強行して安心感を得ようとします。自動車を組み立てたあとにネジを改善するような行為はやめて適切にリファクタリングを施す必要があります。当然予測日はずれる可能性があるわけです。その予測日がずれることで、ステークホルダーからスコープ調整や、リファクタリングを行わなかったときのリスクを説明するチャンスを得るようにしましょう。プロダクト開発は不確実性に富み、設計は漸進的であり、固定的でないこと、固定的な設計で発生するリスクを繰り返しステークホルダーに伝えましょう。プロダクトは最初の構築の段階より運用される時間のほうが圧倒的に長いのです。(このあたりは その品質は最初から間違っている! | ドクセル がとてもおもしろいです。)スクラム開発であればこれはスプリントレビューで実施することが望ましいです。