2020年9月5日

忍者のコード

考えのない学習は労力の浪費である。学習のない考えは危険である。

孔子(論語)

過去のプログラマー忍者は、コードメンテナの精神を研ぎ澄ますためにこれらのトリックを使っていました。

コードレビューの達人は、テストタスクでそれらを探します。

初心者開発者でさえ、プログラマー忍者よりも上手にそれらを使用することがあります。

それらを注意深く読んで、自分が忍者なのか、初心者なのか、それともコードレビュー担当者なのかを調べてください。

皮肉が検出されました

多くの人が忍者の道をたどろうとします。成功する人はほとんどいません。

簡潔さは機知の魂

コードをできるだけ短くしてください。あなたがどれほど賢いかを示してください。

微妙な言語機能に導いてもらいましょう。

たとえば、この三項演算子'?'を見てください。

// taken from a well-known javascript library
i = i ? i < 0 ? Math.max(0, len + i) : i : 0;

クールですよね?このように書くと、この行に出くわしてiの値が何であるかを理解しようとする開発者は、楽しい時間を過ごすでしょう。そして、答えを求めてあなたにやって来ます。

短い方が常に良いと彼らに伝えましょう。彼らを忍者の道へと導きましょう。

一文字変数

道は言葉のないところに隠れている。道だけがよく始まり、よく完成する。

老子(道徳経)

コードを短くするもう1つの方法は、どこでも一文字の変数名を使用することです。たとえば、ab、またはcなどです。

短い変数は、森の中の本当の忍者のようにコードの中に消えます。エディタの「検索」を使用しても見つけることができません。そして、誰かがそれを見つけたとしても、名前abが何を意味するのかを「解読」することはできません。

…ただし、例外があります。本当の忍者は、"for"ループのカウンタとしてiを使用することはありません。どこでも、ここ以外は。周りを見てください、他にもっとエキゾチックな文字がたくさんあります。たとえば、xまたはyです。

ループカウンタとしてエキゾチックな変数を使うのは、ループ本体が1〜2ページに及ぶ場合(できる限り長くしてください)特にクールです。そうすると、誰かがループの奥深くを覗き込んでも、xという名前の変数がループカウンタであることをすぐには理解できません。

省略形を使用する

チームルールで一文字やあいまいな名前の使用が禁止されている場合は、それらを短縮して省略形を作成します。

このように

  • listlst
  • userAgentua
  • browserbrsr
  • …など

本当に良い直感を持っている人だけが、そのような名前を理解することができるでしょう。すべてを短くするようにしてください。価値のある人だけが、あなたのコードの開発を支持するべきです。

高く舞い上がれ。抽象的になれ。

偉大な正方形は角がない
偉大な器は最後に完成し
偉大な音は希薄な音であり
偉大な像は形がない。

老子(道徳経)

名前を選ぶときは、最も抽象的な単語を使うようにしてください。たとえば、objdatavalueitemelemなどです。

  • 変数の理想的な名前はdataです。できる限りどこでも使ってください。実際、すべての変数はデータを保持していますよね?

    …しかし、dataが既に使用されている場合はどうすればよいでしょうか?valueを試してみてください。それも普遍的です。結局のところ、変数は最終的にを取得します。

  • 変数に型で名前を付けます:strnum

    試してみてください。若いイニシエートは、そのような名前が本当に忍者にとって有用なのかどうか疑問に思うかもしれません。実際、そうなんです!

    確かに、変数名はまだ何かを意味しています。変数の中身、つまり文字列、数値、またはその他の何かを意味しています。しかし、部外者がコードを理解しようとすると、実際には情報がまったく無いことに驚くでしょう!そして、最終的にはあなたのよく考えられたコードを変更することに失敗するでしょう。

    値の型はデバッグすることで簡単に見つけることができます。しかし、変数の意味は何ですか?どの文字列/数値を格納しているのですか?

    良い瞑想なしには、それを理解する方法はありません!

  • …しかし、そのような名前がもうない場合はどうすればよいでしょうか?数字を追加してください:data1, item2, elem5

