設定値の管理設計とは、アプリケーションの動きを変える値を「どこに置き、どの順で採用し、いつ反映するか」を決めることです。 置き場所は、値の性質(環境で変わるか、秘密か、稼働中に変えるか)で決まります。

この記事では、置き場所の判定表、設定項目一覧のテンプレートと記入例、優先順位を解決するコードと実行結果、起動時の検証、似た概念との違いをまとめます。.env ファイルの書き方やサーバーの設定項目の一覧ではなく、置き場所と優先順位の設計 に絞ります。

確認環境(確認日 2026-10-06):Ubuntu 24.04、Python 3.11.15、OpenJDK 21.0.10、PostgreSQL 16.15。Spring Boot の内容は、今回開いた公式リファレンス(Spring Boot 4.1.1「Externalized Configuration」)の記載にもとづきます。

この記事の結論

  • 環境で変わる値は環境変数(接続先のホスト名、ポート、ログレベル)。
  • 構造を持つ既定値は設定ファイル(リポジトリに入れ、レビューを通す)。
  • 稼働中に変える業務値は DB(上限件数、表示の切り替え。変更者と日時を残す)。
  • 秘密情報はシークレット管理に保管し、起動時に環境変数として渡す(設定ファイルにも DB にも書かない)。
  • 採用する順は 引数 > 環境変数 > 設定ファイル > 既定値。必須の値が無ければ 起動時に止める。

設定値の置き場所の判定表

置き場所 向いている値 例 変更の反映 履歴の残り方 向かない値
コード内の既定値 どの環境でも同じで、無くても動く初期値 ページあたり件数 50、タイムアウト 30秒 デプロイ Git 環境ごとに違う値、秘密情報
設定ファイル 階層や一覧などの構造を持つ既定値 ログの出力設定、機能ごとの上限のまとまり デプロイ(再起動) Git(レビューつき) 秘密情報
環境変数 環境ごとに変わる値、起動時に渡す秘密情報 DB のホスト名、ログレベル、API キー 再起動 基盤側の設定履歴 入れ子の構造、長い一覧
起動引数 その回の実行だけ変えたい値 バッチの対象日、検証用のポート その実行のみ 実行ログ 秘密情報(プロセス一覧に見える)
DB(設定テーブル) 稼働中に担当者が変える業務値 予約できる日数の上限、お知らせ表示 即時〜数分(読み直しの方式による) 更新者・更新日時の列、監査ログ DB の接続先そのもの、秘密情報
シークレット管理 パスワード、API キー、署名鍵 DB パスワード、外部 API のトークン 再起動またはローテーション時 保管側のアクセス記録 秘密でない値(探しにくくなる)

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「設定値の管理設計|環境変数・設定ファイル・DBの使い分けと優先順位」

置き場所を決める4つの質問

上から順に答え、最初に「はい」になったところが置き場所です。

順 質問 「はい」のとき
1 漏れると困る値か シークレット管理に保管し、環境変数として渡す
2 再起動なしで、運用担当者が変える値か DB(設定テーブル)
3 環境(開発・検証・本番)で値が変わるか 環境変数
4 階層や一覧の構造を持つか 設定ファイル
— どれにも当てはまらない コード内の既定値、または定数

The Twelve-Factor App は、設定を「デプロイごとに変わりうるものすべて」と定義し、コードから厳密に分離して環境変数に置く方針を示しています。目安として、認証情報を漏らさずにコードをいつでも公開できる状態か を確かめる方法も挙げられています。この記事の判定表は、その方針に「構造を持つ既定値」と「稼働中に変える業務値」の置き場所を足したものです。

設定項目一覧のテンプレート

設定値は、一覧にして初めて「どこを見ればよいか」が決まります。

| No | キー | 説明 | 型 | 既定値 | 置き場所 | 環境変数名 | 必須 | 秘密 | 変更時の反映方法 | 値の範囲 |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 |  |  |  |  |  |  |  |  |  |  |
列 書くこと
キー アプリ内での名前。分類.項目 の形でそろえる
型 int/bool/string/duration など。単位が要るものは説明に書く(秒、件)
既定値 何も指定しないときの値。必須の値は「なし」
置き場所 既定値/設定ファイル/環境変数/DB/シークレット管理
必須 無いときに起動を止めるか
秘密 ログや画面に出すときに伏せるか
変更時の反映方法 再起動/デプロイ/即時/読み直しの間隔

記入例(架空の備品予約システム)

No キー 説明 型 既定値 置き場所 環境変数名 必須 秘密 変更時の反映方法 値の範囲
1 app.port 待ち受けポート int 8000 設定ファイル APP_PORT 任意 いいえ 再起動 1024〜65535
2 app.log_level ログレベル string WARNING 環境変数 APP_LOG_LEVEL 任意 いいえ 再起動 DEBUG/INFO/WARNING/ERROR
3 app.page_size 一覧の1ページの件数 int 50 設定ファイル APP_PAGE_SIZE 任意 いいえ デプロイ 10〜200
4 database.host DB のホスト名 string なし 環境変数 DATABASE_HOST 必須 いいえ 再起動 —
5 database.port DB のポート int 5432 設定ファイル DATABASE_PORT 任意 いいえ 再起動 1〜65535
6 database.pool_size 接続プールの数 int 10 既定値 DATABASE_POOL_SIZE 任意 いいえ 再起動 1〜100
7 database.password DB のパスワード string なし シークレット管理 DATABASE_PASSWORD 必須 はい 再起動 —
8 reservation.max_days_ahead 何日先まで予約できるか int 30 DB — 任意 いいえ 即時 1〜365
9 reservation.max_per_user 1人が同時に持てる予約数 int 3 DB — 任意 いいえ 即時 1〜20
10 feature.new_search 新しい検索画面を出すか bool false 環境変数 FEATURE_NEW_SEARCH 任意 いいえ 再起動 true/false

表計算ソフトに貼るときは、次のタブ区切りを使います。

No  キー  説明  型   既定値 置き場所    環境変数名   必須  秘密  変更時の反映方法    値の範囲
1   app.port    待ち受けポート int 8000    設定ファイル  APP_PORT    任意  いいえ 再起動 1024〜65535
2   app.log_level   ログレベル   string  WARNING 環境変数    APP_LOG_LEVEL   任意  いいえ 再起動 DEBUG/INFO/WARNING/ERROR
3   app.page_size   一覧の1ページの件数  int 50  設定ファイル  APP_PAGE_SIZE   任意  いいえ デプロイ    10〜200
4   database.host   DBのホスト名 string  なし  環境変数    DATABASE_HOST   必須  いいえ 再起動 —
5   database.port   DBのポート  int 5432    設定ファイル  DATABASE_PORT   任意  いいえ 再起動 1〜65535
6   database.pool_size  接続プールの数 int 10  既定値 DATABASE_POOL_SIZE  任意  いいえ 再起動 1〜100
7   database.password   DBのパスワード    string  なし  シークレット管理    DATABASE_PASSWORD   必須  はい  再起動 —
8   reservation.max_days_ahead  何日先まで予約できるか int 30  DB  —   任意  いいえ 即時  1〜365
9   reservation.max_per_user    1人が同時に持てる予約数    int 3   DB  —   任意  いいえ 即時  1〜20
10  feature.new_search  新しい検索画面を出すか bool    false   環境変数    FEATURE_NEW_SEARCH  任意  いいえ 再起動 true/false

優先順位の考え方:引数>環境変数>設定ファイル>既定値

同じキーが複数の場所にあるときは、実行する人に近いほうを優先 します。

順位 置き場所 誰がいつ決めるか
1(最優先) 起動引数 実行する人が、その回だけ
2 環境変数 基盤の担当者が、環境ごとに
3 設定ファイル 開発者が、リリースごとに
4 コード内の既定値 開発者が、最初に

この順にしておくと、「普段は設定ファイルの値、本番だけ環境変数で上書き、調査のときだけ引数で変える」という使い方ができます。DB の業務値はこの順位の外に置き、同じキーを DB と環境変数の両方に持たせない ようにすると、どちらが効いているかで迷いません。

Pythonで優先順位を解決するコードと実行結果

標準ライブラリだけで、4つの置き場所から値を決め、どこから採用したかも表示します。

# settings.toml  構造を持つ既定値(リポジトリに入れる。秘密情報は書かない)
[app]
port = 8080
log_level = "INFO"
page_size = 20

[database]
host = "localhost"
port = 5432
# config.py  引数 > 環境変数 > 設定ファイル > 既定値 の順で設定値を決める(Python 3.11、標準ライブラリのみ)
import argparse
import os
import sys
import tomllib

def to_bool(v) -> bool:
    if isinstance(v, bool):
        return v
    if str(v).lower() in ("1", "true", "yes", "on"):
        return True
    if str(v).lower() in ("0", "false", "no", "off"):
        return False
    raise ValueError(v)


