1行で済むはずの修正依頼を受けて検索したら、同じ値がソースコードのあちこちに埋まっているのを見つけて、画面を見つめている保守運用の担当者

ハードコードされた値の洗い出し|設定へ外出しする安全な手順

「お問い合わせフォームの送信先を、新しいアドレスに変えてもらえますか」

1行直せば終わる、と思って検索をかけると、同じメールアドレスが画面いっぱいに出てきます。フォームの処理、完了メールのテンプレート、管理画面、夜間バッチ、なぜか古いテスト用のファイルにまで。数えてみたら十数か所。しかも、どれが今も使われているのか分からない。

ここで手が止まるのは、慎重だからです。雑に全部置き換えれば一瞬ですが、どこかで何かが壊れそうな気配がする。その感覚は正しいです。

ハードコード(設定で変えるべき値を、プログラムの中に直接書き込んである状態)は、書いた人の怠慢というより、「急ぎの改修」と「当時は変わらないと思っていた」が積み重なった跡です。責める相手はいません。この記事では、それを少しずつ外へ出していく順番を、一緒に整理していきます。

結論:やることは4つです。①まず探して一覧にする(直すのは後。数と場所を先に知る)、②値の「行き先」を仕分ける(設定へ/秘密なら金庫へ/コードに残してよいもの)、③振る舞いを変えずに、1種類ずつ外へ出す(まず1か所の定数に集め、動作を確かめてから設定へ)、④移したあと、古い値が残っていないかもう一度探す。一度に全部やる必要はありません。今日の依頼で触る1種類からで十分です。

なお、言語やフレームワーク、サーバーの構成によって、設定の置き場所や読み込み方は変わります。ここで挙げるのは「進め方の型」です。コード例はPHPで書いていますが、自分の現場の書き方に置き換えながら読んでみてください。

ハードコードされた値は、なぜ増えるのか

まず、何が起きているのかを落ち着いて見ておきます。ハードコードは、たいていこんな経緯で増えます。

困るのは、値が変わる日が来たときです。直す場所が分からない、直し漏れが出る、直したつもりで一部だけ古いまま動き続ける。「一部だけ古い」がいちばんやっかいで、エラーにならないので誰も気づきません。

だからこそ、最初の一手は置き換えではありません。探して、数えることです。

ステップ1:ハードコードされた値を探して、一覧にする

いきなり直し始めず、まず全体を見ます。探すときは、「これが変わったら困る値」の形で検索するのが近道です。

Gitで管理しているなら git grep が便利です(管理されているファイルだけを探すので、ライブラリやログに埋もれにくくなります)。まだGitに載っていないなら、バージョン管理なしのソースをGitに載せる手順を先にやっておくと、この後の作業がぐっと安全になります。

# URL(外部APIや自社の別サイトなど)
git grep -n -E "https?://" -- '*.php' '*.js' '*.html'

# メールアドレス
git grep -n -E "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}"

# IPアドレス(接続先のサーバーが直書きされていないか)
git grep -n -E "\b[0-9]{1,3}(\.[0-9]{1,3}){3}\b"

# サーバー上の絶対パス
git grep -n -E "/home/|/var/www/|/usr/local/"

# 今回の依頼で変える値そのもの(まずはこれがいちばん確実)
git grep -n "info@old-example.co.jp"

Gitがない場合は、grep で同じことができます。ライブラリの置き場所は外しておくと、結果が読める量になります。

grep -rn --exclude-dir={vendor,node_modules,.git} "info@old-example.co.jp" /var/www/app/

見落としやすい場所も、ここで一緒に確かめておきます。

探した結果は、表にして1枚にまとめます。この段階ではまだ何も直しません。

値意味見つかった場所変わる頻度行き先
info@old-example.co.jp問い合わせ通知の宛先7か所(フォーム・バッチ・テンプレート)年に数回設定へ
https://api.example-pay.jp/v1決済APIの接続先3か所環境ごとに違う設定へ
sk_live_xxxx決済APIの秘密鍵1か所漏れたら即交換金庫へ
1.1税込計算の係数5か所制度が変われば設定へ(要確認)
20一覧の表示件数4か所ほぼ変わらない定数に集める

「見つかった場所」の件数が、そのまま作業の大きさです。ここが見えるだけで、依頼者に「今日できること」と「少し時間がかかること」を分けて伝えられるようになります。

ステップ2:値の「行き先」を3つに仕分ける

ソースコードから見つけたハードコードの値を、設定ファイルへ出すもの、金庫に入れる秘密の情報、コードに残してよいものの3つに仕分ける考え方を示した図
見つけた値は、行き先を3つに分けてから動かす。全部を設定に出す必要はない

一覧ができたら、それぞれの値をどこへ動かすかを決めます。全部を設定ファイルに出せばいい、というわけではありません。

迷ったら、「この値が変わるとき、誰が、どのくらいの頻度で変えたいか」を考えてみてください。依頼者から年に何度か変更依頼が来る値なら設定へ。開発者しか触らない値なら、定数で十分なことが多いです。

「環境ごとに違う値」の考え方は、環境差で事故らない設定管理でもう少し詳しく整理しています。あわせて読むと、置き場所の決め方が楽になります。

ステップ3:振る舞いを変えずに、1種類ずつ外へ出す

ここが本番です。コツは、「場所を変える」と「値を変える」を同じ日にやらないことです。

いきなり「新しいアドレスに変えつつ、設定ファイルから読むように書き換える」と、動かなかったときに原因が2つに増えます。順番を分けると、どこで壊れたかがすぐ分かります。

3-1. まず、同じ値を1か所の定数に集める

値はそのまま、置き場所だけを1か所にまとめます。この段階では、画面の動きは何も変わらないはずです。