注意テスト

本当に注意深いプログラマーだけが、あなたのコードを理解できるはずです。しかし、それをどのように確認するのでしょうか?

方法の1つは、datedataのように、似たような変数名を使用することです。

できる限りそれらを混ぜてください。

そのようなコードをざっと読むことは不可能になります。そして、タイプミスがあると…うーん…私たちは長い間立ち往生し、お茶を飲む時間です。

賢い同義語

語ることのできる道は永遠の道ではない。名づけることのできる名前は永遠の名前ではない。

老子(道徳経)

同じものに似た名前を使用すると、人生がより面白くなり、あなたの創造性を公衆に示します。

たとえば、関数のプレフィックスを考えてみましょう。関数が画面にメッセージを表示する場合は、display…で始めるようにします(例:displayMessage)。そして、別の関数が画面に別のもの(たとえば、ユーザー名)を表示する場合は、show…で始めます(例:showName)。

そのような関数間には微妙な違いがあると示唆しますが、実際にはありません。

チームの仲間の忍者と協定を結びましょう。ジョンが彼のコードでdisplay...で「表示」関数を始める場合、ピーターはrender..を、アンはpaint...を使用できます。コードがどれほど面白くて多様になったかに注目してください。

…そして今、ハットトリックです!

重要な違いを持つ2つの関数には、同じプレフィックスを使用してください!

たとえば、関数printPage(page)はプリンタを使用します。そして、関数printText(text)はテキストを画面に表示します。馴染みのない読者に、同様の名前の関数printMessageについてよく考えてもらいましょう。「メッセージはどこに出力されるのか?プリンタか画面か?」。本当に輝かせるには、printMessage(message)を新しいウィンドウに出力する必要があります!

名前を再利用する

全体が分割されると、部分には
名前が必要になります。
すでに十分な名前があります。
いつ止まるべきかを知っておく必要があります。

老子(道徳経)

絶対に必要な場合にのみ、新しい変数を追加します。

代わりに、既存の名前を再利用します。新しい値を書き込むだけです。

関数では、パラメータとして渡された変数のみを使用するようにしてください。

そうすると、変数の現在の内容を正確に特定するのが非常に困難になります。また、それがどこから来たのかもわかりません。目的は、コードを読んでいる人の直感と記憶力を養うことです。直感の弱い人は、コードを1行ずつ分析し、すべてのコードブランチでの変更を追跡する必要があります。

このアプローチの高度なバリアントは、ループまたは関数の中間でこっそり(!)値を似たようなものに置き換えることです。

たとえば

function ninjaFunction(elem) {
  // 20 lines of code working with elem

  elem = clone(elem);

  // 20 more lines, now working with the clone of the elem!
}

関数の後半でelemを操作したいプログラマーは、驚くでしょう…デバッグ中に、コードを調べた後、クローンを操作していることに気づくでしょう!

コードで定期的に見られます。経験豊富な忍者に対しても致命的に有効です。

楽しむためのアンダースコア

変数名の前にアンダースコア___を付けます。たとえば、_name__valueなどです。あなただけがそれらの意味を知っていると素晴らしいでしょう。または、より良いことに、特定の意味をまったく持たずに、ただ楽しむためにそれらを追加してください。または、場所によって異なる意味で。

一石二鳥です。まず、コードが長くなり、読みにくくなり、次に、仲間の開発者がアンダースコアが何を意味するのかを理解しようとして長い時間を費やす可能性があります。

賢い忍者はコードの1箇所にアンダースコアを置き、他の場所ではそれを回避します。これにより、コードがさらに壊れやすくなり、将来のエラーが発生する可能性が高くなります。

愛を示しましょう

あなたのエンティティがどれほど素晴らしいかをみんなに見せてください!superElementmegaFrameniceItemのような名前は、読者を間違いなく啓発するでしょう。

