AOYAMA Koji's プログラミングブログ - プログラミングを楽しく体験

40年の開発経験から生まれた実践的コーディングルールプログラマー向け実装解説

2025/09/06
40年の開発経験から生まれた実践的コーディングルール

 大規模ゲーム開発プロジェクトでのチームリーダー経験と、アマチュア時代から数えて40年のプログラミング経験に基づいて策定した、当ブログのコーディングルールをご紹介します。 理論だけでなく現場で培ったノウハウを反映した実践的なものです。
 対象言語は JavaScript ですが、他の言語への応用も可能です。 特に、各ルールを策定した考え方は、参考にしていただけるハズです。 これらが当ブログにおけるプログラム理解や、皆さんの開発品質向上にお役立てば幸いです。
 長編のため、エッセンスのみ確認したい場合は大原則をご参照ください。
40年の開発経験から生まれた実践的コーディングルール

プログラミングブログ記事一覧


[PR]

大原則


 最初に、当ブログプログラミングのコーディングルール策定における大原則を記載します。

可読性重視


 当ブログのコーディングは可読性を重要視します。 つまり読みやすいプログラミングを目指します。
 書く文字数が長くなる問題よりも、後から読みやすいことの方が重要です。
 これはブログだからではなく、実際の開発において、 書く回数よりも読む回数の方が多いという経験則からです。

可読性を重視する例

  • 名称が長くなることを許容し、不明瞭な省略は避ける。
  • コードが長くなることを許容し、必要なコメントや改行、スペーシングは確保する。

厳密性重視


 厳密性を重視し、曖昧な要素はできるだけ排除します。

厳密性を重視する例

  • == ではなく === を用いて型も比較する。
  • 原因不明の不具合はたとえ軽微でも放置しない。


禁止事項最小化


 禁止事項はできるだけ少なくします。
 一般的には、例えば移植性を重視して、言語の特性を活かしたテクニックを禁止する場合もあります。 当ブログでは禁止せず、それを利用することで簡潔に書けるなら、むしろ積極的に取り入れ、技術力向上を目指します。

比較的新しいJavaScript言語仕様の使用例

// null または undefined の場合の分岐処理
let sResultA = sValue ?? sDEFAULT;
let sResultB = oUser?.sName;
 なおこれらは古い環境では動作しない場合がありますので、参考にされる場合はご注意ください。

歴史的経緯許容


 以前に記載したコードがコーディングルール違反だった場合は、 いったん許容し、無理のないタイミングで順次修正するものとします。
 すなわち、当ブログの初期に書いたプログラムはコーディングルールに沿っていない場合があります。 ご了承ください。

体裁統一


 コーディングの体裁は、統一が必要です。
 実質的な内容が変更されていなくても、体裁が変更されると、 バージョン管理ツールで差分として出てしまい、実質的な変更点を追うのが困難になるためです。
 大規模なチームなら、自動フォーマッターのESLintやPrettierのデフォルトに合わせる方が良いかもしれません。 当ブログは原則筆者のみが手で書いているので、個人の好みで設定します。 以下が具体的な体裁です。

インデント


 インデントは、半角スペースx2 とします。

スペーシング


 原則として、演算子と変数名等の間には左右バランスよくスペースを入れます。
 関数の引数などは、左側にスペースを入れず、右側に入れる形が古くからの主流ですが、 当ブログでは左右にスペースを入れる形を原則とします。
 入れない方が可読性が上がる場合は、左右共に入れません。 個人感覚で良いものとします。

スペーシングの例

let iTotal = 0;
for ( let iIndex = 0 ; iIndex < iINDEX_MAX ; ++iIndex ) {
  iTotal += aiSrc[ iIndex ];
  aiDst[ iIndex ] = iTotal;
}
let oUser = {
  sName: 'test',
};
let sResultA = oUser.sName;
let sResultB = oUser?.sName;
fncResult( iTotal , sResultA , sResultB );

コメント


 コメントは原則 // 以降に英語で記します。
 当ブログで紹介する際には、コメントを日本語に翻訳し、必要に応じて詳細に書き直します。
[PR]

関数


