LoginSignup
315

More than 3 years have passed since last update.

re: こんなコードは嫌だ、古い書き方のコード駆逐したい

Last updated at Posted at 2021-02-23

伏線回収です。

TL;DR

  • 当該記事は総論賛成だけれど、「古い」「ダサい」で物事は変えられない
  • 最近の環境の変化、コードを書いた人が持っている暗黙の前提なども考えないといけない
  • 単純にコードを変えるだけでなく、それに合わせて環境やプロセスを整える必要がある。そこはみんなそれぞれ頑張れ。

まずは分類

9個の「古い書き方」が挙げられていますが、実はいくつかに分類できるかと思います。私が分けるなら以下のような感じです(いくつかにまたがっているものもあると思いますが、私なりにエイやで分けました)

  • エディタやIDEの進化により不要/必要性が低くなったもの

    • 変数名が雑
    • 無駄な省略
    • ハンガリアン記法
    • ヨーダ記法
  • 古い言語の常識を新しい言語でも使ってしまうもの

    • 必要以上に広いスコープで変数宣言する
    • 言語として用意されている機能を使わない
    • goto
  • バージョン管理システムを使いこなせていないもの

    • 変更するときに昔のコードをコメントアウトして残す
  • コードの可読性の問題

    • 無駄に()つけている

エディタやIDEの進化により不要/必要性が低くなったもの

まず、モダンなIDE(VSCodeやJetbrains)や、Vim/Emacs向けのLanguage Serverの普及など、エディタの普及により必要性が低くなってしまったものです。

雑な変数名や無駄な省略がなぜ発生するのか、というのを考えると、勿論名前をつけるのが面倒くさい、というのもあると思うのですが、それ以上に(特にベテランの方の)反応として返ってくるのは「長い文字を打つのが非効率」というのがあります。

そう、かれらは偉大なるIntellisense(コード補完)に不慣れ、というよりほとんど利益を受けてきていないんですよね。仮に長い名前をつけてしまってしまっても、きちんとIDEやエディタを設定していれば3文字から4文字も打てば候補がほぼ絞り込めるはずです1

同様にハンガリアン(正確に言うとシステムハンガリアン)記法も必要性は少なくなっています。変数名にカーソルを持っていけば型を表示してくれますし、そもそも安全でない代入は警告が発生することがほとんどです2

ただハンガリアンについては、本来の意味である「プログラム上の意味を表すハンガリアン」や、あるいはアクセス修飾子もハンガリアン的に書くことは一般的とは思いますので、個人的にはシステムハンガリアンはもういらないよ、でいいのかなとは思います。

ヨーダ記法はWikipediaの記述にもあるとおりで、代入演算子と等価演算子を混同しないために使われていることは未だに多いですね。

public sample(price : number) : void{
    if (0 = price) { // コンパイルエラーになって嬉しい!!
    }
}

ただ、そもそもこれも現在のLintツールやIDEの警告で十分にカバーできます。

これらのルールを削除することには異論はありませんが、その代わりチームのルールとして、モダンなエディタを利用し、コミット時などのLintチェックを仕組みとして取り入れる、というところまでセットで実行することが重要だと思います。

古い言語の常識を新しい言語でも使ってしまうもの

