<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Reference-Strategy on Tarragon</title><link>https://tarrragon.github.io/blog/tags/reference-strategy/</link><description>Recent content in Reference-Strategy on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Fri, 17 Jul 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/reference-strategy/index.xml" rel="self" type="application/rss+xml"/><item><title>Frozen vs Live Reference（凍結參照與活解析）</title><link>https://tarrragon.github.io/blog/testing/knowledge-cards/frozen-vs-live-reference/</link><pubDate>Fri, 17 Jul 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/testing/knowledge-cards/frozen-vs-live-reference/</guid><description>&lt;p>下游持有上游資料的 id 時，有兩條策略：凍結參照（寫死 id，後續操作直接使用）和活解析（以跨操作不變的識別鍵在查詢時動態解引用，取得當前有效的 id）。凍結參照在上游重建資料後失效；由測試餵資料的 stub 同時控制凍結值與回應資料，讓「id 失效」的狀態無法在測試中出現——這與 &lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/mock-masking/" data-link-title="Mock Masking（Mock 遮蔽）" data-link-desc="mock 模擬 API 層但不模擬協議層，造成的結構性驗證盲區">mock 遮蔽&lt;/a>是不同層次的盲區，遮蔽來自假設而非協議。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>凍結與活解析的分野是狀態所有權問題：凍結參照假設上游資料的 id 在下游生命週期內不會改變，活解析承認 id 可能失效、改由穩定的業務鍵重新定位。這個區分跨越測試與領域設計——領域層決定哪些 id 會被上游操作重建（合併、拆分、歸檔），測試層要覆蓋「重建後舊 id 指向已刪除資料」的劇本。使用&lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/semantic-fake-backend/" data-link-title="Semantic Fake Backend（語意級假後端）" data-link-desc="持有狀態、只固化已證實後端行為的測試假件；與由測試餵資料的 stub 以狀態歸屬和行為出處劃界">語意級假後端&lt;/a>自身模擬重建行為，是讓這類劇本可測的前提。&lt;/p>
&lt;h2 id="可觀察訊號與例子">可觀察訊號與例子&lt;/h2>
&lt;p>操作在特定條件後全部靜默失敗（回寫目標不存在），且單元測試全綠。典型案例：後端合併兩張單據時重建全部明細並更換 id，前端凍結的舊 id 全部指向已刪除資料（&lt;a href="https://tarrragon.github.io/blog/testing/cases/stale-reference-stub-blindspot/" data-link-title="T.C5 凍結參照失效被 stub 遮蔽 — 測試全綠、功能全壞" data-link-desc="前端把後端資料的 id 凍結在本地記錄裡，後端的合併操作會重建資料、舊 id 全部失效 — 單元測試的 stub 由測試自己餵資料，永遠不會經歷「id 死亡」，bug 存在期間所有測試綠燈">T.C5&lt;/a>）。修復同樣受盲區支配——第一版只處理單據層 id、遺漏明細層，因為明細 id 在測試裡從未死過。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>測試設計要回答「哪些上游操作會讓凍結的 id 死亡」，並把每個死亡路徑做成測試劇本。用有狀態的假後端取代 stub（假後端自行模擬重建行為）才能讓活解析邏輯在測試中被真正驅動。「什麼會變、什麼不變」是事實問題——合法出處是可驗的契約文件或一次實測取證，推理與記憶都不算出處。跨邊界的 id 存活規則同時是領域設計的責任範圍。&lt;/p></description><content:encoded><![CDATA[<p>下游持有上游資料的 id 時，有兩條策略：凍結參照（寫死 id，後續操作直接使用）和活解析（以跨操作不變的識別鍵在查詢時動態解引用，取得當前有效的 id）。凍結參照在上游重建資料後失效；由測試餵資料的 stub 同時控制凍結值與回應資料，讓「id 失效」的狀態無法在測試中出現——這與 <a href="/blog/testing/knowledge-cards/mock-masking/" data-link-title="Mock Masking（Mock 遮蔽）" data-link-desc="mock 模擬 API 層但不模擬協議層，造成的結構性驗證盲區">mock 遮蔽</a>是不同層次的盲區，遮蔽來自假設而非協議。</p>
<h2 id="概念位置">概念位置</h2>
<p>凍結與活解析的分野是狀態所有權問題：凍結參照假設上游資料的 id 在下游生命週期內不會改變，活解析承認 id 可能失效、改由穩定的業務鍵重新定位。這個區分跨越測試與領域設計——領域層決定哪些 id 會被上游操作重建（合併、拆分、歸檔），測試層要覆蓋「重建後舊 id 指向已刪除資料」的劇本。使用<a href="/blog/testing/knowledge-cards/semantic-fake-backend/" data-link-title="Semantic Fake Backend（語意級假後端）" data-link-desc="持有狀態、只固化已證實後端行為的測試假件；與由測試餵資料的 stub 以狀態歸屬和行為出處劃界">語意級假後端</a>自身模擬重建行為，是讓這類劇本可測的前提。</p>
<h2 id="可觀察訊號與例子">可觀察訊號與例子</h2>
<p>操作在特定條件後全部靜默失敗（回寫目標不存在），且單元測試全綠。典型案例：後端合併兩張單據時重建全部明細並更換 id，前端凍結的舊 id 全部指向已刪除資料（<a href="/blog/testing/cases/stale-reference-stub-blindspot/" data-link-title="T.C5 凍結參照失效被 stub 遮蔽 — 測試全綠、功能全壞" data-link-desc="前端把後端資料的 id 凍結在本地記錄裡，後端的合併操作會重建資料、舊 id 全部失效 — 單元測試的 stub 由測試自己餵資料，永遠不會經歷「id 死亡」，bug 存在期間所有測試綠燈">T.C5</a>）。修復同樣受盲區支配——第一版只處理單據層 id、遺漏明細層，因為明細 id 在測試裡從未死過。</p>
<h2 id="設計責任">設計責任</h2>
<p>測試設計要回答「哪些上游操作會讓凍結的 id 死亡」，並把每個死亡路徑做成測試劇本。用有狀態的假後端取代 stub（假後端自行模擬重建行為）才能讓活解析邏輯在測試中被真正驅動。「什麼會變、什麼不變」是事實問題——合法出處是可驗的契約文件或一次實測取證，推理與記憶都不算出處。跨邊界的 id 存活規則同時是領域設計的責任範圍。</p>
]]></content:encoded></item></channel></rss>