関数名


 関数名は、英小文字の動詞で始め、先頭大文字の単語を並べます。 省略は極力控え、関数名のみで中身が推測できるようにします。
 開発中に機能が変更された場合には、合わせて関数名も変更します。

  • getElementId()
  • setImageData()

DRYの原則


 関数は機能ごとに分離し、長くなりすぎないようにします。 目安は、最長でもエディターの1画面で収まる範囲とします。
 また同ロジックは一箇所にまとめるDRY(Don't Repeat Yourself)の原則に従います。
 ただしコンテンツごとの保守効率を考え、あえて分離することは問題ありません。

コメント


 関数の説明コメントは以下の形式とし、function定義直前に記載します。
//**********************************************************************
// 関数名や機能概要
//   arguments:
//     引数説明
//   return:
//     返り値説明
//   note:
//     注意事項
//**********************************************************************

非同期関数名


 非同期関数で async 宣言されておらず Promise オブジェクトを返す関数は、コメントにその旨を記載します。
 関数名における同期と非同期による差別化は規定しません。 これは LOTaskManager(当ブログ自作モジュール) が各タスクを await で呼び出しているなど、同期関数から非同期関数に変更しても大きな問題にならないことがあるためです。 環境によっては、分けたほうが良いでしょう。

変数


変数宣言


 変数宣言は原則 let で行います。
 const であるべき場合や var で問題ない場合も、特にローカル変数は let で統一します。
 let は var と異なり重複定義がエラーになるため、厳密性に優れています。
 また変数の用途が変わり変更されなくなったとしても const 宣言に変更しなくて良いルールとすることで、作業効率を上げています。

変数名


 変数名は、名称だけで用途がわかるものにします。 開発中に用途が変更された場合には、合わせて変数名も変更します。
 また、想定している型が変数名からわかるようにします。
 具体的には、型に対応した英小文字の識別子([a-z]+)で始め、先頭大文字の単語を並べます。 省略は極力控え、変数名のみで用途が推測できるようにします。
 型を変数名に入れている理由は JavaScriptが動的型付け言語のため、宣言する方法が無く、厳密性が保ちづらいためです。 TypeScriptのように型指定が明確にできる言語を使用するなら、型指定を必須とすることで、本型識別子は無くても良いでしょう。

変数名の先頭に付加する型識別子

 以下が、変数名の先頭に付加する型識別子です。 配列は a の後に型の識別子が続きます。
識別子
備考
何でも
x
任意・未知
整数
i
Number型(Integer)
少数
f
Number型(Float64)
文字列
s
String型
真偽
b
true or false (Bool)
巨大整数
n
BigInt(1n等)
2D位置
ci
LOPosI {iX,iY} (Coord)
2D座標
cf
LOPosF {fX,fY} (Coord)
2Dベクタ
hf
LOVecF {fX,fY} (Heading)
3D頂点
v
LOVector3d {fX,fY,fZ,fW})
3D行列
m
LOMatrix3d (4x4少数)
四元数
q
LOQuotanion3d (4x1少数)
オブジェクト
o
Object
辞書
d
Dictionary (明示時)
配列
a
Array(続いて識別子)
関数
fnc
FuNCtion

変数名の例

  • iCount
  • fAngle
  • sFileName
  • mPerspective
  • aoWorker
  • afSamplingRate
  • fncOnLoad

簡易変数名の例(非推奨)

for ( let i = 0 ; i < iARRAY_MAX ; ++i ) {
   aiDst[i] = aiSrc[i];    // i は iIndex 等とすべき (Dst と Src は許容)
}

例外

  • g: モジュールグローバルな変数を集約するオブジェクト
  • e: catchやイベント発火時に呼ばれる関数の引数

定数


 デフォルト定義など、C言語であれば #define で定義するものを定数とします。

定数宣言


 定数は const で宣言します。

定数名


 定数名のルールは、型に応じた英小文字の識別子([a-z]+)で始まり、大文字の単語を並べます。 2つ目以降の単語は _ で繋ぎます。

  • iCOUNT_MAX
  • fDEFAULT_ANGLE
  • aoARROW_TYPE

OAOOの原則


 複数箇所で同じ意味の値を使用する場合は、定数を定義し、原則としてそれを使用します。 同一意味は一箇所とするOAOO(Once And Only Once)の原則に従います。
 関連して、ソースコードに直接数値を書くマジックナンバーの使用を原則として禁止します。