確かに、一方では、何か(super..mega..nice..)が書かれています。しかし、他方では、詳細がありません。読者は隠された意味を探し、自分の有給勤務時間の1時間か2時間を瞑想に費やすかもしれません。

外部変数をオーバーラップさせる

光の中にいるときは、暗闇の中では何も見えない。
暗闇の中にいるときは、光の中ですべてを見ることができる。

関尹子

関数内外の変数に同じ名前を使用します。簡単です。新しい名前を考案する必要はありません。

let user = authenticateUser();

function render() {
  let user = anotherValue();
  ...
  ...many lines...
  ...
  ... // <-- a programmer wants to work with user here and...
  ...
}

renderの中に飛び込むプログラマーは、外側のuserを隠蔽するローカルのuserがあることに気づかないかもしれません。

次に、authenticateUser()の結果である外部変数であると想定して、userを操作しようとします…罠が仕掛けられています!こんにちは、デバッガー…

いたるところに副作用が!

何も変更しないように見える関数があります。たとえば、isReady()checkPermission()findTags()などです。それらは計算を実行し、データを検索して返すだけで、外部のものを何も変更しないと想定されています。言い換えれば、「副作用」なしです。

本当に美しいトリックは、メインタスクに加えて、「便利な」アクションを追加することです。

名前がis..check..、またはfind...の関数が何かを変更しているのを見たときに、同僚の顔に浮かぶ驚きの表情は、あなたの理性の境界を間違いなく広げるでしょう。

驚かせるもう1つの方法は、標準外の結果を返すことです。

あなたの独創的な考えを示しましょう!checkPermissionの呼び出しでtrue/falseではなく、チェックの結果を含む複雑なオブジェクトを返させましょう。

if (checkPermission(..))を書こうとする開発者は、それが機能しない理由に疑問を抱くでしょう。「ドキュメントを読んでください!」と言ってください。そして、この記事を渡してください。

強力な関数!

偉大な道はいたるところを流れ
左にも右にも。

老子(道徳経)

関数の機能を、その名前に書かれている内容で制限してはいけません。もっと広く捉えましょう。

例えば、validateEmail(email) という関数は、(メールアドレスが正しいかどうかをチェックするだけでなく) エラーメッセージを表示して、メールアドレスの再入力を求めることもできます。

追加のアクションは、関数名から明白であるべきではありません。真の忍者コーダーは、コードからも明白でないようにするでしょう。

複数のアクションを一つにまとめることは、コードの再利用を防ぎます。

想像してみてください。他の開発者がメールアドレスをチェックするだけで、メッセージを出力したくないとします。両方を行うあなたの関数 validateEmail(email) は、彼らにとって都合が悪いでしょう。そのため、彼らはあなたにそれについて何も尋ねて、あなたの瞑想を妨げることはないでしょう。

まとめ

上記の「アドバイス」はすべて、実際のコードから引用したものです… 時には、経験豊富な開発者によって書かれたものです。あなたよりも経験豊富な場合もあるでしょう😉

  • それらのいくつかに従うと、あなたのコードは驚きに満ちたものになるでしょう。
  • それらの多くに従うと、あなたのコードは真にあなただけのものになり、誰もそれを変更したくなくなるでしょう。
  • すべてに従うと、あなたのコードは、悟りを求める若い開発者にとって貴重な教訓となるでしょう。
チュートリアルマップ

コメント

コメントする前にこれを読んでください…
  • 改善点について提案がある場合は、コメントするのではなく、GitHub issueを提出するか、プルリクエストを送ってください。
  • 記事の内容で理解できない点がある場合は、詳しく説明してください。
  • コードを数語挿入するには、<code> タグを使用し、複数行の場合は <pre> タグで囲み、10行を超える場合はサンドボックス ( plnkr, jsbin, codepen… )を使用してください。