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

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

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

什麼是硬編碼

硬編碼是指,直接把值寫進程式碼裡。

魔術數字

例如,以下的 80 就不容易看出意思。

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

這種值就稱為所謂的 魔術數字

在這種情況下,如果替 80 加上一個表示其意義的名稱,意圖就會更容易傳達。

const PASSING_SCORE = 80;

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

這樣一來,就能看出 80 不只是單純的數字,而是代表及格分數

這種幫值命名的做法,即使該值只會使用一次,只要它在業務上有重要意義,我也認為是有價值的。

const MAX_LOGIN_ATTEMPTS = 5;

在這個例子裡,重點不是重複使用 5

而是要能在程式碼中看出,5 代表「最大登入嘗試次數」

也就是說,將值定義成常數,也是 賦予數值意義 的手段之一。

會隨環境改變的值

以下這類值也不建議直接寫死在程式碼中。

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

像 API URL 這種值,可能會因為開發環境、測試環境等不同環境而改變。

如果各環境的值不同,將其抽出成環境變數或設定檔會比較自然。

const apiUrl = process.env.API_BASE_URL;

特別是以下這些值,應該盡量避免硬編碼:

  • API URL
  • 資料庫連線資訊
  • 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 使用的常數放置處

判斷基準

如果猶豫要不要硬編碼,可以從以下幾點來判斷:

  • 這個值未來有可能變更嗎?
    • 定義成常數後,可以減少修改處
  • 這個值會在多個地方使用嗎?
    • 定義成常數後,可以減少修改處
  • 這個值有業務上的意義嗎?
    • 可以明確表達字串或數字的意義

只要符合其中一項,就值得考慮常數化、設定化或環境變數化。

總結

我原本一直認為,硬編碼是應該被徹底消滅的壞東西。這次反過來思考什麼情況下可以被允許,也算是動腦筋的一種練習。

這次雖然沒有把它列成條目,不過如果是想先快速做出來驗證,或是遇到超緊急狀況必須先修正並發布(後續修正也是必要的)等等,依情況來說也可能是可以接受的。

實務上還是依照團隊和專案方針來決定吧。


原文出處:https://qiita.com/musenmai/items/b525b64882548d4aec0d


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝11   💬1   ❤️6
329
🥈
我愛JS
📝2   💬6   ❤️3
113
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登