定数使用例

// 定義
const iPOSITION_OFFSET_MOVE_COUNT_MAX = 10;   // [10] 詰み手数
// 使用 if ( iMoveCount < aiPosition[ iPOSITION_OFFSET_MOVE_COUNT_MAX ] ) { // 処理 }

マジックナンバー使用例(非推奨)

// 10 というマジックナンバーの使用は原則禁止
if ( iMoveCount < aiPosition[ 10 ] ) {
  // 処理
}

クラス


クラス名


 クラス名は基本的に LO を先頭に付けて、以降、先頭が大文字の単語を並べます。
 LO は Logic Lovers の LO です。 C++などのネームスペース機能の代替としてこの形にしています。 なおシリーズ名がある場合は、その略称の大文字を使用します。


  • LOGraph
  • LOImageData

継承


 クラスの継承は、意味として継承する妥当性が高いときのみに限定して行います。
 例えば保有で十分であれば継承せず保有します。
 このルールは、継承を駆使すると、策士策に溺れがちになるという経験則から、最小限にすべきと考えて策定しています。

スコープ


 クラスのメンバ変数は、C++ の protected の扱いとし、名称の先頭に「_」(アンダースコア) を付けます。
 外部から直接アクセスは、JavaScriptの言語仕様としては可能ですが、コーディングルールとして禁止します。 継承先からのアクセスは問題ありません。
 これにより、各クラスを疎結合にしやすくなり、保守性の高い実装がしやすくなります。
 なお、C言語の構造体のような扱いで、メンバ変数に直接アクセスする書き方が望ましい一部のクラスは、明示的に getter と setter を実装します。

Web Worker用辞書


 JavaScript の Web Workerは、辞書はやりとりできますが、クラスオブジェクトのような関数を含むオブジェクトはやりとりできません。
 そのため Web Worker でやりとりするクラスについては、 メンバ変数を辞書データ化した値を返す getDict() メンバ関数を実装します。
 そして getDict() メンバ関数の返り値は constructor の引数に渡せるものとします。

クラスサンプル


//**********************************************************************
// 2D位置クラス
//**********************************************************************
export class LOPosI {
  //**********************************************************************
  // メンバ変数
  //**********************************************************************
  _iX = NaN;
  _iY = NaN;
  //**********************************************************************
  // getter
  //**********************************************************************
  get iX() { return this._iX }
  get iY() { return this._iY }
  //**********************************************************************
  // setter
  //**********************************************************************
  set iX(iArgX) { this._iX = iArgX }
  set iY(iArgY) { this._iY = iArgY }
  //**********************************************************************
  // 辞書取得
  //**********************************************************************
  getDict() {
    return { iX:this._iX , iY:this._iY };
  }
  //**********************************************************************
  // セット
  //**********************************************************************
  setByDict( { iX,iY } = {} ) {
    this._iX = iX;
    this._iY = iY;
  }
  //**********************************************************************
  // コンストラクター
  //   引数: { iX , iY }
  //**********************************************************************
  constructor( dOpt ) {
    this.setByDict( dOpt );
  }
}

モジュール


 当ブログでは、基本的に JavaScript はモジュール(module)を使用します。

ファイル拡張子


 JavaScriptモジュールファイルの拡張子は .mjs です。

モジュール内共通変数


 モジュール内で共有する変数は g オブジェクト内に設定します。

let g = {
  iScore: 0,
  bExit: false,
};

クラスモジュール


 クラスは、原則1クラスごとに1モジュールとします。 モジュールファイル名はクラス名と同じです。
 インポートは {クラス名} の形式とします。
 このルールは Java の class 関連の仕様を参考にしています。

インポート例


import { LOGraph } from '../js/LOGraph.mjs'

クラスモジュール名称例

  • LOMatrix3d
  • LOTaskManager

ユーティリティーモジュール


 ユーティリティーモジュールは、適宜作成します。
 ファイル名は lo (小文字) で始まる、適切な名称です。
 lo は Logic Lovers の lo です。 C++などのネームスペース機能の代替としてこの形にしています。 なおシリーズ名がある場合は、その略称の小文字を使用します。
 インポートは「* as モジュール名」の形式とします。

ユーティリティーモジュール名称例

  • loMath
  • loImageDecoder

インポート例

import * as loMath from '../js/loMath.mjs'

アプリケーションモジュール


 ひとつのアプリケーションを構成するモジュールは名称を loapp で始めます。 これで定義されたアプリケーションは、htmlに対応する id の div エレメントを定義して、対象のモジュールを読み込み指定するだけで動作するように、制作します。

アプリケーションモジュール例

  • loappXmlXslViewer
  • loappTsumeShogi

アプリケーションモジュール使用例

(head)
<script type="module" src="../js/loappSwissTournament.mjs"></script>
(body) <div id="loIdDivSwissTournament" class="center textcenter"></div>

各モジュールのインポート順序


 各モジュールのインポートは、原則として汎用性の高いモジュールほど先(上部)にするものとします。

シングルトン特例


 シングルトンは、クラスを作成せず、ユーティリティーモジュールのルールに従います。
 JavaScriptではシンプルに実現できますので、そのままで十分です。

定義順序


 変数や関数、およびクラスやメンバ変数、メンバ関数の定義は、原則として参照される方を先(上部)にするものとします。 これは C言語の extern 関連の仕様を参考にしています。
 エクスポートの順序は規定しません。 変数および関数定義の順序に従います。

宣言順序を考慮したクラス定義例

class LOMyClass {
  // メンバ変数
  _iValue = 0;
  // 設定
  setValue( iValue ) {
    this._iValue = iValue;  // _iValueが上部定義済み
  }  
  // コンストラクター
  constructor( iValue ) {
    setValue( iValue );     // setValueが上部定義済み
  }
}


同値比較


 同値比較は原則 === を用い、形の一致も確認します。 定数と変数の比較については左辺を定数とします。
 左辺を定数としているのは、 意図せず == を = と書いてしまった場合にC系の言語でコンパイル時にエラーにできるためです。 しかし === と = を間違えることはほぼ無いので、ここは好みの問題です。

nullチェック例

 以下とすることで sValue を意図的に null にしている場合のみ true になりデフォルト値が適用されます。 '' や undefined のときはそのままです。
let sResult = (null === sValue) ? sDEFAULT : sValue;

undefinedもtrueになる例(非推奨)

// null,undefinedはtrue
let sResult = (null == sValue) ? sDEFAULT : sValue;

''もtrueになる例(非推奨)

// null,undefined,''はtrue
let sResult = (!sValue) ? sDEFAULT : sValue;

0チェック例

 以下とすることで、意図せず iValue に '' や '0' が入っていた場合に false の処理にできます。
let iResult = (0 === iValue) ? iDEFAULT : iValue;

''や'0'もtrueになる例(非推奨)

// 0,'0',''がtrue
let iResult = (0 == iValue) ? iDEFAULT : iValue;

大小号比較


 大小の比較は原則として、不等号に < または <= を使用します。 ※つまり > および >= は原則使用しません。
 この形で統一することにより、可読性を上げています。

範囲チェック例

if ( 0 <= iX && iX < 512 ) ) {
  //処理
}

少数演算の明示


 少数演算は小数点以下を記述します。

小数点代入例

let fVelue = 1.0;

 JavaScriptは、Number型で扱われ整数と小数の区別はありませんが、少数であることを明示するものとします。

判定式は正常時trueに


 変数との比較は、正常時に true になるように記載します。 これは意図せず unedfined や NaN のときに、判定式が必ず false になるためです。

エラー判定例

if ( ! ( 1 <= iMonth && iMonth <= 12 ) ) {
  //エラー処理
}

エラー判定例(非推奨)

if ( iMonth < 1 || 12 < iMonth ) {
  //0未満あるいは12超の数値が「正しく」入っているときのみエラー処理
}

制御文


 if や for, while などの制御文についての規定を記します。

{}(波括弧)省略の条件


 制御文では原則として { と } を使用します。
 ただし1行で済む場合のみ、省略可能です。 その場合、改行は禁止とします。

制御文{}使用例

if ( ! bCondition ) {
  console.log( 'skip' );
  continue;
}

制御文1行例

if ( ! bCondition ) continue;

制御文{}不使用複数行例(禁止)

