目次
良いソースコードとは?
IT業界でプログラマやシステムエンジニアとして、18年間働いていて「良いソースコード」って何だろう?と考えることがたまにあります。
システム開発はたくさんの人が関わります。システムの規模にもよりますが、1つのシステムに対して多くの人が開発を行います。コードの書き方は人それぞれで
「この人のコードは見やすいな」
「この人のコードは見にくいな・・・」
とさまざまな書き方をする人がいます。
現場によってはコーディング規約があり、ある程度の書き方は統一されますが、コーディング規約はあくまでも基本的なルールであり、コーディング規約があっても、書き方は人それぞれです。
ソースコードの書き方や見易さについては「十人十色」ですが、私個人が考える「良いソースコード」とは
シンプルに作られているコード
だと思うんですよね。高スキルの人達しかコードを書かないのであれば、分かりづらくても高性能な作りで良いと思います。
しかし、実際はプログラミングの経験が浅い人から、経験値の高い人までたくさんの人がコードを書きます。
昔はパソコンの性能が低かったため、高性能な作りが必要でしたが、今はパソコンの性能が良いため、そこまで高性能な作りにこだわる必要はありません。(ただし、SQLに関してはしっかりと性能検証が必要。また大量なデータを処理するようなシステムでは、性能を考えた作りが必要)
新しいプログラミング言語「Go言語」は、言語仕様が非常にシンプルです。
C言語から派生してJavaやJavaScript、C#、PHP、Python、Objective-Cなど多くのプログラミング言語が作られ、更にGo言語やSwift、Kotlinなど、プログラミング言語は昔に比べ簡潔でシンプルになってきているように感じています。
機能が多い方が高性能な作りができるかもしれません、ただ機能が多いとソースコードが複雑化したり、肥大化したり、複雑な機能が正しく使われなかったりと「結局使いこなせない」「間違って使われる」などの問題が発生します。
シンプルな作りにすることで、可読性が高いコード(経験値の低い人でも分かりやすくなる)になり、生産性向上に繋がるのではないでしょうか。
良いコードを書くためのコツ
変数やメソッドの命名は分かりやすく
プログラミングする際の基本中の基本であるネーミング。これが意外にできてない人が多いんですよね。
例えば、変数名を「num」や「flag」のようにつけているコードを見かけたことがある人も多いのではないでしょうか。
private int num;
private boolean flag;
こう書かれると「numって何の数値をあらわしている?」「flagって何のフラグ?」となり、コードを解析する必要があります。
また「num2」とか・・・2号が現れたります。「この2号は何者?」
変数名やメソッド名は次のように意味が分かるような命名にすることが大切です。
private int carNumber;
private boolean isOpen;
メソッドのコード量、部品化を意識する
続いてはメソッド内のコード量です。
1つのメソッドに大量コードを書いているソースを見たことがある人も多いと思います。これも結構いるんですよね。本当見づらい・・・。
C言語以降のプログラミング言語のほとんどがオブジェクト指向のプログラミング言語です。
オブジェクト指向とは簡潔に説明すると、機能を部品化し、その部品を組み合わせてプログラムを完成させる考え方のことです。
1つのメソッドに処理を長々と書くのはよくありません。メソッドも部品化するべきであり、しっかりとした役割を持たせるべきです。
例えば、次のようなメソッドがあるとします。このメソッドには次の3つの機能が入っています。
- 中古車の情報を取得する
- 新車の情報を取得する
- 中古車と新車の情報を比較する
※説明のため、日本語で書いてます。
メソッド() {
中古車の情報を取得するための入力値を作る
中古車の情報を取得する
取得した中古車の情報を格納する
新車の情報を取得するための入力値作る
新車の情報を取得する
取得した新車の情報を格納する
中古車の情報と新車の情報を比較する
比較ロジック・・・・
}
この3つの機能をメソッド化すれば、次のようなコードを書くことができます。
処理実行メソッド() {
中古車情報取得メソッド();
新車情報取得メソッド();
中古車と新車を比較するメソッド();
}
中古車情報取得メソッド() {
中古車の情報を取得するための入力値を作る
中古車の情報を取得する
取得した中古車の情報を格納する
}
新車情報取得メソッド() {
新車の情報を取得するための入力値作る
新車の情報を取得する
取得した新車の情報を格納する
}
中古車と新車を比較するメソッド() {
比較ロジック・・・・
}
3つの機能を部品化することで、もし他の機能が「中古車情報取得」の処理が必要になったとき、「中古車情報取得メソッド」を使うことができます。(部品化しておけば、使いまわすことができ、同じコードを複数書く必要がなくなる)
また、メソッドのコード量が少なくなると可読性があがります。機能がごちゃ混ぜのメソッドより、○○機能と機能ごとにメソッドが分けられている方が見やすく、メンテナンスもしやすいです。
異常系の動作を意識する
バグが少ない人とバグが多い人の違いは「異常系を意識したコード作りができているか」だと言っても過言ではありません。
正常系の処理を作るのは当たり前です。重要なのは「異常系をどこまで考慮して作れているか」です。
例えば
- if文やfor文で使用している変数がNULLだったとき、どう動きますか?
- try-catchは適切な場所で出来ていますか?
- ログは適切に出力できていますか?
- トランザクション管理を意識した作りになっていますか?
- データベース仕様やシステムの仕様をある程度理解してコーディングできてますか?
ソースレビューをしていると、この変数がNULLだったときどうなりますか?と質問すると、答えれずに固まる人や見当違いな答えを言う人など、「考えてなかった」という人が意外に多いです。
正常系を作るのは当たり前です。リリース後に正常系がまともに動かないことはまずないです。だいたいが開発者にとって想定外な動きをしたときに問題が発生します。
異常系を意識したコード作りができていない人の場合、この想定外な動きに対応できておらず、試験(結合試験、総合試験、シナリオテストなど)やリリース後にバグとしてあがってくることが多いです。
仕様書に書いてあることを、コーディングするだけではなく、異常系のことを意識しながらコーディングすることが大切です。
エラーが発生する可能性のある個所を確認し、エラーが発生したとき、どう動くべきか。それをしっかりと検討しながらコードを書いていれば品質の高いソースコードが書けるはずです。
性能を意識して作る
最後は「性能を意識した作りをする」です。
よくあるのが「開発時は普通に動いていたがリリース後少ししたら、処理が遅くなった」というパターンです。性能を意識した作りができておらず、また負荷試験をしっかりやっていなかったことが原因です。
設計やコーディングする際に、どのくらいの数を想定しているシステムなのかをしっかりと把握することが大切です。
例えば
- 検索画面:100件であれば高速に動くが、10000件になると処理が一気に遅くなる。
- 登録するバッチ:100件であれば高速に動くが、10000件になると一気に遅くなる。
- 大量データの処理:100件であれば高速に動くが、10000件になると「Out Of Memory」のエラーが発生する。
などなど
SQLであれば、インデックスは適切に張られているか、SQLの発行回数を抑えた作りになっているか、実行計画を確認しSQL文が適切か。などしっかりと検討が必要です。
Out Of Memoryが発生する場合は、メモリにデータを保持しすぎていないか、例えば、10万件のデータをメモリに保持しているのであれば、1000件や1万件ずつ処理する仕組みに変えるなど、環境に合った作りに変える必要があります。
また、無駄にfor文の中にfor文を何個も作って、ループ件数がすごいことになっていないか。whileで無限ループを作っていないかなど確認が必要です。
負荷試験は大切な試験です。システムが想定している件数を把握し、その件数に耐えられるか事前に確認することが大切です。