# キー: (型, 既定値, 必須か, 秘密か)
SPEC = {
    "app.port":          (int, 8000, False, False),
    "app.log_level":     (str, "WARNING", False, False),
    "app.page_size":     (int, 50, False, False),
    "database.host":     (str, None, True, False),
    "database.port":     (int, 5432, False, False),
    "database.pool_size": (int, 10, False, False),
    "database.password": (str, None, True, True),
    "feature.new_search": (to_bool, False, False, False),
}


def to_env(key: str) -> str:
    # app.port -> APP_PORT / database.host -> DATABASE_HOST
    return key.replace(".", "_").upper()


def load_file(path: str) -> dict:
    if not os.path.exists(path):
        return {}
    with open(path, "rb") as f:
        data = tomllib.load(f)
    return {f"{sec}.{k}": v for sec, table in data.items() for k, v in table.items()}


def resolve(argv=None, environ=None, path="settings.toml"):
    environ = os.environ if environ is None else environ
    parser = argparse.ArgumentParser()
    for key in SPEC:
        parser.add_argument("--" + key.replace(".", "-").replace("_", "-"), dest=key)
    args = {k: v for k, v in vars(parser.parse_args(argv)).items() if v is not None}
    file_values = load_file(path)

    values, sources, errors = {}, {}, []
    for key, (typ, default, required, _secret) in SPEC.items():
        if key in args:
            raw, src = args[key], "引数"
        elif environ.get(to_env(key), "") != "":      # 空文字は「設定なし」として扱う
            raw, src = environ[to_env(key)], "環境変数"
        elif key in file_values:
            raw, src = file_values[key], "設定ファイル"
        elif not required:
            raw, src = default, "既定値"
        else:
            if required:
                errors.append(f"{key}: 必須の値がありません(環境変数 {to_env(key)} を設定してください)")
            continue
        try:
            values[key] = typ(raw)
            sources[key] = src
        except ValueError:
            errors.append(f"{key}: {raw!r} を {typ.__name__} に変換できません({src})")
    if errors:
        print("設定エラーのため起動を中止します:", file=sys.stderr)
        for e in errors:
            print("  - " + e, file=sys.stderr)
        sys.exit(1)
    return values, sources


if __name__ == "__main__":
    values, sources = resolve()
    for key, (_t, _d, _r, secret) in SPEC.items():
        shown = "********" if secret else values[key]
        print(f"{key:<20} = {shown!s:<10} ({sources[key]})")

引数・環境変数・設定ファイル・既定値が混ざった状態で実行した結果です。

$ DATABASE_PASSWORD=s3cret DATABASE_HOST=db.internal APP_LOG_LEVEL=DEBUG python3 config.py --app-port 9000
app.port             = 9000       (引数)
app.log_level        = DEBUG      (環境変数)
app.page_size        = 20         (設定ファイル)
database.host        = db.internal (環境変数)
database.port        = 5432       (設定ファイル)
database.pool_size   = 10         (既定値)
database.password    = ********   (環境変数)
feature.new_search   = False      (既定値)

app.port は設定ファイルに 8080、コードの既定値に 8000 がありますが、引数の 9000 が採用されています。パスワードは値を出さず、どこから来たかだけを表示しています。採用元を起動ログに出しておく と、「なぜこの値になっているか」をすぐに追えます。

起動時に必須値の欠落で止める

必須の値が無いまま起動すると、最初のリクエストが来るまで気づけません。起動時にまとめて検証し、足りないものを一度に全部表示して終了 します。上のコードの errors がその役割です。

必須の値を渡さずに起動した結果:

$ python3 config.py; echo "終了コード: $?"
設定エラーのため起動を中止します:
  - database.password: 必須の値がありません(環境変数 DATABASE_PASSWORD を設定してください)
終了コード: 1

環境変数が空文字のとき:

$ DATABASE_PASSWORD= DATABASE_HOST=db.internal python3 config.py; echo "終了コード: $?"
設定エラーのため起動を中止します:
  - database.password: 必須の値がありません(環境変数 DATABASE_PASSWORD を設定してください)
終了コード: 1

型が合わない値が複数あるとき:

$ DATABASE_PASSWORD=s3cret APP_PORT=abc FEATURE_NEW_SEARCH=maybe python3 config.py; echo "終了コード: $?"
設定エラーのため起動を中止します:
  - app.port: 'abc' を int に変換できません(環境変数)
  - feature.new_search: 'maybe' を to_bool に変換できません(環境変数)
