どうも!hoge太郎です。
不定期投稿シリーズ「開発を進めていく上で気をつけるべき事項」その3です。

前回の投稿からいつの間にか4年も経過してましたwww

しかし今でも自分がブログに書きたいと思える内容は変わっていませんでした。
ということでやっていきましょう。


目次

  1. ドキュメントとソースで異なる名前をつけない
  2. 名前は省略しない
  3. 速度より可読性を重視する
  4. 同じコードを書かない (関数化)
  5. 役割を分担する (クラス化)
  6. 仕様による制約をコードで表現する (より良いインターフェース設計)
  7. 何でも出来る→これしか出来ない
  8. 例外処理をちゃんと書く
  9. ログ出力にも設計を
  10. リリース作業は簡単に
  11. 環境変数の勧め
  12. まとめ

速度より可読性を重視する

あなたはどっち派ですか?

  1. 処理速度が少しでも落ちそうなら可読性を犠牲にする
  2. 可読性が少しでも落ちそうなら速度を犠牲にする

これまで本シリーズを読んできた方なら分かると思いますが、私は明らかに後者です。
本シリーズでは宗教めいた話も出てくるので明確に反対意見を持っている方は適当に読み飛ばしてください。

と言っても具体的な実例があまり思い当たらないのですが、これまで出会った他のエンジニアと違った考えを挙げると

  1. クラス化、メソッド化するオーバーヘッドを気にするよりも見通しの良さを優先する
  2. ビューの階層深度を気にし過ぎない

くらいですかね。
あとは亜種として以下もあるかな。

  1. DBでフラグやステータスを表すカラムにはtinyintよりもchar型、MySQLならenum型を採用する

一つずつ見ていきましょう。

 

クラス化、メソッド化するオーバーヘッドを気にするよりも見通しの良さを優先する

このような議論があります。
https://teratail.com/questions/19948

例えばこういうコードがあったら、

function process() {
    // 処理A
    ...

    // 処理B
    ...
    ...

    // 処理C
    ...
    ...
    ...
}

私の場合はこう書きたいと思います。

function process() {
    processA();
    processB();
    processC();
}

function processA() {
    ...
}

function processB() {
    ...
}

function processC() {
    ...
}

もちろんメソッド名は適切な名前(コメントに書いていた事を端的に表現する)にします。
そして何でもかんでもメソッド化する訳でもないです。

最初に貼ったリンク先での議論では1度しか書かない処理なら〜とか、
直接記述されていた方が分かりやすい場合はしないなど様々な意見が見られますが、
私の中の基準はフローチャートや設計書などに登場する処理の単位でメソッド化するです。
リンク先の中では意味のかたまり毎にメソッド化するという方に近い気がします。
(それが正しいかはともかくとして)

そして、このように書きたいが為に、どうすればこう書けるのかという事を検討します。

例えばそのスコープ内の変数を一連の処理で使いたい場合など、
メソッドに分割するとどう書けばいいのか直ぐには分からない事もあるでしょう。
そういう時にどうすればいいのかを調べたり考えたりして書きたいように書く方法を覚えて行きます。

この作業が無駄だと感じるエンジニアが多い気がします。
ときにはクラスやメソッドを呼び出す形にするとオーバーヘッドが掛かるので遅くなるという人もいます。
もちろんそのレベルのオーバーヘッドすら気にしないと行けない事だってあるでしょう。
しかし多くの場合はもっと致命的なボトルネックがあると思います。
処理速度を気にするのは負荷試験を実施して必要だと感じてからで十分だと思います。

極論を言えば最初のコードでも次のコードでも正しく動いていれば問題はないのですが、
次のステップとしてどのように書くのがより望ましいのかというテーマがあって、
その軸の一つに可読性や保守性、実行速度があります。

まだそこを気にするほど経験値がない駆け出しのエンジニアは良いとして、
ある程度の経験があるエンジニアがそこを追求していなかった時の言い訳として
速度が遅くなると言っている気がしてモヤモヤするのです。

もしかするとそういった実装をしたエンジニアにとっては意味のある単位での分割をした結果かもしれません。
同様に私が適切だと思って実装した物も、他のエンジニアが見たら理解しにくい可能性もあります。

なので設計書などで意味のある単位を明確に定義して共通認識としておくと良いと思うのと、
そうなった世界であればそこに定義された単位で分割されていれば理解しやすいコードになっているはずという考えです。


次にビューの階層深度を気にし過ぎないですが、HTMLやiOS、Androidアプリでビューを構築する際に
ビューグループ(子を持てるビュー)を出来るだけ使わない事を美徳とする人を偶に見かけます。

意味的には一つの概念の中に閉じている物であっても、
無理やりスタイルや相対配置などを駆使する事で同様の見た目を作る事が出来る場合、
階層を一つ減らせたと満足している人がいるのです。

例えば上のようなビューを構築する時に、
画像と右側に並ぶ要素を一つのビューグループに入れるかどうか、
ユーザー名とアイコンを一つのビューグループに入れるかどうか、
シェアボタンやリツイートボタンなどを一つのビューグループに入れるかどうかが人によってまちまちだったりします。
更にはこういうビューが縦にいくつも並ぶタイムラインもリストではなくビューを並列に並べるという実装を見かけたりします。

単純に実装方法が分かっていなかったとしても、
そう実装する方が明らかに難しいでしょと思うような無理矢理実装を目にする事が結構多いのです。
そしてそういう実装は大抵何らかの不具合を抱えているのです・・・

