策略式 vs. 戰術式編程 (Strategic vs. Tactical Programming)

★★★★★Draft

FromA Philosophy of Software Design

📚 From the Books

策略式 vs. 戰術式編程 (Strategic vs. Tactical Programming)Complexity

Ousterhout 的主張:好設計最關鍵的元素之一,是你動手時抱持的心態。戰術式編程只求「讓東西能動」,每次任務都偷偷塞一點複雜度,短期看似最快;戰略式編程把時間當作投資,主要目標不是「能跑」,而是「做出一個優秀的設計,而它剛好也能跑」。程式能跑,還不夠。

🧠 Why It Matters

WARNING

每個組織幾乎都有一位「戰術式龍捲風(tactical tornado)」:產量驚人、塞功能沒人比他快,管理層常把他當英雄。但他徹底以戰術式方式寫程式,留下滿地殘骸——真正的英雄,是那位看起來比較慢、其實在替團隊還債、收拾殘局的工程師。

⚖️ 兩種心態的對照

戰術式 — 只求能動,順手塞 kludge 與 patch
// 目標:讓新的「折扣」需求今天就能上線。
function priceFor(order) {
  let total = order.items.reduce((s, i) => s + i.price, 0);

  // 臨時:VIP 打九折(趕上線先這樣)
  if (order.user.tier === "vip") total *= 0.9;

  // 又一個需求:週年慶再打折。直接再補一層。
  if (Date.now() < order.campaignEnd) total *= 0.8;

  // bug 回報:某些訂單 total 是 NaN,來不及查,先 patch 掉
  if (Number.isNaN(total)) total = 0;

  return total;
}

每個判斷單看都是「為了快點完成」的合理妥協,但折扣規則、活動、防呆全糊在一起。下一個需求只能再疊一層 if——複雜度就是這樣一點一點累積、最後反噬。

戰略式 — 把時間當投資,先找乾淨的設計
// 主動式投資:折扣是一串可組合的規則,而不是一堆內嵌 if。
const vipDiscount = (t, order) => (order.user.tier === "vip" ? t * 0.9 : t);
const campaign = (t, order) => (Date.now() < order.campaignEnd ? t * 0.8 : t);

function priceFor(order, rules = [vipDiscount, campaign]) {
  const subtotal = order.items.reduce((s, i) => s + i.price, 0);
  return rules.reduce((t, apply) => apply(t, order), subtotal);
}

// 反應式投資:發現 NaN 是「品項缺 price」造成的 —— 修在根源,而不是最後遮起來。

多花的那 10–20% 時間,換來的是:下次新增折扣只是往 rules 加一條,既有邏輯一行都不用動。設計持續變好,而不是持續變糟。

IMPORTANT

投多少?持續做小投資,約佔總開發時間的 10–20%。 兩種極端都不對:一次性把整個系統設計完是瀑布模型(已知不管用);完全不投資則會被複雜度反噬。好設計傾向「邊累積經驗、邊一點一滴浮現」,所以最佳做法是持續做許多小投資。

NOTE

這 10–20% 很快變「免費」:戰略式路徑初期慢 10–20%,但數個月內開始受益,之後至少快 10–20%,省下的時間足以支付未來的投資;戰術式路徑初期快 10–20%,但複雜度累積後會迅速吐回,並在系統剩餘生命週期裡永遠更慢。(劣化的 codebase 至少會拖慢開發 20%。)

🔑 Takeaways

✍️ My Notes

No notes yet — jot your takeaways or Q&A here.

📖 Further Reading