終了コード: 1

真偽値に専用の変換関数(to_bool)を使っているのは、Python の bool() が文字列の中身を見ないためです。

$ python3 -c 'print(bool("false"), bool("0"), bool(""))'
True True False

FEATURE_NEW_SEARCH=false と書いたつもりが有効になる、という取り違えを防げます。

Javaで優先順位を解決するコードと実行結果

同じ順序を、Java 21 の標準ライブラリだけで書いた例です。

# application.properties  既定値をまとめたファイル
app.port=8080
app.log-level=INFO
app.page-size=20
database.host=localhost
database.port=5432
// ConfigDemo.java  引数 > 環境変数 > 設定ファイル > 既定値(Java 21、標準ライブラリのみ)
import java.io.IOException;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

public class ConfigDemo {
    record Item(String defaultValue, boolean required, boolean secret) {}

    static final Map<String, Item> SPEC = new LinkedHashMap<>();
    static {
        SPEC.put("app.port", new Item("8000", false, false));
        SPEC.put("app.log-level", new Item("WARNING", false, false));
        SPEC.put("app.page-size", new Item("50", false, false));
        SPEC.put("database.host", new Item(null, true, false));
        SPEC.put("database.port", new Item("5432", false, false));
        SPEC.put("database.pool-size", new Item("10", false, false));
        SPEC.put("database.password", new Item(null, true, true));
    }

    // app.log-level -> APP_LOGLEVEL(ドットを _ に、ハイフンを取り除き、大文字にする)
    static String toEnv(String key) {
        return key.replace(".", "_").replace("-", "").toUpperCase();
    }

    public static void main(String[] args) throws IOException {
        Map<String, String> argValues = new LinkedHashMap<>();
        for (String a : args) {                       // --app.port=9000 の形を読む
            if (a.startsWith("--") && a.contains("=")) {
                int i = a.indexOf('=');
                argValues.put(a.substring(2, i), a.substring(i + 1));
            }
        }
        Properties file = new Properties();
        Path path = Path.of("application.properties");
        if (Files.exists(path)) {
            try (Reader r = Files.newBufferedReader(path)) {
                file.load(r);
            }
        }
        Map<String, String> env = System.getenv();

        List<String> errors = new ArrayList<>();
        Map<String, String[]> resolved = new LinkedHashMap<>();
        SPEC.forEach((key, item) -> {
            String envValue = env.get(toEnv(key));
            if (argValues.containsKey(key)) {
                resolved.put(key, new String[] {argValues.get(key), "引数"});
            } else if (envValue != null && !envValue.isEmpty()) {
                resolved.put(key, new String[] {envValue, "環境変数"});
            } else if (file.containsKey(key)) {
                resolved.put(key, new String[] {file.getProperty(key), "設定ファイル"});
            } else if (!item.required()) {
                resolved.put(key, new String[] {item.defaultValue(), "既定値"});
            } else {
                errors.add(key + ": 必須の値がありません(環境変数 " + toEnv(key) + " を設定してください)");
            }
        });
        if (!errors.isEmpty()) {
            System.err.println("設定エラーのため起動を中止します:");
            errors.forEach(e -> System.err.println("  - " + e));
            System.exit(1);
        }
        resolved.forEach((key, v) -> {
            String shown = SPEC.get(key).secret() ? "********" : v[0];
            System.out.printf("%-20s = %-12s (%s)%n", key, shown, v[1]);
        });
    }
}
$ DATABASE_PASSWORD=s3cret DATABASE_HOST=db.internal APP_LOGLEVEL=DEBUG java ConfigDemo.java --app.port=9000
app.port             = 9000         (引数)
app.log-level        = DEBUG        (環境変数)
app.page-size        = 20           (設定ファイル)
database.host        = db.internal  (環境変数)
database.port        = 5432         (設定ファイル)
database.pool-size   = 10           (既定値)
database.password    = ********     (環境変数)
$ java ConfigDemo.java; echo "終了コード: $?"
設定エラーのため起動を中止します:
  - database.password: 必須の値がありません(環境変数 DATABASE_PASSWORD を設定してください)
終了コード: 1

端末の文字コードの設定によって日本語が ? で表示される場合は、java -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 ConfigDemo.java のように出力の文字コードを指定します。

Spring Bootの優先順位(公式リファレンスの順序)

