開発におけるハードコード:その意味、リスク、回避方法

著者: IT Sectr 公開日: 2026-07-31 読了時間: 7 分

“釘付け”や“ハードコード”は、設定や構成に値を外出しせずに、プログラムコードに直接値を固定することを意味する業界用語です。ハードコードは開発において最もよく知られたアンチパターンの一つであり、コードの柔軟性と再利用性を低下させます。Refactoring Guruによると、ハードコードはテスト、保守、および異なる環境へのアプリケーションの適応を困難にします。ハードコードの代わりに意識的に定数を使用することは、成熟したアーキテクチャの証です。

重要なポイント

  • ハードコードする — ソースコードに特定の値を直接記述すること
  • ハードコードは柔軟性の喪失と保守の困難さからアンチパターンと見なされる
  • 例外:数学定数、配列サイズ、デフォルト値
  • 代替手段:設定ファイル、環境変数、リソース
  • ハードコードのリファクタリングはコードのテスタビリティと拡張性を向上させる

“釘付け”と“ハードコード”の意味

ハードコードする(釘付けにする) — プログラムコードに特定の値を、変更するにはソースコードの編集とアプリケーションの再コンパイルが必要となるように組み込むこと。“釘付け”というメタファーは本質を正確に反映しています。値は永久に固定され、コードから引き剥がすには努力が必要です。

ハードコードの例 — 関数の本体に文字列として直接書かれたサーバーURL。サーバーが別のアドレスに移行した場合、開発者はコード内の文字列を見つけ、変更し、アプリケーションを再ビルドしてリリースする必要があります。適切なアーキテクチャを持つアプリケーションでは、そのURLは設定ファイル、環境変数、または設定サービスに外部化されているでしょう。

“釘付け”という用語はより感情的なニュアンスを持ちます。値が恒久的に挿入され、迅速な交換が不可能であることを強調しています。ロシア語圏では、両方の表現が否定的なニュアンスを持つ完全な同義語として使用されます。ハードコードは皮肉を込めて“定数から取り出した別の定数の中の定数”と呼ばれることもあります。

ハードコードがアンチパターンとされる理由

ハードコードは、コードの保守性、テスタビリティ、拡張性の原則に違反するため、アンチパターンです。値が“釘付け”されたコードでは、環境、デザイン、ロジックの変更ごとに、ソース内での手動検索と置換が必要になります。これによりエラーのリスクが高まり、開発が遅れます。

典型的なモバイルアプリケーションを例に、ハードコードの具体的な結果を考えてみましょう。すべてのボタンのマージンがコード内の数値で指定され、リソース経由ではない場合 — デザイン変更にはすべての出現箇所の検索と置換が必要になります。エンドポイントURLが固定されている場合 — 環境(dev、stage、prod)の切り替えは再ビルドなしでは不可能です。

結果説明重大度
保守の複雑さ変更にはコード全体の検索が必要
コピー時のエラーすべての出現箇所が見つかり置換されるとは限らない
テスティング不能テストデータを差し替えられない
ローカライゼーションの問題コード内のテキストが翻訳されない
コードレビューの複雑化レビュアーがすべてのコンテキストを把握する必要がある

悪いハードコードの例

マジックナンバーと固定文字列を使用する関数は、ハードコードの典型です。1ヶ月後には、作者でさえ18、0.07、2.5が何を意味するか覚えていないでしょう。1年後には — チームの誰もロジックを壊すことを恐れてこれらの数値を変更しようとしません。値を名前付き定数に抽出することで、コードは自己文書化されます。

kotlin
// 悪い例:マジックナンバーと文字列
fun calculatePrice(base: Double): Double {
    val tax = base * 0.07
    val tip = base * 0.15
    val discount = if (base > 100) 10 else 0
    return base + tax + tip - discount
}

テスティングへの影響

ハードコードされたデータベースURLでは、ローカルのインメモリデータベースでテストを実行できません。開発者はテスト前に本格的なサーバーを起動するかコードを編集する必要があります。コードから設定を外部化することで問題が解決します。テストはテストパラメータを、本番環境は実際のパラメータを使用し、コードは変更されません。

ハードコードが正当化されるケース:例外ルール

ハードコードはアンチパターンですが、固定値が許容されるだけでなく推奨される正当な例外が存在します。境界線は可変性の軸に沿っています。値がアプリケーションのライフサイクル内で決して、またはほとんど決して変更されない場合、ハードコードしても問題ありません。変更される可能性がある場合は — 設定に外部化してください。

数学定数や物理定数 — 円周率、重力加速度、1秒あたりのミリ秒数 — はハードコードしても安全です。これらは自然または標準によって定義され、変更されることはありません。仕様で定義された定数配列のサイズも固定できますが、数値の出所に関するコメントを付けてください。

正当化されるハードコードの例

1秒あたりのミリ秒数は、時間標準によって定義された安定した定数です。これは決して変更されないため、設定に外部化する意味はありません。ただし、そのような定数でも、コードに“マジックナンバー”が含まれないよう、意味のある名前で宣言する方が良いです。1000ではなくMILLISECONDS_IN_SECONDと書きましょう。

kotlin
// 正当化されるハードコード:安定した定数
private const val MILLIS_IN_SECOND = 1000
private const val LOGIN_TIMEOUT_SECONDS = 30

fun formatDuration(ms: Long): String {
    val seconds = ms / MILLIS_IN_SECOND
    return "${seconds} sec."
}

ハードコードの代替手段:設定、ENV、DI

ハードコードを回避する実証済みの方法がいくつかあり、それぞれ値のタイプに適しています。代替手段の選択は、値が変更される頻度と、誰が変更するか(開発者、デブオプス、エンドユーザー)によって異なります。

