5
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ハードコーディングは本当に悪なのか

5
Posted at

つい先日、プロジェクト内に点在するハードコーディングを滅ぼすタスクを行っていました。

滅ぼしながらも「ハードコーディングが許されるパターンはないのか?」ということが気になり、調べてみました。

簡単にいうと、大事なのは、ハードコーディングされた値の意味・変更理由・影響範囲がコードから読み取れるか であり、場合によってはハードコーディングを許容してもいいのかもしれないと思えました。

ハードコーディングとは

ハードコーディングとは、値をコードの中に直接書くことです。

マジックナンバー

例えば、以下の 80 は意味が分かりにくいです。

if (score >= 80) {
  return "passed";
}

このような値は、いわゆる マジックナンバー と呼ばれます。

この場合は、80が何の数字なのかを表す名前を付けると、意図が伝わりやすくなります。

const PASSING_SCORE = 80;

if (score >= PASSING_SCORE) {
  return "passed";
}

こうすることで、80 が単なる数値ではなく、合格点を意味していることが分かります。

このように名前をつけることは、1回しか使わない値だとしても、業務上の意味が重要なら価値があると思います。

const MAX_LOGIN_ATTEMPTS = 5;

この場合、大事なのは 5 を再利用することではありません。

5 が「最大ログイン試行回数」を意味しているとコード上で分かることです。

つまり、定数化は 値に意味を与えるための手段 でもあります。

環境によって変わる値

以下のような値もコードに直接書かない方がよいです。

const apiUrl = "https://prod.example.com";

API URLのような値は、開発環境や検証環境などの環境によって変わる可能性があります。

それぞれで値が変わるなら、環境変数や設定ファイルに切り出す方が自然です。

const apiUrl = process.env.API_BASE_URL;

特に、以下のような値はハードコーディングを避けるべきです。

  • API URL
  • DB接続情報
  • APIキー
  • パスワード
  • 外部サービスのエンドポイント
  • 環境ごとに変わる設定値

これらはコードに直接書くと、変更しづらいだけでなく、セキュリティ上の問題にもつながります。

ハードコーディングが許容される(かもしれない)場合

局所的で意図が明確なもの

一方で、すべてのリテラルを定数化すればよいわけでもありません。

例えば、テストコードで以下のように書く場合、この 201 はHTTPステータスコードとして比較的意味が明確です。

expect(response.status).toBe(201);

これを毎回次のように書くと、かえって読みにくくなることもあります。

const CREATED_STATUS_CODE = 201;

expect(response.status).toBe(CREATED_STATUS_CODE);

もちろん、プロジェクトの方針や文脈によります。

ただ、局所的で意味が明確な値まで何でも定数化すると、読む人が定義元を追う手間が増えます。

テストコードの一部

テストコードでは、期待値を直接書いた方が読みやすい場合があります。

expect(user.name).toBe("Taro");
expect(user.age).toBe(20);

このような値は、そのテストケースの入力・期待値としてその場で読める方が分かりやすいことがあります。

一方で、テストでも業務ルールに関わる値は定数化した方がよいです。

const RESERVATION_CAPACITY_LIMIT = 10;

expect(result.capacity).toBe(RESERVATION_CAPACITY_LIMIT);

おまけ: 定数の置き場所にも注意する

定数化するときは、置き場所も重要です。

定数置き場として、 constants.ts を作成しこれに集約する、というやり方があるかと思います。

最初は便利に見えます。しかし、プロジェクトが大きくなると、何のための定数なのか分かりにくくなります。

// constants.ts
export const ADMIN = "admin";
export const LIMIT = 10;
export const DEFAULT_NAME = "guest";

このような巨大な共通定数ファイルは、かえって依存関係を分かりにくくすることがあります。

まずは、使う場所の近くに置くのがよいです。

const MAX_LOGIN_ATTEMPTS = 5;

function canRetryLogin(attemptCount: number): boolean {
  return attemptCount < MAX_LOGIN_ATTEMPTS;
}

そして、本当に複数箇所で共有する必要が出てきたら、共通化を検討します。

特定のドメインでしか使わない定数なら、それが分かるような場所にconstants.tsを作るのもいいと思います。

domain/
├─ user/
└─ post/
   ├─ index.ts
   └─ constants.ts // postでしか使わない定数置き場

判断基準

ハードコーディングしてよいか迷ったら、以下を考えると判断しやすいです。

  • その値は変更される可能性があるか?
    • 定数化すると、変更箇所を減らすことができる
  • 同じ値を複数箇所で使っているか?
    • 定数化すると、変更箇所を減らすことができる
  • その値に業務上の意味があるか?
    • 文字列や数値の意味を明示できる

1つでも当てはまるなら、定数化・設定化・環境変数化を検討する価値があります。

まとめ

ハードコーディングは、常に滅ぶべき悪だと考えていました。今回、逆にどのような時なら許されるのかを考えることで、頭の体操になりました。

今回は項目として立てることはやめましたが、とにかく早く作って検証したい場合、超緊急で修正&リリースしないといけない場合(後の修正は必須)なども、場合によっては許されるかもしれないです。

実際の運用はチームやプロジェクトの方針に従いましょうね。

5
7
0

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
5
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?