プログラマが知るべき97のこと/リファクタリングの際に注意すべきこと
私達プログラマには必ず、既存のコードの「リファクタリング」が必要になる時がやってきます。ただ、リファクタリングをする前にいくつか考えてほしいことがあります。次に書くようなことに注意すれば、自分を含め、開発に関わる全ての人の時間と労力を大幅に節約できるでしょう。
リファクタリングするにあたってはじめにすべきことは、既存のコードベースと、そのコードに対して書かれたテストコードの洗い直しです。具体的に、現状での良い点、悪い点、強み、弱みを1つずつ確認していきます。これは、良い点、強みを残しながら、悪い点、弱みを克服することにつながります。既存のシステムに手を加えれば、必ず元より良い物になるはずと考えがちですが、実は何も良くならないこともあるし、もとより悪くなることもあり得るのです。既存のコード、テストを十分に検証しなければ、過去の失敗に学ぶことが出来ません。
すべてをゼロから書き直したい衝動に駆られることもありますが、その誘惑に打ち克たなければなりません。既存のコードをできるかぎり活かすべきです。いかに醜悪なコードであっても、そのコードはテストやレビューを通っているものなのです。既存のコード(特に、すでにリリースされていたシステムのコード)をすべて破棄するというのは、それまでの何ヶ月(何年)という時間を捨ててしまうということを意味します。大変な作業を経て、曲がりなりにも形にしてきたコードです。その過程では無数に発生した問題の回避策を講じ、数えきれないほどのバグ修正もしてきたでしょう。仮にコードを新たにゼロから書き直したとすると、同じようなことをまた繰り返すことになりますし、既存のコードでは発見/修正できたバグを、今度は見逃してしまうかもしれません。これでは時間と労力の無駄だし、過去の作業で得た知識も無駄になってしまいます。
一度に大幅な変更を加えるよりも、少しずつの変更(インクリメンタルな変更)を数多くするべきです。インクリメンタルな変更ならば、テストからのフィードバックを得ることで、変更がシステムへ及ぼす影響を容易に知ることが出来ます。変更を加えたらテストが百個以上も失敗する、というのは非常につらいものです。いらだちと焦りから、誤った意思決定をしてしまう恐れもあります。失敗するテストが2つや3つならば、冷静に確実な対処ができるでしょう。
各イテレーションの最後には、既存のテストが通るか必ず確認します。既存のテストだけでは、変更を加えた部分をカバーするのに充分でない場合は、新たにテストを追加します。十分な検討もせずに、古いコードに対応するテストを破棄してはいけません。古いテストの中には、一見すると、変更後のコードには合わないのでは、と思えるものも確かにあります。しかし破棄する前に、そもそもなぜそのテストが存在したのかを深く掘り下げて検討する必要があります。
個人の好みやエゴを入れてはいけません。壊れてもいないものを直すのは無意味です。コードの書き方の流儀や構造が自分の好みに合わないというのは、修正の十分な理由にはなりません。自分のほうが前任者よりも能力があるのだから良いコードが書けるはず、というのも十分な理由にはなりません。
新技術を使いたい、というのもそれだけではリファクタリングの十分な理由にはなりません。最悪なのは「今流行の新技術が取り入れられていない」「時代遅れである」というだけのりゆうでリファクタリングをする、ということです。新しい言語やフレームワークを使えば、今と同じことがもっとうまくやれるだろう、というような単純な考えでリファクタリングをすべきではありません。コストの分析を行い、新しい言語やフレームワークの導入により、機能、保守性、生産性が著しく向上するという結論が得られた場合を除き、現状の技術のままにしておくのが得策です。
人間は必ずミスをする、ということを常に忘れないようにしましょう。コードを書き換えてもコードがもとより良くなるとは限りません。(何かミスをして、かえって質が下がることもありえます。実際、私は何度かコードの書き換えに失敗しています。情けないことではありますが、それが人間というものです。)