Spring Boot は、この優先順位の解決を標準で備えています。Spring Boot 4.1.1 のリファレンス「Externalized Configuration」には、後に挙げたものが前のものを上書きする 順序として次の一覧があります(主なものを抜粋。番号はリファレンスの番号)。

番号 置き場所 この記事の分類
1 既定のプロパティ(SpringApplication.setDefaultProperties) 既定値
3 設定データ(application.properties や YAML) 設定ファイル
5 OS の環境変数 環境変数
6 Java のシステムプロパティ(-D で渡す値) 起動時の指定
10 SPRING_APPLICATION_JSON のプロパティ 環境変数(JSON でまとめて渡す)
11 コマンドライン引数 引数

番号が大きいほど優先されるので、引数 > 環境変数 > 設定ファイル > 既定値 の順と一致します。設定データの中にも順序があり、後のものが優先されます。

  1. jar の中の application.properties
  2. jar の中の application-{profile}.properties
  3. jar の外の application.properties
  4. jar の外の application-{profile}.properties

環境変数の名前については、多くの OS がピリオド区切りのキーを使えないため、アンダースコアで代用できる(spring.config.name の代わりに SPRING_CONFIG_NAME)とリファレンスに書かれています。

必須値の検証は、@ConfigurationProperties のクラスに @Validated と制約のアノテーションを付けます。リファレンスに載っている形は次のとおりです。

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
import jakarta.validation.constraints.NotEmpty;

@ConfigurationProperties("my.service")
@Validated
public class MyProperties {

    @NotEmpty
    private String username;

    public String getUsername() {
        return this.username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
}

稼働中に変える業務値をDBに置く

運用担当者が画面から変える値は、設定テーブルに置きます。誰がいつ変えたかを残せる ことが、環境変数や設定ファイルとの違いです。

-- 稼働中に変える業務値を置くテーブル(PostgreSQL 16)
CREATE TABLE app_settings (
    setting_key   varchar(100) PRIMARY KEY,
    setting_value text         NOT NULL,
    value_type    varchar(10)  NOT NULL CHECK (value_type IN ('int', 'bool', 'string')),
    description   text         NOT NULL,
    updated_by    varchar(50)  NOT NULL,
    updated_at    timestamptz  NOT NULL DEFAULT now()
);

INSERT INTO app_settings (setting_key, setting_value, value_type, description, updated_by) VALUES
  ('reservation.max_days_ahead', '30', 'int',  '何日先まで予約できるか', 'admin'),
  ('reservation.max_per_user',   '3',  'int',  '1人が同時に持てる予約数', 'admin'),
  ('notice.banner_enabled',      'false', 'bool', 'お知らせ帯を表示するか', 'admin');

UPDATE app_settings
   SET setting_value = '5', updated_by = 'sato', updated_at = now()
 WHERE setting_key = 'reservation.max_per_user';

SELECT setting_key, setting_value, value_type, updated_by FROM app_settings ORDER BY setting_key;
        setting_key         | setting_value | value_type | updated_by 
----------------------------+---------------+------------+------------
 notice.banner_enabled      | false         | bool       | admin
 reservation.max_days_ahead | 30            | int        | admin
 reservation.max_per_user   | 5             | int        | sato
(3 rows)

一覧に無い型を入れようとすると、CHECK 制約で止まります。

ERROR:  new row for relation "app_settings" violates check constraint "app_settings_value_type_check"

DB に置くときに決めておくことは次の3点です。

決めること 選択肢と目安
読み直しのタイミング リクエストごとに読む(常に最新。問い合わせが増える)/一定間隔で読み直す(数十秒〜数分の遅れを許容)/変更時に通知して読み直す
DB から読めないとき 直前に読めた値を使い続ける。初回から読めないときはコードの既定値を使う
値の検証 画面で保存するときに型と範囲を確かめる。値の列は文字列になるため、読む側でも型変換でエラーにする

設定値と定数・マスタデータ・機能フラグの違い

種類 何を表すか 変える人 変える頻度 置き場所 例
定数 仕様として決まっていて変わらない値 開発者(仕様変更時のみ) ほぼ無い コード 1日の秒数、状態コードの定義
設定値 動きを調整する値。環境や運用で変わる 開発者・基盤の担当者・運用担当者 リリースごと〜随時 既定値/設定ファイル/環境変数/DB 接続先、タイムアウト、上限件数
マスタデータ 業務で扱う対象の一覧 業務の担当者 随時 DB のマスタテーブル 部署、備品の種類、会議室
機能フラグ 機能を出すか出さないかの切り替え 開発者・リリースの担当者 リリースの前後に集中 環境変数、またはフラグ管理の仕組み 新しい検索画面の表示

どれにするかの判断基準

条件 選ぶもの
値を変えるとテストのやり直しが必要になる 定数(コードの変更として扱う)
行が増減し、ほかのテーブルから参照される マスタデータ
値が1つで、キーで引く 設定値
一時的な切り替えで、役目を終えたら消す 機能フラグ
環境ごとに値が違う 設定値(環境変数)

設定値にしすぎると、組み合わせの数が増えて確かめきれなくなります。変える予定のない値は定数のままにする ほうが、一覧が短く保てます。

確かめて分かったこと