設定ファイル

サーバーURL、APIキー、フィーチャーフラグには、JSON、YAML、TOML形式の設定ファイルを使用してください。Androidではbuild.gradleのbuildConfigFieldやres/values/config.xmlを使用します。iOSでは — Info.plistやxcconfig。設定ファイルはアプリケーションと一緒にビルドされますが、ビルドスキームごとに異なるものにできます。

環境変数

シークレット(トークン、パスワード)や環境パラメータには環境変数を使用してください。これらはリポジトリに入らず、dev、stage、prodサーバーで異なる値にできます。モバイル開発では、環境変数はXcodeのビルドスキームやGradleのビルドフレーバーを通じてエミュレートされることがよくあります。

アプリケーションリソース

文字列、色、サイズ、画像はリソースファイルに外部化する必要があります。Androidではstrings.xml、iOSではLocalizable.strings、FlutterではARBファイル。これにより、ローカライゼーション、異なる画面への適応、ダークテーマが容易になります。リソース内の文字列を変更しても、コードを書き換える必要はありません。

xml
<!-- Android: res/values/strings.xml -->
<resources>
    <string name="app_name">MyApp</string>
    <string name="api_base_url">https://api.example.com</string>
</resources>

依存性注入(DI)

サービスやプロバイダーには、AndroidではDagger、Hilt、Koin、iOSではSwinjectを使用した依存性注入を利用してください。DIフレームワークは — テスト用、異なる環境用、異なるユーザー用に — 実装を動的に差し替えることを可能にします。これは抽象化の最高レベルであり、値の“固定”が外部からの注入に置き換えられます。

ハードコードされたコードのリファクタリング方法

ハードコードのリファクタリングは、固定された値を設定やリソースに抽出するプロセスです。これは、系統立てて行えば、最も安全なリファクタリング操作の一つです。以下に説明する手順は、あらゆる言語とプラットフォームに適しています。

ステップ1:すべてのマジックナンバーと文字列を見つける

IDE(Search in Project)やスクリプトを使用して検索できます。文字列、URL、数値リテラル、サイズ、タイムアウトを探します。特に注意が必要なのは — 繰り返し出現する値です。同じ数値が5箇所に出現する場合、それは定数に抽出する候補です。grepやIDEA / Xcodeの内蔵検索を使用してください。

ステップ2:名前付き定数に置き換える

見つかった各値に対して、意味のある名前を持つ定数を作成します。定数をモジュールやクラスごとにグループ化します。名前は値の意味を説明するものであり、使用方法を説明するものではありません。API_TIMEOUTであってTIMEOUT_30ではありません。置き換え後、コード内に説明なしの数値が残ってはいけません。

swift
// 以前:マジックナンバー 0.4
let cardHeight = screenHeight * 0.4

// 以後:名前付き定数
private let cardHeightRatio: CGFloat = 0.4
let cardHeight = screenHeight * cardHeightRatio

ステップ3:設定またはリソースに抽出する

値がビルド間や環境間で変わる可能性がある場合は — 設定ファイルまたはアプリケーションリソースに抽出します。文字列にはローカライゼーションファイルを使用します。URLには — build configまたはxcconfig。サイズには — リソースファイル(Androidのdimens.xml)。抽出後もアプリケーションが正しくビルドされ動作することを確認してください。

ステップ4:テストを書く

リファクタリング後、設定が正しく読み込まれ、値が期待通りであることを確認するテストを書きます。将来誰かが設定を変更した場合、テストが不一致を指摘します。設定のテストは — リグレッションを防ぐ迅速で信頼性の高い方法です。

ステップ5:重複を削除する

設定に抽出した後、古い値を使用していたすべての箇所が単一のソースを参照していることを確認します。コメントアウトされたコードや使用されなくなった古い定数を削除します。リファクタリングを、どの値がどこに抽出されたかを説明するコミットメッセージで完了します。

よくある質問

プログラミングにおける“ハードコード”とは何ですか?

ハードコードする — 設定やリソースに抽出せずに、ソースコードに直接値を書き込むこと。これによりコードの柔軟性が低下し、保守が難しくなります。

なぜハードコードは悪い慣行とされるのですか?

ハードコードはアプリケーションの動作変更を困難にし、テストを妨げ、重複を生み出し、コピー時のエラーリスクを高めます。ハードコードされた値を変更するには、アプリケーションの再ビルドと再リリースが必要です。

ハードコードが許容されるのはどのような場合ですか?

許容されるのは、数学定数、アプリケーションのライフサイクル内で変更されない安定した値、および一時的なプロトタイプの場合です。本番環境では、定数でさえ名前付き変数に抽出するべきです。

既存のコードのハードコードを置き換えるには?

検索ですべてのマジックナンバーを見つけ、名前付き定数に置き換えるか、設定ファイルに抽出します。設定の読み込みを確認するテストを書き、重複を削除し、変更内容を説明するコミットメッセージでコミットします。

定数とハードコードの違いは何ですか?

定数 — コード内の名前付きの値で、一箇所で変更可能です。ハードコード — コード中に散在する名前のない値。良い慣行:常に意味のある名前の付いた名前付き定数を使用すること。

まとめ

  • ハードコードする(釘付けにする) — 迅速な置換が不可能な状態でコードに値を書き込むこと
  • ハードコード — 保守、テスト、拡張性を損なうアンチパターン
  • マジックナンバーと名前のない文字列 — 最も一般的なハードコードの形態
  • 例外:数学定数と安定したデフォルト値
  • 代替手段:設定ファイル、リソース、ENV、DIコンテナ
  • ハードコードのリファクタリングは、重複の検索と名前付き定数への置換から始まります
  • リファクタリング後、設定読み込みのテストを書きましょう

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください