Help us understand the problem. What is going on with this article?

config.exs の書き方

More than 1 year has passed since last update.

mix new で新しいプロジェクトを作ると config/config.exs ファイルが生成されます。
そして、このファイルには「環境ごとの設定を読むなら import_config "#{Mix.env()}.exs" と書いてね」みたいなことが書かれています。

そのせいか、Elixir には「共通の設定は config/config.exs に書く。環境ごとの設定は config/#{Mix.env()}.exs に書く」という文化があります。

しかし自分は、この文化は良くない と考えています。

何が良くないか

端的に言えば、この書き方は 設定ミスが発生しやすい からです。

例えば共通設定を config.exs に書き、prod.exs の設定で共通設定を上書きするようなコードだったとします。
以下のようになるでしょう。

config.exs
config :my_app,
  value1: 100,
  value2: "foo"

import_config "#{Mix.env()}.exs"
prod.exs
config :my_app,
  value2: "bar"

この時、MIX_ENV=prod での設定を確認しようと思ったら、両方の設定を確認しなければならないことがほとんどです。
なぜなら、config.exs を開いて確認しても、その内容は prod.exs で上書きされてる可能性があるからです。
あるいは prod.exs を開いて確認しても、欲しい設定が書いてあるとは限りません。その場合には config.exs も開く必要があります。
そのため「config.exs の設定を確認した限り問題ないはずなのに、なぜかプロダクション環境でエラーが起きる」ということが起こります。通常は dev.exstest.exs で開発しているので、prod.exs で上書きしていることは忘れがちです。

また、ある設定を書き換える場合、全ての設定ファイルを開き、全体の設定がどうなるかを相互に眺めながら確認する必要があります。
なぜなら、共通設定の config.exs を書き換えて問題ないかどうかを判断するために、全ての環境で最終的にどうなるかを見る必要があるからです。
かなり手間が掛かるため「ある環境では問題なくても、ある環境では問題がある設定にしてしまう」ということが起こります。

実際に何度か間違えたりした経験から、自分は 一度書いた設定は二度と上書きしてはいけない ということと、 環境ごとに設定ファイルを分けてはならない という結論に至りました。

どのように書くべきか(小さいアプリケーション向け)

小さいアプリケーションであれば、全ての設定を1つの config.exs に書いて良いと思います。
以下のように書くのがいいでしょう。

config.exs
# :my_app

# :my_app 全体での共通設定は case の外に書いても構わない。
# ただし、ここで書いた値を絶対に case の中で上書きしないように注意する必要がある。
config :my_app,
  value1: 100

case Mix.env() do
  :dev ->
    config :my_app,
      value2: "foo"
  :sandbox ->
    config :my_app,
      value2: "foofoo"
  :prod ->
    config :my_app,
      value2: "bar"
end

# :my_app2
case Mix.env() do
  # 複数の設定が同じならこのように書いても構わない
  env when env in [:dev, :sandbox] ->
    config :my_app2,
      val: 1000
  :prod ->
    config :my_app2,
      val: 2000
end

この設定ファイルは、以下のルールに従って書かれています。

  • 同じアプリケーションの設定を一箇所に纏める
  • 環境ごとに case で分岐する
  • 環境ごとのパターンマッチでワイルドカードを使わない
  • 同じ設定を絶対に上書きしない

このルールに従って書けば、書き換えて問題ないかを判断するのに時間が掛かったり、書き換えた項目が反映されなかったり、書き換えた範囲が大きすぎて他の環境で影響が出るといった問題はかなり少なくなるでしょう。

なぜこのように書くのかを、それぞれ説明していきます。

同じアプリケーションの設定を一箇所に纏める

あるアプリケーションの設定を確認したり書き換えたりする時、同じアプリケーションの設定が1画面に収まる範囲にあった方が分かりやすいからです。
環境ごとに設定ファイルが分けられていた場合、それぞれの設定ファイルを開く必要があるため、確認するだけで大分手間が掛かります。

同じ設定は同じ場所にあり、他の場所には決して無いということが分かっていれば、考えることは大幅に減ります。

環境ごとに case で分岐する

同じアプリケーションの設定を一箇所に纏めるのであれば、環境ごとの設定は必然的に case で分けるしか無くなります。

環境ごとのパターンマッチでワイルドカードを使わない

例えば以下のように書いてはいけません。

config.exs
# :my_app2
case Mix.env() do
  env when env in [:dev, :sandbox] ->
    config :my_app2,
      val: 1000
  # :dev, :sandbox 以外用の設定
  _ -> # ←決してこのように書いてはいけない
    config :my_app2,
      val: 2000
end

これは、環境が増えた時に、意図しない設定になってしまうのを防ぐためです。

環境が増えたなら、その環境のために 全ての設定を見直すべき です。

同じ設定を絶対に上書きしない

これは最初に説明した通りです。
設定を上書きすると、見なければならない範囲が増えてしまいます。

config.exs:my_app の設定例で書いたように、共通設定を case の外に書いても構いませんが、確実に上書きしないようにうまくやる必要があります。
例えば環境が :sandbox の場合だけ別の値にしたくなったなら、共通設定の :value1 を削除して、全体に :value1 を設定する必要があります。

厳密にやるなら、設定ファイルを上書きするコードが入ってたらエラーにした方がいいでしょう。
しかし Mix.Config はそのような実装にはなっていないし、設定ファイルのロード時に外部ライブラリを読むのは難しいので、人間が頑張る方向で妥協しています。

どのように書くべきか(大きいアプリケーション向け)

大きいアプリケーションでは、アプリごとに設定ファイルを分ける のがいいでしょう。
以下のようになります。

config.exs
import_config "my_app.exs"
import_config "my_app2.exs"
my_app.exs
# さっきの :my_app の設定と同じ
config :my_app,
  value1: 100

case Mix.env() do
  :dev ->
    config :my_app,
      value2: "foo"
  :sandbox ->
    config :my_app,
      value2: "foofoo"
  :prod ->
    config :my_app,
      value2: "bar"
end
my_app2.exs
# さっきの :my_app2 の設定と同じ
case Mix.env() do
  env when env in [:dev, :sandbox] ->
    config :my_app2,
      val: 1000
  :prod ->
    config :my_app2,
      val: 2000
end

それぞれのルールは、小さいアプリケーション向けの設定と同じで、単にアプリケーションごとにファイルを分けただけです。

このように書くことで、設定ファイルを間違えて書いてしまう可能性は大分低くなると思います。
もっと頑張るなら、これらのルールを人間が頑張って守らなくてもいいようなスクリプトを書いて、それ経由で設定を記述するのがいいでしょう。

まとめ

「共通の設定は config/config.exs に書く。環境ごとの設定は config/#{Mix.env()}.exs に書く」というやり方は、設定ミスが発生しやすいのでやめましょう。

以下のルールに従って書きましょう。

  • 同じアプリケーションの設定を一箇所に纏める
  • 環境ごとに case で分岐する
  • 環境ごとのパターンマッチでワイルドカードを使わない
  • 同じ設定を絶対に上書きしない

また、ある程度規模が大きくなってきたら、アプリケーションごとに設定ファイルを分けましょう。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした