跳轉到

Go Hardcode vs DB Default 的 Migration 衝突模式

概念概覽

Bug 模式描述

核心知識

Bug 模式描述

Go code 在 INSERT 時明確寫死某欄位值(如 relation_level=0),後續 migration 將該欄位的 DB default 改為另一個值(如 DEFAULT 1)。

結果:migration 正確執行,但 Go code 每次 INSERT 都用寫死值覆蓋 DB default,DB default 的變更對新資料完全無效。這是「靜默失效」——沒有 error,邏輯卻是錯的。

具體案例

-- migration 040: 將 relation_level 預設改為 1
ALTER TABLE teach_progress ALTER COLUMN relation_level SET DEFAULT 1;
// Go code(舊):寫死 0,導致 migration 040 無效
db.Create(&TeachProgress{
    RelationLevel: 0,  // ← 這行讓 DB default 永遠不生效
})

// Go code(修正後):移除寫死值,讓 DB default 生效
db.Create(&TeachProgress{
    // RelationLevel 不設定,DB 自動套用 DEFAULT 1
})

根因識別方式

  1. 症狀:migration 已改 DB default,但新資料的欄位值仍是舊值
  2. 排查:grep -n 'RelationLevel\|relation_level' **/*.go 找出所有寫死的地方
  3. 確認:INSERT SQL log 中看到欄位被明確賦值而非 DEFAULT

Migration 安全性判斷

  • Migration 035 確認**未在任何環境執行過** → 可安全直接修改檔案
  • 若已在 dev/staging/prod 任一環境執行 → 必須新增新 migration,不可修改舊檔案

經驗教訓

  • Go ORM(GORM)在 Create 時若欄位有零值(0、false、""),會明確寫入 0 而非使用 DB default,需特別注意

  • migration 改 DB default 之後,必須同步檢查 Go code 是否有寫死初始值的地方

  • migration 是否已執行是判斷「直接修改 vs 新增 migration」的核心依據

常見陷阱

  • GORM 的零值問題:int 欄位寫死 0 會被視為明確賦值,不會套用 DB DEFAULT

  • 只改 migration 沒改 Go code,產生靜默失效——測試可能通過但邏輯錯誤

最佳實踐

  • 新增 migration 改 DB default 時,同步 grep 所有相關 Go struct 初始化,確認沒有寫死覆蓋

  • 未在任何環境執行的 migration 可直接修改;已執行的必須新增新 migration

相關概念

  • dirty-migration----dangling---
  • postgresql----dangling---

來源 Sessions

日期 Session 貢獻摘要

| 2026-03-20 | d9903c52-a136-4d3b-b5d9-99c8d0c4d1c1 | 識別出 Go code 寫死欄位初始值、與後續 migration 改 DB default 產生靜默衝突的 bug 模式,並確認修正方式 |


本概念頁面由 Semi-Brain Wiki 系統自動維護

最後更新: 2026-03-20