Skip to content

Latest commit

 

History

History
149 lines (137 loc) · 7.68 KB

File metadata and controls

149 lines (137 loc) · 7.68 KB

コーディング ルール

原則

  • 原則とは考える側面である
    • 単純に従うことではなく、その原則が提供する「考える側面」について考えた上で行動することが大切
  • SOLID
    • S(Single Responsibility): 単一責任
    • O(Open-Closed): 拡張にはオープン、変更にはクローズド
      • YAGNI(You ain't gotta need it)
    • L(Liskov Substitution): リスコフの置換原則
      • ある型のオブジェクトが規定する動作を、その派生型のオブジェクトのものに置き換えても、 その動作はオブジェクトの利用(開発者など)にとって予想可能である
    • I(Interface Segregation): インターフェース分離
      • 使わないインターフェースを実装したり使われないインターフェースに依存したりしないようにインターフェースを分ける
    • D(Dependency Inversion): 依存性の逆転
      • 別の型に依存するとき、具体的な型ではなく抽象的な型に依存するようにする
      • DI(Dependency Injection、依存性の注入)を行う
  • KISS(Keep it simple/short/straightforward/silly)
    • 認知負荷を下げる
    • 原則に従って認知負荷が上がる場合、従わない方がいいかも
  • DRY(Don't repeat yourself)
    • 繰り返しは共通化する
    • やり過ぎない(認知負荷が上がる)

言語非依存

定数・変数

  • スコープを減らす
    • ブロック→関数→ファイル→モジュール・パッケージ→グローバル
    • 値の影響範囲を減らし、認知負荷を下げるため
  • 変数よりも定数
    • 意図しない書き換えを防ぎ、認知負荷を下げるため
  • 実行時よりもコンパイル時
    • 計算時間を減らすため
  • 変数は初期化する
    • 不正な値を防ぐため
    • 読み込まれることのない書き込みを消すのは最適化の仕事とする
  • 型推論を使う
    • 長すぎる型名を省略するため
    • 型の変更をダックタイピング的に許容するため
    • 認知負荷が上がりすぎる場合には明示的に型を指定する

値の取り扱い

  • コピーよりもムーブ
    • メモリアクセスを減らすため
    • 認知負荷が上がりやすいので注意
  • 番兵(本来とは異なる特殊な意味を持つ値。 0 とか -1 とか最大値とか)を使わない
    • バグになるのを防ぐため
    • 特殊な意味は別に変数を持って表す
  • リフレクションは使わない
    • 認知負荷が上がるの防ぐため
    • テストコードは例外
      • 公開されていない対象へのアクセスに必要かも

条件分岐・エラーチェック

  • Fail fast(なるべく早く失敗させる)
    • 不正な値について正常系の処理をするとどうなるか、という検討を不要にするため
  • なるべくエラーや例外的な状況でネストが深くなるようにする
    • 正常系のコードのネストを減らし認知負荷を下げるため
  • ユーザの入力はバリデーションする
    • サニタイズしない
    • 脆弱性を防ぐため

高速化・最適化

  • 高速化の順番
    • プロファイリングをする
      • 遅い箇所を推測しない
    • アルゴリズムの変更
    • 並列化
  • 並列化で気をつけること
    • データハザード(書き込みの前後の読み書きの順番)
    • デッドロック
    • ロック・同期の代わりにアトミックな変数・メモリバリア(処理の順序を強制し、前の処理が終わったことを別のスレッドから見えるようにする)が使えるか検討する
      • ロック・同期は遅い

その他

  • コードを書き始めるとき
    • 予め
      • formatter/linter/静的解析を使う
      • CI/CDを準備する
    • 後からだとリファクタが大変

C/C++

  • 関連ツール
    • formatter: clang-format
    • linter: cpplint
    • 動的解析: コンパイラのフラグ、valgrind
  • RAII(Resource Acquisition Is Initialization)
    • メンバ変数はコンストラクタで初期化し、デストラクタで廃棄する
    • 失敗時に既に確保したリソースがあれば廃棄する
      • リソースリークを防ぐため
      • Cではリソース解放用のコードを用意してラベルを貼り goto する
      • C++では例外をスローしてスタックの巻き戻しにより対応する(メンバ変数のデストラクタに任せる)
  • スマートポインタを使う
    • リソース(メモリ、ハンドル、接続など)のリークを防ぐため
    • 単一所有者: std::unique_ptr
    • 複数所有者: std::shared_ptr
      • 循環参照がある場合は std::weak_ptr を検討する
  • Interger promotionを意識する
  • friend を使わない
    • カプセル化を守るため

JavaScript

  • 関連ツール
    • formatter: prettier
    • linter: ESLint
      • ルールが膨大なため、ルールセットと併せて導入する
  • ビルトインのオブジェクトを使う場合は必ず実行環境で使えるか確認する
    • 古いブラウザやNode.jsでは使えないことがある
  • あるオブジェクトのプロパティに対応する値にアクセスして undefined が得られた場合、状態が2パターンあることに注意
    1. プロパティに対応する値として undefined が入っている場合
    2. プロパティそのものが存在しない場合
  • Array.prototype.sort() は、値を文字列に変換して辞書順ソートとなることに注意

Java

  • 型推論: var
  • 並列ソートが標準で存在する( parallelSort

PHP

  • 配列は順序付きのマップであることに注意する

MySQL

  • プリペアードステートメントにする
    • 典型的なSQLインジェクションを防ぐため
  • ストアドプロシージャを使わない
    • 引数の型がプロシージャの定義と与えられた値で異なる場合、エラーとならず不正な値となる
    • 表を変数に格納できない
    • クライアント側でプロシージャライクにまとめる
  • トランザクションをネストしない
    • トランザクション中に新たにトランザクションを開始しない
    • 既存のトランザクションがコミットされてしまう
    • SAVEPOINT で代替できるか検討する
  • トランザクション分離レベル
    • SERIALIZABLE 以外は、あるトランザクションで実行した変更は、例え COMMIT 後でも別のトランザクションからではすぐに読まれないことに注意する(強一貫性ではない)
  • MySQL互換データベースで使えない機能を把握しておく
    • Percona XtraDB Clusterではトランザクション分離レベル SERIALIZABLE は使用不可

Cassandra

  • Consistency Level
    • 基本的に書き込みは EACH_QUORUM 、読み込みは LOCAL_QUORUM にする
      • 強一貫性(書き込みが即座にノード・クラスタ間で反映される)
    • 要件によって弱いレベルを検討する
    • ALL は使わない
      • 1ノードでもダウンしたら必ず失敗になる
  • 読み書きの失敗の再試行はRetry Policyではなくクライアント側で行う
    • Retry Policyは意味論が不明瞭で、バグがクライアント側かドライバ側か分かりづらい
  • あるキーの範囲でアクセスする時、クライアントでキーを列挙してアクセスする
    • 範囲に当てはまるかどうかを判別するために全検索されるのを防ぐ
  • BATCH 句は必要最小限にする

シェルスクリプト