そういう時にビューの階層は出来るだけ減らせを誤解しているのではないか、と思ったりします。
もし本当に誤解している人がいるなら、そんなハック的な知識を得るよりも先に正しい実装を覚えてくれと思います。

ビューの階層が無駄に多ければ描画の負荷が上がるのは事実です。
なので無駄に包みまくるのは確かに悪です。
しかしそういう実装はあまり見かけないし、意味のある単位で包む事は悪ではありません。

先に述べたメソッドを意味のある単位で分割するのと同じで
適切に分離された要素は展開しやすく、操作もしやすく保守性が高まります。

ハック的な実装が必要になるのは画面が表示されるのが遅いと感じてからで十分です。
そしてそれが必要でそうしているのであれば、ちゃんとコメントなりドキュメントで理由を書きましょう。
それよりも先に仕様を変える事を検討した方が良いと思います。


DBでフラグやステータスを表すカラムにはtinyintよりもchar型、MySQLならenum型を採用する

 

皆さんはdelete_flgなどのカラムにどのような型を採用しますか?

一旦脱線しますが、私は過去に
フラグを多用するのは良くない、フラグで表現したい物は大抵状態だと耳にした事があり、
確かになと納得して以降statusをよく使うようになったのですが、
周りのエンジニアにはあまり浸透していないようですw

フラグ多用の是非については以下が見つかりました。
https://www.deep-rain.com/programming/program/747

流し読みですが私の考えと近い人が書いている気がします。

話を本筋に戻すと、私の周囲ではよくdelete_flgの型にtinyintが採用されています。
昔からの慣習という印象ですが、私はこのtinyintが嫌いです。

そもそもbooleanのようにfalseかtrueの二値だけではなく、-128〜127まで扱える型です。
しかし利用用途は0か1のみを想定している。
アプリケーションレイヤーでは2や3が入っていたらどうするのかなど想定していない。
そういう事は名前から察する事しか出来ない。

delete_flgであれば名前から察する事が出来るだけマシだ。

typeだとしたらどうだろうか。
取り得る値は何があって、どの値が何を示すのかはどこを見れば分かるのだろうか。
カラムのコメントに書いてあり、メンテナンスもちゃんとされていて書いてある事が正しいのであれば良い。
書いてなかったら設計書、仕様書などに現在の正しい対応表が書いてあるならまだ良い。
コードを見るしか無いとなったら最悪だ。
そのカラムに値を入れている箇所を漏れなく見つけだす能力が求められる。
もし見逃してしまったら私の責任か!?私の責任なのかぁーーーーっ!!とモヤモヤするわけだ。

カラムのコメントや設計書などに書いてあったら良いと言ったが、
その内容が今も正しいかって誰か保証してくれるんだろうか。
そうならいい、そうでないのであれば極論を言えばそんなドキュメントは無意味だ。
クライアントに質問され確実に正しい回答をしなければならない時は結局一度はコードを見るしかない。
全く以て不毛である。

なので使えるのであればenum型が一番良い。
値が増える時マイグレーションを書かないといけないと言ってたエンジニアも居たが、
そもそもそういうレベルの改修なんじゃないのかと小一時間・・・

で、mysqlではない場合とか、将来的にmysql以外に移行する可能性を考慮してenumを採用出来ない、などのケースでは
tinyintよりはchar型の方がいい。

その差は僅かではあるものの、中の値から何を示しているのかが読み取りやすいからだ。
もちろんAとかBとかでは全く意味がないが、ちゃんと理解出来る文字列が採用されていれば数値より遥かに有用である。

にも関わらずtinyintが採用されている事の方が多い。
enumやchar型を使っているケースを知っているにもかかわらずtinyintを採用していたりもする。
DBの内部的には僅かではあるもののtinyint型の方が実行速度が早いらしい。
それでなのかは不明だが今回私が言いたいのは、

そんなレベルの速度差を気にするより保守のしやすさを考えてくれって事だ。
弊社ではほとんどMySQLが採用されている。
そして将来的にMySQL以外のDBに移行することなんて一度もなかったくらい稀だ。

だからenum型でいいじゃないか。
値のバリエーションが変化する時はマイグレーションを書けばいいじゃないか。

気にするポイントがおかしいぜって話でした。


 

その他

ちょっと速度よりも可読性が〜という話の割に例が微妙だった気もするのでもう一点追加で挙げるが、
私がインデントを深くしないように早期リターンを推奨するという話をしていたときに
早期リターンだと計算量が多くなると指摘を受けた事がある。

実際にそうなのかを測ったわけではないのでなんとも言えないし、
ケースによってはそういう事もあるかもなとは思うものの、
その差が問題になると分かってから敢えて採用しない事をコメントで残す、で良いと思う。

 

まとめ

何よりも先に速度を考えるエンジニアに対して、
そこよりは可読性の方が重要じゃないかという話でした。

組み込み系をやってきた人など、CPUやメモリが乏しい環境下で実行されるプログラムで、
一度稼働したら滅多に改修が入らないような物を作っているのであれば
常日頃から速度や効率を真っ先に考えてコードを書く事が正しいのだろうけど、
我々が作っている物はWEBやスマホアプリであり、
比較的短納期で開発、検収し、頻繁に改修が入りやすく、実行環境は日進月歩で潤沢になっていく。

この特性から考えたら保守性が高いことが最も優先度が高いはず。

という事で、何が正解かはともかく、保守しやすいとはどういう事かを考えながらコードを書いていきましょう。

ではまたいつか。