Skill 寫法圖鑑

ub-mode

Uncle Bob | 架構與重構 | 原文 | MIT

寫程式時照精簡原則走:先讓它能動,再做對,最後遵守實務

中文翻譯

---
name: ub-mode
description: >
  寫程式或改程式時,遵循精簡過的原則。每次改動都照
  先讓它能動(First Make it Work)、再把它做對(Then Make it Right)、最後遵守實務(Follow Practice)的順序。
  使用者執行 /ub-mode 或要求精簡原則時使用。
---

# ub-mode

每次被要求做改動時,照這個順序:
* 先讓它能動(First Make it Work)。
* 再把它做對(Then Make it Right)。
* 遵守實務(Follow Practice)

# 先讓它能動(First Make it Work)

## 偷懶協議(Laziness Protocol)

* 一個決定只放在一個地方。
* 只送出能解決問題的最小改動。

## 基礎思考

* 先把資料的形狀弄對,再寫邏輯。
* 先做那些對後面每個階段都有幫助的事。
* 如果並行寫入會有影響,就不要共用 state。

## TDD

* 改程式之前,先寫會失敗的測試。
* 確認它是因為你預期的原因失敗。
* 做出能讓它通過的最小改動。
* 如果誠實的測試做不出來,就不要寫一個爛的。

## 從第一原理重新設計

* 不要把新需求硬接在舊設計上。
* 問自己:如果一開始就有這個需求,你會怎麼蓋?把它想清楚,然後分段交付。

## 攻擊前提

* 當兩個共用同一個前提的修法,在同一道關卡失敗,用一句話把那個前提寫下來,並數一數哪些角色握有那個不平衡。
* 把這個指派拿掉。
* 做完這件事之前,不要開始另一個修法。

## 先減再加

* 加任何東西之前,先把複雜度拿掉。

## 以結果為導向的執行

* 瞄準你能檢查的最終狀態。
* 中途可以有中斷,前提是你事先規劃過、而且能還原。

## 打造槓桿

* 如果這個工作不是一眼就看得出怎麼做,就去做一個能做這件事的工具。
* 先手動做一個單位,再讓工具對上那個單位。

## 架構

### 為領域建模

* 把領域放進一個讓非法狀態不可能出現的結構裡。
* 維護一份領域名詞的詞彙表。
* 如果程式碼已經清楚而且是區域性的,就別動它。

### 邊界紀律

* 在資料進來的地方檢查它。
* 進到裡面就信任它。
* 業務邏輯放在純函式裡。

### 型別系統紀律

* 讓非法的值根本寫不出來。
* 外部資料在進來的地方只解析一次。
* 不要讓編譯器閉嘴。

### 讓操作具有冪等性

* 執行兩次,或在中途當機後再執行,最後都會落在同樣的狀態。

### 序列化共享狀態之前先分離

* 如果它們不需要同一個可變的東西,就各給一份。
* 如果需要,就只允許一個寫入者,並且在結構上強制做到。

# 再把它做對(Then Make it Right)

## 修根本原因

* 重現它。
* 修原因。
* 只是把當機遮起來的防護,不算修好。

## 把工作切成可驗證的單位

* 完成一個小單位並檢查它。
* 然後再開始下一個。

## 測試行為,不測實作

* 測試要用使用者呼叫程式碼的方式去呼叫,並檢查使用者會看到的結果。
* 如果每個函式都回傳空值,這個測試還是會通過,那就把它丟掉。

## Crapper

* 使用 crapper 工具
* 把每個函式的 CRAP 分數降到 10 以下,方法是拆分它、測試它,或兩者都做。
* 想想 case 敘述值不值得去降。

## Dryer

* 使用 dryer 工具,消除排名 0.8 以上的重複。

## Mutator

* 使用 mutator 工具
* 補上沒覆蓋到的部分。
* 殺掉存活的 mutant。
* 刪除死碼。

## 重構

* 把程式碼目前的行為釘住。
* 改結構。
* 證明行為沒有變。

# 實務(Practice)

## 記錄日誌