<?php
// 変更前:7か所に同じアドレスが直書きされている
mail('info@old-example.co.jp', $subject, $body);

// 変更後:1か所に集めて、そこを参照する(値はまだ変えない)
// config/app.php
const CONTACT_NOTIFY_TO = 'info@old-example.co.jp';

// 使う側
mail(CONTACT_NOTIFY_TO, $subject, $body);

置き換えたら、フォームの送信・バッチ・管理画面など、見つかった場所の機能をひと通り動かして、これまでと同じ宛先に届くことを確かめます。触る範囲の見積もりは改修の影響範囲を調べる手順が役に立ちます。

3-2. 次に、設定ファイルから読むようにする

1か所に集まったら、その1か所だけを「設定から読む」形に変えます。

<?php
// config/app.php
$config = require __DIR__ . '/../config/settings.php';  // Gitに入れない本番用の設定ファイル

if (empty($config['contact_notify_to'])) {
    // 値が入っていないときは、黙って動かずに止める
    throw new RuntimeException('contact_notify_to が設定されていません');
}
define('CONTACT_NOTIFY_TO', $config['contact_notify_to']);

ここで1つ気をつけたいのが、「値がないとき、どう動くか」です。

設定が読めなかったときに、コードの中の既定値(たとえば本番のアドレス)へ黙って戻る作りにすると、検証環境から本番の宛先へメールが飛ぶ、ということが起きます。宛先や接続先のように「間違うと外に影響する値」は、値がなければ止まる形にしておくほうが安全です(メール誤送信を防ぐリリース手順でも同じ考え方を扱っています)。

設定の渡し方として環境変数を使う現場も多いと思います。ただ、PHP-FPMでは設定(clear_env)によってサーバーの環境変数がPHPまで届かないことがあります。環境変数にする場合は、php-fpm のプール設定でどう渡すかまで含めて検証環境で確かめてください。一人運用なら、権限を絞ったPHPの設定ファイル1枚のほうが、仕組みが見えやすく迷いにくいこともあります。

3-3. 値を変えるのは、そのあと

置き場所の移動が本番で問題なく動いたのを確かめてから、はじめて設定ファイルの値を新しいアドレスに変えます。ここまで来れば、変更は設定ファイルの1行です。反映の手順は設定ファイル変更を安全に反映する手順のとおり、退避・差分・反映・確認で通せば大丈夫です。

1回のリリースで外に出すのは、1種類の値までを目安にしてください。「メールアドレス」と「決済APIのURL」を同じ日にまとめると、何かあったときの切り分けが一気に難しくなります。

ステップ4:移したあと、古い値が残っていないか探し直す

外出しが終わったら、最初と同じ検索をもう一度かけます。

# 古い値が、設定ファイル以外に残っていないか
git grep -n "info@old-example.co.jp"

# データベースの中に残っていないか(テーブル名・カラム名は環境に合わせて)
# SELECT id, name FROM mail_templates WHERE body LIKE '%info@old-example.co.jp%';

ここで0件(または設定ファイルの1件だけ)になっていれば、完了です。残っていたら、それは直し漏れか、「残す」と決めた場所のどちらかです。残すなら、一覧表にその理由を1行書いておきます。

最後に、一覧表の「行き先」の列に、移した日付と設定のキー名を書き足しておきましょう。次に誰かが「この値、どこで変えるの?」と迷ったとき、この表が答えになります。

具体例:現場でよく見る4つのハードコード

どの例にも共通するのは、危ないのは値そのものではなく、「どこにあるか分からないこと」だということです。場所が分かっていれば、変更は怖くありません。

影響:外出しを進めると、何が変わるか

全部を外に出し切る必要はありません。変更依頼が来た値から1つずつ進めるだけで、半年後にはよく触る値のほとんどが1か所に集まっているはずです。

明日やること

次にハードコードの値を直す依頼が来たら、この順でどうぞ。

  1. その値で検索して、件数と場所を数える。コードだけでなく、テンプレート・cron・データベースも見る。
  2. 見つけた場所を表に書く。値・意味・場所・変わる頻度・行き先の5列で十分です。
  3. 行き先を決める。設定へ/金庫へ/コードに残す。秘密の情報が見つかったら、それは別件として優先して扱う。
  4. まず定数1か所に集める。値は変えずに、動きが変わらないことを確かめる。
  5. 次に設定から読む形にする。値がないときは止まるようにしておく。
  6. 最後に値を変えて、もう一度検索する。古い値が0件になったら完了。表に日付とキー名を書き足す。

今日やるのは、1の「検索して数える」だけでも十分です。数が分かれば、依頼者への返事も、作業の見積もりも、ずっと落ち着いて出せるようになります。

ハードコード外出しチェックリスト

全部そろわなくても大丈夫です。最初の3つができていれば、もう「どこにあるか分からない」状態からは抜け出せています。

よければ、こちらも

散らばっていた値を1か所にまとめ終えて、整理された一覧を眺めながら穏やかに微笑んでいる保守運用の担当者

ハードコードの値を外に出す仕事は、終わっても画面の見た目は何も変わりません。依頼者から見れば「アドレスが変わった」だけ。でもその裏で、次の変更が1行で済む状態を、あなたが作っています。

散らばった値の一覧を前にすると気が重くなりますが、全部を一度に片づける必要はありません。今日の依頼で触る1種類を、1か所に集める。それを繰り返すだけで、システムは少しずつ、あなたが変えやすい形に近づいていきます。

まずは次の依頼のとき、直す前に一度だけ検索して、数を数えるところから始めてみてください。

ほかの実務ヒントは記事一覧からどうぞ。保守運用の小さな備えを、メールでも少しずつお届けしています。

関連用語