// 禁止
if ( ! bCondition ) 
  console.log( 'skip' );
  continue; // こう書いてしまいそうになる(if文の外)

ループの記述方法


 ループについては、もし for ~ in または for ~ of で記述可能なら、そちらを優先します。
 ただし禁止制御はありません。 for ( ; ; ) や while, do ~ while および forEach や map, filter 等々も使用可能です。

for ~ of 例(推奨)

for ( let oUser of aoUser ) {
  // 処理
}

関数型ループに関する注意事項


 forEach 等の関数を引数に取る関数型ループを直接記述する場合は、原則としてアロー関数を使用し、 function は使用しないものとします。 理由は function を使用すると this の意味が変わるためです。
 また、return が continue 相当の意味になる場合は、可読性のため、コメントにその旨を記載します。

アロー関数例(関数型ループでは推奨)

aoUser.forEach( ( oUser ) => {
  if ( ! oUser.bActive ) return; // continue相当
  // 処理
});

funtion例(非推奨)

aoUser.forEach( function( oUser ) {
  // この中だけ this の意味が変わるので非推奨
});

switch文のbreak省略可


 switch文においてbreak省略は可能です。
 可読性の問題で禁止しているケースもあると思いますが、 むしろ可読性が上がる場合もあり、そのとき当ブログでは積極的に活用します。

エラーハンドリング


 エラーハンドリングは、論理的にエラーが発生する可能性がある場合のみ行います。 意図していないエラー発生時はそのままエラーで終了させます。

パフォーマンスと可読性


 パフォーマンスが必要な場合は、可読性が落ちてもパフォーマンスを優先します。 その場合「performance optimized」のコメントを入れます。
 ただし、可読性を落としたパフォーマンス向上が有効なケースは経験上ごくわずかしかありません。 本当に必要かどうか、確認してから実施するものとします。

ディレクトリ(フォルダ)構成


 ディレクトリ構成は以下です。
blog.kojibm.net/
├── js/           # メインの js/mjs ファイル
├── css/          # スタイルシート  
├── img/          # 画像リソース
├── snd/          # サウンドリソース
├── html/         # htmlドキュメント
├── cgi/          # cgiプログラム
├── (公開用)/     # 必要に応じて追加
└── _(非公開用)/  # 開発用ディレクトリ

 「_」(アンダースコア)始まりは開発用ディレクトリです。 記事の原文や、各種テンプレート、テストコードなどが入ります。
 また、各ディレクトリ以下には必要に応じて同シリーズごとにサブディレクトリを持つものとします。

サブディレクトリ例

blog.kojibm.net/
├┬─ js/           # メインの js/mjs ファイル
│├── shogi/      # 自動生成☆詰将棋用ファイル
│
├┬─ img/          # 画像リソース
│├── shogi/      # 自動生成☆詰将棋用ファイル
│

[PR]

まとめ


 当ブログのコーディングルールを解説しました。
 筆者の40年以上のプログラミング経験と、小さいチームを含めると30年以上のチームリーダー経験に基づき、どうしてそのルールに決めたのかの理由も解説しています。 ルールそのものよりも、その策定理由が参考になると思います。 ある意味では、筆者のここまでの集大成とも言える記事になりました。
 コーディングルールに限りませんが、組織運営のためのルールは、実は策定よりも運用が重要です。 例えば納得のいかないプログラマーがルール違反を犯し続けて是正できない運用では、むしろルールが策定されている方が害悪です。
 チーム内コミュニケーションをしっかり取り、必要最低限の、運用可能なコーディングルールを策定しましょう。 それに向けて、少しでも本記事が参考になりましたら幸いです。

補足

  • 本ルールは筆者の経験に基づき新たに策定したものです。特定組織のルール転用ではありません。ただし結果的に類似している箇所もあります。
  • 記事の校正/添削に生成AIの Anthropic Claude を利用しております。
  • 画像内のラスタライズ文字フォントにOpen Font LicenseNoto Sans Japaneseを使用しております。
  • 画像内のラスタライズ文字フォントにOpen Font LicenseBIZ UDGothicを使用しております。
  • (本記事公開後)主に基本的な原則についてルール追記しました。
  • (本記事公開後)使用フォント誤記を訂正しました。


カテゴリー:プログラミング解説
[PR]