* 把你做的每個動作和原因都記成日誌。
* 完成時回報這份日誌。

## 守護 Context Window

* 讓主執行緒保持精簡。
* 大量的內容送去別的地方,只留摘要。

## Worktree 清理

* 找出可以安全移除的東西,確認沒有還在用的東西需要它,再回收磁碟空間。
* 還沒 commit 的工作要等人決定。

## Commit 與 Push

* 常常 commit,切成小而有順序的 commit。
* 每個 commit 都要說明為什麼改、改了什麼、怎麼證明它沒問題。
* 最後 push 到主線。

英文原文

---
name: ub-mode
description: >
  Follow the reduced principles when writing or changing code. Every change
  follows First Make it Work, then Then Make it Right, then Follow Practice.
  Use when the user runs /ub-mode or asks for the reduced principles.
---

# ub-mode

Every time you are asked to make a change follow:
* First Make it Work.
* Then Make it Right.
* Follow Practice

# First Make it Work

## Laziness Protocol

* One decision lives in one place.
* Ship the smallest edit that solves the problem.

## Foundational Thinking

* Get the data shape right before you write the logic.
* Do first whatever helps every later phase.
* If a concurrent writer would matter, do not share state.

## TDD

* Write the failing test before you change the code.
* See it fail for the reason you expect.
* Make the smallest change that makes it pass.
* If an honest test is impractical, do not write a bad one.

## Redesign From First Principles

* Do not bolt a new requirement onto the old design.
* Ask what you would have built if it had been there from the start, think that through, and deliver it in pieces.

## Attack the Premise

* When two fixes that share one premise fail the same gate, write that premise in one sentence and count which actors hold the imbalance.
* Remove the assignment.
* Do not start another fix until you have done that.

## Subtract Before You Add

* Take complexity out before you add anything.

## Outcome-Oriented Execution

* Aim at the end state you can check.
* A break in the middle is allowed when you planned it and can undo it.

## Build the Lever

* If the work is not obvious at a glance, build the tool that does it.
* Do one unit by hand, then make the tool match that unit.

## Architecture

### Model the Domain

* Put the domain in a structure that makes the illegal states impossible.
* Maintain a glossary of domain terms.
* If the code is already clear and local, leave it.

### Boundary Discipline

* Check the data where it enters.
* Inside, trust it.
* Keep the business logic in a pure function.

### Type System Discipline

* Make the illegal value impossible to write.
* Parse outside data once, where it comes in.
* Do not silence the compiler.

### Make Operations Idempotent

* Running it twice, or running it after a crash halfway through, ends in the same state.

### Separate Before Serializing Shared State

* If they do not need the same mutable thing, give each its own.
* If they do, allow one writer, and enforce that in the structure.

# Then Make it Right

## Fix Root Causes

* Reproduce it.
* Fix the cause.
* A guard that only hides the crash is not a fix.

## Sequence work into verifiable units

* Finish one small unit and check it.
* Then start the next.

## Test Behavior, Not Implementation

* The test calls the code the way its user does and checks the result that user would see.
* If the test would still pass when every function returned nothing, throw it out.

## Crapper

* Use the crapper tool
* Reduce each function under  CRAP 10 by splitting it, by testing it, or both.
* Consider whether case statements are worth reducing.

## Dryer

* Use the dryer tool to eliminate duplication that rank 0.8 or above.

## Mutator

* Use the mutator tool
* Cover the uncovered.
* Kill the survivors.
* Delete dead code.

## Refactoring

* Pin what the code does.
* Change the structure.
* Prove the behavior did not change.

# Practice

## Keep a Log

* Keep a log of every action you take and the reason you took it.
* Report that log upon completion.

## Guard the Context Window

* Keep the main thread small.
* Send the bulk elsewhere, and keep the summary.

## Worktree Cleanup

* Find what is safe to remove, confirm nothing still in use needs it, then reclaim the disk.
* Uncommitted work waits for a decision.

## Commit and Push

* Commit often, in small ordered commits.
* Each commit says why, what changed, and how you proved it.
* Push to the main line at the end.