  • 環境変数は 空文字でも「設定されている」扱い になる。DATABASE_PASSWORD= のように値を空で渡した場合に備え、空文字を「設定なし」と同じに扱う1行を入れておくと、必須の検証で止められる。
  • Python の bool("false") は True。真偽値は専用の変換関数 を通す。
  • エラーを1件目で止めずに集めて表示すると、型の合わない値が2つあるときも1回の起動で両方わかる。
  • 採用元(引数・環境変数・設定ファイル・既定値)を表示 すると、設定ファイルの 8080 とコードの 8000 と引数の 9000 のどれが効いたかが一目でわかる。
  • Spring Boot の公式の順序は「後のものが優先」と書かれていて、一覧の上にあるほど弱い。「上が強い」と読み違えやすい。
  • 設定テーブルの値は文字列の列に入るため、value_type に CHECK 制約を付けておくと、想定外の型の行が入る前に DB が止めてくれる。

つまずきやすい点と直し方

状況 起きること 直し方
コードのあちこちで環境変数を直接読む どの設定があるのか一覧できない 読み込みを1か所にまとめ、ほかは設定オブジェクトを受け取る
必須の値にも既定値を入れている 本番で開発用の接続先のまま動く 環境で変わる値は既定値を「なし」にして必須にする
秘密情報を設定ファイルに書く リポジトリの履歴に残る シークレット管理に置き、環境変数で渡す。ファイルにはキー名だけ書く
秘密情報を起動引数で渡す プロセス一覧やシェルの履歴に見える 環境変数か、ファイルの受け渡しにする
起動ログに設定値をそのまま出す パスワードがログに残る 一覧の「秘密」列を見て伏せ字にする
同じキーを DB と環境変数の両方に持つ どちらが効くのか人によって理解が違う キーごとに置き場所を1つに決め、一覧に書く

よくある質問

Q. 設定値は環境変数と設定ファイルのどちらに置けばよいですか? A. 環境ごとに値が変わるなら環境変数、どの環境でも同じ既定値で構造を持つなら設定ファイルです。両方に書いた場合は環境変数を優先させます。

Q. 環境変数と設定ファイルの優先順位はどうなりますか? A. 引数 > 環境変数 > 設定ファイル > 既定値の順が一般的です。Spring Boot の公式の順序もこの並びになっています。

Q. 設定値をDBに置くのはどんなときですか? A. 再起動なしで運用担当者が変える業務値のときです。変更者と日時を残せます。DB の接続先やパスワードのように、DB を読む前に必要な値は置けません。

Q. 設定値と定数の違いは何ですか? A. 定数は仕様として固定された値で、変更はコードの修正として扱います。設定値はコードを変えずに調整する値です。迷ったら「値を変えたあとにテストをやり直すか」で分けます。

まとめ

  • 置き場所は 値の性質 で決める。環境で変わる値は環境変数、構造を持つ既定値は設定ファイル、稼働中に変える業務値は DB。
  • 秘密情報はシークレット管理 に置き、環境変数として渡す。設定ファイル・DB・引数には書かない。
  • 優先順位は 引数 > 環境変数 > 設定ファイル > 既定値。採用元を起動ログに出す。
  • 必須の値が無ければ起動時に止め、足りないものを一度に全部表示する。
  • 設定項目は キー・型・既定値・置き場所・反映方法・必須 を一覧にする。
  • 空文字の環境変数と文字列の真偽値 は検証でつまずきやすい。専用の扱いを入れる。

参考・出典

  • Spring Boot 4.1.1 リファレンス「Externalized Configuration」(docs.spring.io、2026-10-06 確認)
  • The Twelve-Factor App「III. Config」(12factor.net)

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「設定値の管理設計|環境変数・設定ファイル・DBの使い分けと優先順位」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?