次のカテゴリとしては、古い言語の常識を新しい言語でも使ってしまうことですね。ここでいう「古い言語の常識」とは、現在現役の言語の過去バージョンも含みます(Java7以前、C# 3.0以前、C++11以前、などなど)

さて、まず変数のスコープについて。これは元記事のコメント欄でもあるように、C99以前のC言語では必ず最初に変数宣言をつけないといけないという話があったりしました。しかしこれが現在のC/C++で変数宣言を冒頭に持ってくる理由にならず、最新の規格に従って書きましょうというのが大前提になるでしょう。

言語としての機能についてもそのとおり。特にC#におけるLinq、Javaにおけるstream3、あるいは全言語共通的にラムダ式(あるいは関数オブジェクト)については、結構障壁があるようには思います。

この辺は認知負荷の問題だと思っていて、何度も何度も書いていればなれてくるもんだな、というのは感覚的にはあります。基本的には、コードを短縮できる場合が多いので、私がおすすめする場合には、

  1. コードがまず短くできる
  2. 意図を示すコードとして書きやすい
  3. 並列化可能性など最終的なパフォーマンスとして有利な場合がある

というくらいの順番で必要性を説いていきます4

あと goto ですが、実はよくあるパターンは「例外を知らない/使いこなせていない」です。
シェルとかだと goto 使わざるを得ないことあるので、その辺書いたことあるひとはピンとくると思うんですが、 goto ってエラー処理的なものを書くのによく使うんですよね。特にfinally句として非常に使い勝手がいいです。

んで、例外がある言語に関しては、そもそも例外で同様のことができることを知らずにgoto使っていることがあるんじゃないかと思います。というわけで、例外をうまく使いましょう5

バージョン管理システムを使いこなせていないもの

古いコードコメントアウト問題ですね。

これ、実は「git使え」だけの問題だけじゃないよなと思っていて、この抵抗にはいくつかの理由があると思います。

まずは、gitに入れたとして、どうやったら古いコードが復帰できるのかがわからない、というパターン。
この対処法は結構簡単で、Git機能組み込みのエディタで持って、git blameを見せると大体は納得してくれます。結局、どうやってこのポイントに戻ったらいいの?というのがイメージできないというのが大きいですね。

git blame に限らず、タグを打ったり、ブランチを切ったり、stashしたり、みたいなGitのリテラシーを身に着けてもらって、コードコメントアウトでやりたいことが代替できる、ということがわかってもらえればいいのかな、と思います。

…じつはもう一歩踏み込むと、そもそもなぜ「過去のコードをコメントで残す」という行為が必要なのか、ということに行き着きます。いくつかあると思うのですが、ざっと考えられる理由を考えると

  1. 版管理のため
    → これはGitで完全に代替可能
  2. 消したコードを復帰させたい場面がある(デグレード時など)
    → これもGitを使えば復帰可能だが、そもそもそんなに恐る恐るコードを消すのはなぜ?テスト足りてないんじゃない?
  3. 「コードのライン生産量」がメトリクスになってしまっているので、行数を減らす変更をしたくない
    → そもそもそんなLOCでの生産性は測らないよ、と品質やベロシティの計測基準の共通理解を作る

こんな感じのところまで考えたほうがいいのかなと。

コードの可読性問題

演算子の順序が自明な場合、()は省略しよう。もし可読性のことを考えて()をつけるなら、むしろ変数や関数に切り出そう、というのが元記事の主張で、これには反論の余地なく、同意します。

同意しますが、実はカッコに関して言えば、もし自明の場合に省略していないのならば、なんでifの{}を省略しないの?というのは、一つの疑問として上がります。

元記事のサンプルコードを引用すると

public getPrice(fuga : boolean, piyo : boolean) : number {
    let price = 0;
    // ここでitem宣言するとif文の外でも使われるのかと思われてしまう。
    const item = this.getItem();
    if(fuga){
        price += 100;
        if (piyo){
            // このif文の中でしかitem使ってないよね?この中で宣言しようね?
            price += item.Price; //追記コメント: ここ1センテンスだからそもそも中括弧いらないよね?
        }
    }
    return price;
}

上記なんかですね。例えば、私だったら以下のように書くかもしれません。

public getPrice(fuga : boolean, piyo : boolean) : number {
    if(!fuga) return 0 //そもそも早期リターンできる

    const basePrice = 100

    // ここでitem宣言するとif文の外でも使われるのかと思われてしまう。
    const item = this.getItem()
    if(piyo) return basePrice + item.price //ここはカッコ入れてもいいかもね

    return basePrice
}

if のステートメント反転で、早期リターンしちゃうでしょう。両方とも1ステートメントなので文法上のカッコは不要です。シンプルなロジックの場合には、そもそも if 句の前で改行を入れないことで、1行に収めたほうが、個人期には読みやすいと思います。

…とまぁ何を言いたいかと言うと、可読性というのはコンテキストにより変化する要素が大きいのです。
なので、Lintなどのツールによるチェックと、何より相互レビューやペアプロ/モブプロを通して、チームとしての「可読性」の意識を合わせていくほうが何よりも大切です。

終わりに

というわけでダラダラ書きましたが、言いたいことは最初と同じで、

  • 「古い」「読みにくい」などの理由だけでのルール押しつけは誰の得にもならない
  • 相手のコンテキストを理解して、なぜそんなコードを書いているのかを理解する事が必要
  • コーディングルール単体ではなく、IDEなどの環境、Gitなどのプロセス、Lintなどのツールを包括的に設定することが必要

最後に一言だけ。

理想に燃えている若者を、コメント欄でそれこそ嫌味のような言い回しをして、知識不足をあげつらうのが本当に正しい姿勢なのですかね?私も苦言めいたことは書いちゃったとは思いますが、ちゃんと記事という形で、自分の主張を自分の場で書くことにしました。このへんが最低限の先達としてのやり方なのではないかと思います。見ていて正直気持ちのいいものではなかった、というのを自分の感想として書き残しておきます。



  1. もしそれで絞り込めないんだとすると、その変数名は冗長すぎるか、あまりにもスコープを大きく取りすぎているかだと思いますが、それはちょっと別の話とさせてください 

  2. Cとかはもしかすると安全でない場合があるかもしれません。CppLintなどでなんとかなりそうな気もするのですが最近C++触っていないので、ご教授もらえると幸いです 

  3. 現状のstreamがC#とLinqと同様に使いやすいかと言われるとそれはそれで、、、となりますが、 

  4. ちなみにJavaに限れば、Jetbrains製IDEを使うと、For文を勝手にstreamに書き換えたりできるので、IDEの力に頼るのも一つの手だと思います 

  5. うまく使えると入ってない(例外が鬼難しいのはJoel先生の言を待つまでもなくそのとおり 

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
315