大家好!前幾篇我們已經把路由、中介層、API 都玩過一輪,程式碼開始有點專業的味道了。
但是專案越長越大,新的問題來了:我的程式要怎麼知道自己現在跑在「家裡測試」還是「正式上線」?
這就是今天的主角──程式的設定管理。
為什麼要分環境?
想像你開了一間珍珠奶茶店:
開發環境(Dev):在家亂調配,糖跟冰都隨便抓。
測試環境(Stage):請朋友來喝,開始用量杯量糖,但偶爾還是亂加。
正式環境(Prod):真的開始賣錢,每一杯都要精確計算,還要記錄供應商。
如果「糖量」=資料庫密碼,「冰塊」=快取設定,「供應商」=API Key,你把這些東西寫死在程式碼裡,那切換環境時就超麻煩,還可能一不小心把秘密配方洩漏出去。
12-Factor 原則:設定要獨立!
Heroku 提出了一套軟體開發準則,叫 12-Factor App。
其中一個重點就是:程式的設定不能跟程式碼綁死,要分開!
像是:
🔑 資料庫帳號密碼
🗝️ API Key
🌐 伺服器 IP 與 Port
🐞 除錯模式開關
這些都不能直接寫在程式碼裡,而是要從外部讀取。
最好用的方法:環境變數
12-Factor 推薦的做法就是:環境變數。
比喻一下:程式碼就像 SOP(流程表),永遠長一樣。
但真正的數據(糖、冰、供應商)要等老闆(環境變數)在開店前給。
程式碼只會問:「DB_HOST 在哪?」
而環境在啟動時給答案:
Dev:DB_HOST=127.0.0.1
Prod:DB_HOST=prod.db.com
程式碼本身完全不用改!
Go 的好幫手:Viper 套件
雖然我們可以用 os.Getenv() 直接讀環境變數,但寫久了很亂。
這時候就需要一個「總管」── Viper!
它能:
✅ 讀環境變數 🌍
✅ 讀設定檔(json、yaml、toml、env 都行)📂
✅ 設定預設值 ⚙️
🚀 超方便。
go get github.com/spf13/viper
package config
type Config struct {
ServerPort string `mapstructure:"SERVER_PORT"`
Database DBConfig
}
type DBConfig struct {
Host string `mapstructure:"DB_HOST"`
Port string `mapstructure:"DB_PORT"`
User string `mapstructure:"DB_USER"`
Password string `mapstructure:"DB_PASSWORD"`
DBName string `mapstructure:"DB_NAME"`
}
package config
import (
"log"
"github.com/spf13/viper"
)
func LoadConfig() (config Config, err error) {
viper.AddConfigPath(".")
viper.SetConfigName(".env")
viper.SetConfigType("env")
viper.AutomaticEnv()
err = viper.ReadInConfig()
if err != nil {
log.Printf("找不到 .env,改用環境變數: %v", err)
}
err = viper.Unmarshal(&config)
return
}
SERVER_PORT=8080
DB_HOST=localhost
DB_PORT=5432
DB_USER=test_user
DB_PASSWORD=test_password
DB_NAME=todo_db_dev
cfg, err := config.LoadConfig()
if err != nil {
log.Fatalf("載入設定失敗: %v", err)
}
log.Printf("資料庫主機: %s", cfg.Database.Host)
log.Printf("伺服器埠號: %s", cfg.ServerPort)
部署到正式環境
上線時,不需要 .env 檔!
只要在伺服器輸入:
export DB_HOST=prod.db.super.com
export DB_USER=prod_user
./your_go_app
程式會自動讀這些設定,比較安全。
總結
❌ 不要把密碼寫死在程式碼裡。
🌍 用環境變數管理設定。
🛠️ 用 Viper 幫你統一管理。
這樣你的程式就能:
🔒 更安全(密碼不會外洩到 GitHub)
⚡ 更方便(切環境只要改變變數)
🏆 更專業(符合業界標準 12-Factor)