読みづらいソースコードに潜むバグ|リファクタリングで改善

こんにちは。

私は、これまでに多くのシステム開発に携わってきました。

その中で、たくさんのプログラマが書いたソースコードを読んできました。

ソースコードの書き方は、プログラマによって様々です。

長いソースコードもよくあります。

でも、長いソースコードって、とても読みづらいんですよね。

なので、機能の追加が発生すると、コーディングがすごく大変なのです。

今回は、読みづらくなったソースコードを、読みやすくする方法について書いていきます。

読みづらいソースコードに潜むバグ

読みづらいソースコードとは、どのようなソースコードでしょうか?

それは、以下のようなソースコードです。

  • 1つの関数に、多くの処理が入っている。
  • 同じような処理が何度も登場する。
  • if文やfor文のネストが深い
  • 関数名や変数名がわかりづらい。

このようなソースコードは、読みづらく、潜在的なバグが含まれている可能性があります。

おそらく、コーディングした当初は、テストも十分に行っていると思います。

しかし、その後のバグ修正や機能の追加などで、テストに不十分な箇所が生まれ、結果として潜在的なバグが含まれてしまうのです。

このようなソースコードを整理することを、”リファクタリング”と言います。

リファクタリング (refactoring)

“リファクタリング”とは、「外部から見た動きは変えずに、プログラムの内部構造を整理すること」です。

つまり、関数を呼び出した時の挙動は一緒だけど、ソースコードを見ると、関数の内部は整理されていると言うことです。

ここで大事なのが、「外部から見た動きは変えずに」というところです。

外部から見た動きが変わってしまうと、関数の呼び出し元も変更する必要があります。

「壊れていない物を修理するな」と思う人もいるかもしれません。

私も昔は、「壊れていない(正常に動いている)のだから、プログラムを修正する必要ない」と考えていました。

しかし、機能の追加でデグレやバグが頻発してしまいました。

原因は、プログラムの内部構造が整理されていなかったからです。

なので、リファクタリングは、プログラムにとって必要なのです。

メリット

リファクタリングを行うメリットは、以下の3つです。

  • 設計の劣化を防ぐ
  • コードを理解しやすくする
  • 修正や機能の追加を早くする

設計の劣化を防ぐ

開発をしている時は、設計方針にしたがい、きれいにコーディングされていると思います。

しかし、改修や機能の追加が重なると、ソースコードは劣化していきます。

劣化したソースコードは、改修や機能の追加が難しくなっていきます。

リファクタリングは、そのような劣化したソースコードを改善する効果があります。

コードを理解しやすくする

また、劣化したソースコードは、処理の理解が難しくなっています。

そのため、新しく参入するプログラマには、処理を理解するのに時間がかかってしまいます。

リファクタリングすることで、理解しやすいソースコードに整理することができます。

修正や機能の追加を早くする

そして、リファクタリングされたソースコードは、修正や機能の追加がしやすくなります。

そのため、修正や機能の追加にかかる時間が少なくてすみます。

デメリット

リファクタリングをするデメリットは、以下の2つがあります。

  • 工数がかかる
  • デグレが発生する可能性

工数がかかる

リファクタリングには、工数(時間)がかかります。

「外部から見た動きは変えない」ので、サービスを利用する顧客から予算をいただくことが難しいです。

デグレが発生する可能性

「プログラムの内部構造を整理する」ために、処理が共通化されてしまい、結果、デグレが発生することもあります。

長年、改修と機能の追加が行われてきたプログラムには、共通化できない動きもあります。

そのような仕様が引き継がれていない場合に、デグレとなることが多いです。

リファクタリングの方法

リファクタリングの方法は、いろいろあると思います。

私は、よく以下の順序でリファクタリングを行っています。

  1. 関数や変数の名前をわかりやすくする
  2. 使用されていない処理を削除する
  3. 重複した処理をまとめる
  4. ネストを浅くする

関数や変数の名前をわかりやすくする

まずは、関数や変数の名前をわかりやすくしていきます。

関数や変数の名前の付け方は、人それぞれです。

そのため、多くのプログラマが関わってくると、関数や変数の名前が分かりづらくなってきます。

なので、分かりやすい関数名や変数名に変えます。

こんな時に命名規則があると、ブレが少なくなります。

使用されていない処理を削除する

改修や機能の追加が長年行われていると、使用されなくなった処理があります。

何かあった時のためにと、過去の処理をコメントアウトして取っておくことはよくあります。

しかし、コメントアウトされた処理が多くなると、ソースコードが読みづらくなってしまいます。

なので、リファクタリングのタイミングで削除してしまいましょう。

重複した処理をまとめる

同じ処理が何度も出てくると、改修で漏れが発生する可能性があります。

なので、同じ処理は関数にして、その関数を呼び出すようにします。

この時、共通の処理は、クラスにまとめられないかを検討します。

基底クラスにすることで、ソースコードが、より分かりやすくなります。

ネストを浅くする

ネストが深い処理は、不具合の原因となります。

なぜなら、ネストが深くなることで、処理のパターンが増えるからです。

なので、ネストをなるべく浅くするようにします。

私の個人的な意見を言うと、ネストは2つまでが言いと考えています。

タイミング

では、リファクタリングを行うタイミングはいつでしょうか?

リファクタリングするタイミングは、以下になります。

  • 同じ作業を3回繰り返した時
  • 機能を追加する時
  • バグ修正の時

同じ作業を3回繰り返した時

コーディングしている時に、同じ処理が出てくることは、よくあります。

もしも、同じ処理が3回出てきた時は、リファクタリングした方が良いでしょう。

同じ処理が3回出てくると言うことは、今後、何度も同じ処理が出てくる可能性があります。

なので、その時点で、リファクタリングできないかを検討しましょう。

機能を追加する時

機能の追加が発生したときも、リファクタリングする良いタイミングです。

処理が整理されていた方が、機能の追加は、しやすくなります。

ただし、機能の追加を行いながら、リファクタリングを行うことは、避けた方が良いです。

なぜなら、不具合の原因を切り分けるのが難しくなるからです。

なので、リファクタリングは、機能を追加する前に行うようにしましょう。

バグ修正の時

バグの修正をする時も、リファクタリングの良いタイミングです。

バグの修正で、デグレが発生することは良くあります。

そのような、デグレを発生させないためにも、リファクタリングした方が良いでしょう。

しかし、この場合も、バグ修正の前にリファクタリングを行うようにしましょう。

理由は、機能を追加する時と同じで、不具合が発生した場合、原因を切り分けるのが難しくなるからです。

注意点

リファクタリングを行うときの注意点は、以下の3つです。

  • バックアップを取っておく
  • 小さな規模で行う
  • テストを十分に行う

バックアップを取っておく

リファクタリングを行う時は、必ずバックアップを取っておくようにしましょう。

元の処理を見直す時や、リファクタリングに失敗した場合に、戻す必要があるためです。

私がよく使うのは、gitと言うバージョン管理ツールです。

gitを使う場合は、こまめにローカルにコミットするようにします。

そうすることで、ソースコードを戻す必要がある時でも対応できます。

小さな規模で行う

リファクタリングは、できるだけ小さい範囲で行いましょう。

ある程度、理解しているソースコードの場合、大きくリファクタリングを行いたくなります。

しかし、大きくリファクタリングを行うと、デグレが発生する可能性が高いのです。

また、大きくリファクタリングを行ってしまうと、テストに時間がかかってしまいます。

なので、できるだけ小さい範囲でリファクタリングするようにしましょう。

テストを十分に行う

そして、リファクタリングを行った場合は、必ずテストを実行し、既存の処理と同じかを確認するようにしましょう。

1箇所リファクタリングを行ったら、そのテストを行い、動作が変わっていないかを確認するようにするのがベストです。

テストには、RPAやUnitテストを使用すると、同じテストを繰り返し行うことができます。

まとめ

今回は、読みづらいソースコードに潜むバグを防ぐ方法として、リファクタリングを紹介しました。

リファクタリングは、「外部から見た動きは変えずに、プログラムの内部構造を整理すること」です。

そのメリットとしては、以下の3つがあります。

  • 設計の劣化を防ぐ
  • コードを理解しやすくする
  • 修正、機能追加を早くする

そして、デメリットとしては、以下の2つがあります。

  • 工数がかかる
  • デグレが発生する可能性

リファクタリングの方法は、いろいろありますが、私は、経験より以下の順番で行っています。

  1. 関数や変数の名前をわかりやすくする
  2. 使用されていない処理を削除する
  3. 重複した処理をまとめる
  4. ネストを浅くする

リファクタリングを行うタイミングは、以下になります。

  • 同じ作業を3回繰り返した時
  • 機能を追加する時
  • バグ修正の時

ただし、機能の追加やバグの修正の前に、リファクタリングを行うようにしましょう。

リファクタリングを行う際の注意点は以下になります。

  • バックアップを取っておく
  • 小さな規模で行う
  • テストを十分に行う

読みづらいソースコードには、必ずと言って良いほど、バグが潜んでいます。

長期でシステムを運用するには、タイミングを見てリファクタリングを行うようにしましょう。

では、今日はこの辺で。

コメント

タイトルとURLをコピーしました