你是否有過這樣的經驗?
第一次做某類專案時,你興高采烈地 clone 了一個「完美的專案模板」。幾週後,你發現這個模板不符合你的需求:資料夾結構太死板、腳本太通用、文檔跟實際情況對不上。於是你開始修改,東補西改,最後整個專案變成一團糟。
專案結束後,你想:「下次我一定要做個更好的模板!」
然後下一個專案開始,你又 clone 了另一個「更完美的模板」,結果歷史重演。
問題的核心不在於「模板不夠好」,而在於我們對模板的期待本身就錯了。
讓我們換個比喻:你不會去買一件「完美的衣服」穿一輩子不改。你會:
- 穿上現有的衣服(使用基本模板開始)
- 發現哪裡不合身(在實際工作中遇到問題)
- 改衣服(邊做邊優化)
- 記住改動(提煉可重用的部分)
- 下次穿更合身的衣服(下個專案使用改進後的工具)
這不是「設計完美模板」的思維,而是**「持續改進工作流程」**的思維。
關鍵在於:
- ✅ 接受不完美 - 第一次做不會完美,也不需要完美
- ✅ 邊做邊學 - 從實際問題中提煉解決方案
- ✅ 可持續迭代 - 每次專案都讓下次更好
而 Git,正是實現這個哲學的最佳工具。
問題: 為什麼專案模板用一次就丟?
因為我們把**「可重用的工具」和「專案特定資料」**混在一起了。
舉個例子,假設你在做臨床研究:
my-clinical-study/
├── scripts/
│ ├── clean_data.py # 可重用工具
│ ├── calculate_stats.R # 可重用工具
│ └── plot_survival.R # 可重用工具
├── data/
│ ├── patient_records.csv # 專案特定資料
│ └── lab_results.csv # 專案特定資料
├── results/
│ └── analysis_report.qmd # 專案特定內容
└── README.md # 混合:工具說明 + 專案描述
當你完成這個研究後,下次要做新研究時:
- ❌ 複製整個資料夾?不行,會把舊資料也複製過來
- ❌ 手動挑出可重用的腳本?容易漏掉,也不知道改了哪些
- ❌ 創建「template」分支然後刪除資料?太麻煩,而且下次還要再刪一次
解決方案:用 Git 分支物理性分離工具和資料
Repository: clinical-research-toolkit
Branch: main
├── scripts/
│ ├── clean_data.py # 通用資料清理
│ ├── calculate_stats.R # 通用統計分析
│ └── plot_survival.R # 通用生存曲線
├── templates/
│ ├── analysis_template.qmd
│ └── report_template.qmd
└── docs/
└── USAGE.md # 如何使用這些工具
Branch: projects/diabetes-2024
├── data/
│ ├── patient_records.csv # 糖尿病研究資料
│ └── lab_results.csv
├── analysis/
│ └── diabetes_analysis.qmd # 使用 main 的工具
└── README.md # 這個研究的說明
Branch: projects/hypertension-2025
├── data/
│ ├── bp_measurements.csv # 高血壓研究資料
│ └── medication_records.csv
├── analysis/
│ └── bp_analysis.qmd # 也使用 main 的工具
└── README.md
這樣的好處:
- ✅ main 分支保持乾淨,只有可重用工具
- ✅ projects/* 分支包含專案特定資料,不會污染工具庫
- ✅ 下次新研究:
git checkout main && git checkout -b projects/new-study - ✅ 工具改進:在 main 分支改進,所有專案都能受益
問題: 為什麼預先設計的模板總是不夠用?
因為你不可能在第一次做之前就知道所有需求。
傳統做法(瀑布式模板設計):
1. 花一週設計「完美模板」
2. 開始實際專案
3. 發現模板不符需求
4. 痛苦地修改,或乾脆放棄模板
演進式做法(邊做邊改):
1. 從最簡單的結構開始
2. 遇到重複性工作 → 寫成腳本
3. 發現腳本可重用 → 移到 main 分支
4. 下個專案 → 使用改進後的工具
5. 循環往復
實際案例:Quarto 教學專案
假設你要寫一系列技術教學文章,使用 Quarto。
第一個專案(Python 入門教學):
# projects/python-intro branch
python-intro/
├── chapters/
│ ├── 01-basics.qmd
│ ├── 02-functions.qmd
│ └── 03-classes.qmd
├── _quarto.yml
└── index.qmd
你手動寫了 _quarto.yml,手動設定格式,一切都很順利。
第二個專案(R 資料分析教學):
你從 projects/python-intro 複製 _quarto.yml,發現:
- ❌ YAML 裡還有 Python 的設定
- ❌ 主題顏色是 Python 風格的藍色
- ❌ Code block 高亮設定不適合 R
於是你改了一堆地方。此時你意識到:
「我需要一個通用的 Quarto 設定生成器!」
演進式改進:
# 在 main 分支創建工具
git checkout main
# 創建 Quarto 專案生成器
cat > scripts/init_quarto_project.py <<'EOF'
#!/usr/bin/env python3
"""Initialize Quarto project with language-specific settings."""
import argparse
from pathlib import Path
TEMPLATES = {
"python": {
"theme": "cosmo",
"highlight": "github",
"code": {"language": "python"}
},
"r": {
"theme": "flatly",
"highlight": "tango",
"code": {"language": "r"}
}
}
def init_project(lang, title, output_dir):
config = TEMPLATES[lang]
# 生成 _quarto.yml
# 創建基本結構
# ...
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--lang", choices=["python", "r"])
parser.add_argument("--title")
parser.add_argument("--output", default=".")
args = parser.parse_args()
init_project(args.lang, args.title, args.output)
EOF
git add scripts/init_quarto_project.py
git commit -m "Add Quarto project generator (learned from python-intro and r-analysis)"第三個專案(JavaScript 前端教學):
git checkout -b projects/js-frontend
cd /path/to/project
# 使用改進後的工具
uv run ../../scripts/init_quarto_project.py \
--lang javascript \
--title "JavaScript Frontend Guide"
# 欸等等,沒有 JavaScript 的設定!再次演進:
git checkout main
# 擴展工具支援 JavaScript
git diff
+ "javascript": {
+ "theme": "lumen",
+ "highlight": "atom-one",
+ "code": {"language": "javascript"}
+ }
git commit -m "Add JavaScript support to Quarto generator"這就是演進式設計的精髓:
- ✅ 第一次:手動做,了解需求
- ✅ 第二次:發現重複,寫成腳本
- ✅ 第三次:擴展腳本,覆蓋更多情境
- ✅ 第 N 次:工具越來越成熟
問題: 為什麼六個月後你看不懂自己的專案?
因為代碼會變,但文檔沒跟著變。
傳統做法:
# 寫完腳本後
git add scripts/new_tool.py
git commit -m "Add new tool"
# 文檔?算了,以後再說...
六個月後:
$ ls scripts/
new_tool.py old_tool.py helper.py utils.py ???.py
# 這些腳本幾時寫的?做什麼用的?參數是什麼?
解決方案:強制文檔覆蓋檢查
創建一個驗證腳本,確保每個工具都有文檔:
# scripts/validate_docs.py
#!/usr/bin/env python3
"""Validate that all scripts have documentation."""
from pathlib import Path
def check_coverage(script_dir, doc_file):
"""Check if all scripts are documented."""
scripts = set(p.stem for p in Path(script_dir).glob("*.py"))
scripts.discard("__init__")
scripts.discard("validate_docs") # Self-reference
# Parse documentation
with open(doc_file) as f:
doc_content = f.read()
documented = set()
for line in doc_content.split("\n"):
if line.startswith("### ") and ".py" in line:
script_name = line.split("`")[1].replace(".py", "")
documented.add(script_name)
# Find orphans
orphan_scripts = scripts - documented
ghost_docs = documented - scripts
if orphan_scripts:
print(f"❌ Undocumented scripts: {orphan_scripts}")
return False
if ghost_docs:
print(f"⚠️ Ghost documentation: {ghost_docs}")
print(f"✅ All {len(scripts)} scripts documented")
return True
if __name__ == "__main__":
import sys
success = check_coverage("scripts/", "docs/TOOLS.md")
sys.exit(0 if success else 1)整合到 Git 工作流程:
# .git/hooks/pre-commit
#!/bin/bash
echo "Validating documentation coverage..."
python3 scripts/validate_docs.py
if [ $? -ne 0 ]; then
echo ""
echo "❌ Commit rejected: Documentation out of sync"
echo "Please update docs/TOOLS.md before committing"
exit 1
fi文檔結構範例:
# Tools Documentation
## Data Processing
### `clean_data.py`
**Purpose**: Remove invalid records and normalize clinical measurements
**Usage**:
```bash
python scripts/clean_data.py \
--input data/raw.csv \
--output data/cleaned.csv \
--rules rules/validation.yaml
```Parameters:
--input: Raw data CSV file--output: Cleaned output file--rules: Validation rules (optional)
Example:
python scripts/clean_data.py --input lab_results.csv --output clean_data.csvAdded: 2024-01-15 Last modified: 2024-02-06 Used in projects: diabetes-2024, hypertension-2025
這樣做的好處:
- ✅ **強制覆蓋** - 沒寫文檔就無法 commit
- ✅ **實時同步** - 文檔永遠與代碼一致
- ✅ **未來可讀** - 六個月後依然清楚
- ✅ **團隊協作** - 其他人能理解你的工具
---
## Git 分支策略:從混亂到秩序
現在我們把三大原則整合成一個完整的 Git 工作流程。
### 分支結構
Repository: research-toolkit (或 teaching-materials, 或任何你的領域)
main # 可重用工具和基礎設施 ├── scripts/ # 通用腳本 ├── templates/ # 專案範本 ├── docs/ # 工具文檔 └── .git/hooks/ # 文檔驗證 hook
projects/study-2024-q1 # 專案 1 資料 ├── data/ ├── analysis/ └── results/
projects/study-2024-q2 # 專案 2 資料 ├── data/ ├── analysis/ └── results/
### 工作流程(ASCII 流程圖)
開始新專案流程:
┌─────────────────────────────────────────────────────────────┐ │ Step 1: 從 main 分支開始 │ │ $ git checkout main │ │ $ git checkout -b projects/new-study │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Step 2: 使用 main 分支的工具開始工作 │ │ $ python ../scripts/init_project.py --name new-study │ │ $ python ../scripts/clean_data.py --input data/raw.csv │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Step 3: 發現工具不夠用?先在專案分支裡臨時寫 │ │ $ vim analysis/custom_plot.R # 暫時寫在專案裡 │ │ $ git add analysis/custom_plot.R │ │ $ git commit -m "Custom plot for this study" │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Step 4: 發現這個腳本可以重用?提煉到 main │ │ $ git checkout main │ │ $ git checkout projects/new-study -- analysis/custom_plot.R│ │ $ mv custom_plot.R scripts/plot_advanced.R # 泛化 │ │ $ vim scripts/plot_advanced.R # 移除專案特定的 hardcode │ │ $ vim docs/TOOLS.md # 加入文檔 │ │ $ git add scripts/plot_advanced.R docs/TOOLS.md │ │ $ git commit -m "Extract reusable plotting tool" │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Step 5: 回到專案分支,使用改進後的工具 │ │ $ git checkout projects/new-study │ │ $ git merge main # 拉取最新工具 │ │ $ rm analysis/custom_plot.R # 刪除臨時版本 │ │ $ python ../scripts/plot_advanced.R # 使用新工具 │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Step 6: 專案結束,歸檔 │ │ $ git add data/ results/ README.md │ │ $ git commit -m "Complete study analysis" │ │ $ git push origin projects/new-study │ │ # 專案資料保留在分支裡,不污染 main │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 下次專案:重複 Step 1-6,但工具庫更豐富了! │ │ $ git checkout main │ │ $ git checkout -b projects/next-study │ │ $ ls scripts/ # 看到上次提煉的工具 ✨ │ └─────────────────────────────────────────────────────────────┘
### 關鍵決策點
**什麼時候該把腳本移到 main 分支?**
使用這個決策樹:
新寫了一個腳本 │ ▼ ┌──────────────────┐ │ 這個腳本只用一次?│ └──────────────────┘ │ │ 是│ │否 │ ▼ │ ┌──────────────────────────┐ │ │ 腳本裡有 hardcoded 資料?│ │ └──────────────────────────┘ │ │ │ │ 是│ │否 │ │ ▼ │ │ ┌─────────────────────┐ │ │ │ 其他專案可能用得到?│ │ │ └─────────────────────┘ │ │ │ │ │ │ 是│ │否 │ │ │ │ ▼ ▼ ▼ ▼ 保留在 泛化後 立即移到 保留在 專案分支 再移到 main 分支 專案分支 main
範例:
❌ **不該移到 main**:
```python
# analysis/diabetes_specific.py
# 只適用糖尿病研究,不可泛化
df = pd.read_csv("data/diabetes_patients.csv")
diabetes_threshold = 126 # mg/dL, 糖尿病特定
# scripts/filter_by_threshold.py (在 main)
# 通用的閾值過濾工具
def filter_by_threshold(df, column, threshold, operator=">="):
"""Filter dataframe by threshold value."""
if operator == ">=":
return df[df[column] >= threshold]
# ...
# 專案中使用:
# python scripts/filter_by_threshold.py \
# --input diabetes.csv \
# --column glucose \
# --threshold 126 \
# --operator ">="✅ 直接移到 main:
# scripts/calculate_ci.R
# 計算信賴區間,任何研究都用得到
calculate_confidence_interval <- function(data, conf.level = 0.95) {
mean_val <- mean(data, na.rm = TRUE)
se <- sd(data, na.rm = TRUE) / sqrt(length(data))
# ...
}背景:你在做一系列臨床試驗資料分析。
第一個研究(糖尿病藥物):
projects/diabetes-drug-trial/
├── data/
│ ├── patients.csv # 300 筆病患資料
│ └── measurements.csv # 每日血糖測量
├── analysis/
│ ├── 01-clean.R # 手寫的資料清理
│ ├── 02-baseline.R # 手寫的基線特徵
│ ├── 03-efficacy.R # 手寫的療效分析
│ └── 04-safety.R # 手寫的安全性分析
└── report.qmd
完成後你發現:「下次還要重寫這些腳本嗎?」
改進:提煉可重用部分
# 切到 main 分支
git checkout main
# 把 01-clean.R 泛化
git checkout projects/diabetes-drug-trial -- analysis/01-clean.R
# 移除 hardcoded 的部分,改成參數化
vim scripts/clean_clinical_data.R
# 原本:
# data <- read.csv("../../data/patients.csv")
# cutoff_date <- as.Date("2024-01-01")
# 改成:
# args <- commandArgs(trailingOnly = TRUE)
# data <- read.csv(args[1])
# cutoff_date <- as.Date(args[2])
git add scripts/clean_clinical_data.R
git commit -m "Add parameterized clinical data cleaning tool"第二個研究(高血壓藥物):
git checkout -b projects/hypertension-drug-trial
# 使用改進後的工具
Rscript ../scripts/clean_clinical_data.R \
data/patients.csv \
2024-06-01 \
> data/cleaned.csv
# 省下重寫清理腳本的時間!第三個研究:發現需要加入「排除標準檢查」
# 在專案裡先寫
vim analysis/exclude_criteria.R
# 完成後提煉
git checkout main
# ... 提煉過程 ...
# 更新工具
git checkout projects/third-trial
git merge main # 拉取新功能循環往復,工具庫越來越豐富:
main/scripts/
├── clean_clinical_data.R # 從第 1 個研究提煉
├── check_exclusion.R # 從第 3 個研究提煉
├── calculate_efficacy.R # 從第 2 個研究提煉
├── generate_safety_table.R # 從第 4 個研究提煉
└── plot_survival_curve.R # 從第 5 個研究提煉
背景:你在寫一系列技術教學。
第一份教材(Python 入門):
projects/python-intro/
├── chapters/
│ ├── 01-setup.qmd
│ ├── 02-variables.qmd
│ └── ...
├── _quarto.yml # 手寫配置
├── custom.scss # 手寫樣式
└── index.qmd
手動設定一切,花了 3 小時調整格式。
第二份教材(R 資料分析):
你複製 _quarto.yml 和 custom.scss,又花了 2 小時修改。
此時你意識到:「我需要標準化這些配置!」
改進:創建教材生成器
git checkout main
# 創建 Quarto 配置生成器
cat > scripts/init_tutorial.py <<'EOF'
#!/usr/bin/env python3
import argparse
from pathlib import Path
import yaml
THEMES = {
"python": {"primary": "#3776ab", "secondary": "#ffd43b"},
"r": {"primary": "#276dc3", "secondary": "#75aadb"},
"javascript": {"primary": "#f7df1e", "secondary": "#000000"}
}
def create_tutorial(lang, title, chapters):
"""Generate Quarto tutorial structure."""
# 創建基本結構
base = Path(title.lower().replace(" ", "-"))
base.mkdir(exist_ok=True)
(base / "chapters").mkdir(exist_ok=True)
# 生成 _quarto.yml
config = {
"project": {"type": "book"},
"book": {
"title": title,
"chapters": [f"chapters/{i:02d}-{c}.qmd"
for i, c in enumerate(chapters, 1)]
},
"format": {
"html": {
"theme": "cosmo",
"css": "custom.scss"
}
}
}
with open(base / "_quarto.yml", "w") as f:
yaml.dump(config, f)
# 生成 custom.scss
colors = THEMES.get(lang, THEMES["python"])
scss = f"""
$primary: {colors['primary']};
$secondary: {colors['secondary']};
.title {{
color: $primary;
}}
"""
with open(base / "custom.scss", "w") as f:
f.write(scss)
# 創建章節模板
for i, chapter in enumerate(chapters, 1):
chapter_file = base / "chapters" / f"{i:02d}-{chapter}.qmd"
chapter_file.write_text(f"# {chapter.replace('-', ' ').title()}\n\n")
print(f"✅ Created tutorial: {base}")
print(f" Chapters: {len(chapters)}")
print(f" Theme: {lang}")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--lang", required=True, choices=THEMES.keys())
parser.add_argument("--title", required=True)
parser.add_argument("--chapters", nargs="+", required=True)
args = parser.parse_args()
create_tutorial(args.lang, args.title, args.chapters)
EOF
chmod +x scripts/init_tutorial.py
git add scripts/init_tutorial.py
git commit -m "Add tutorial generator (learned from python-intro and r-analysis)"第三份教材(JavaScript 前端):
git checkout -b projects/js-frontend
# 使用工具生成
python3 ../scripts/init_tutorial.py \
--lang javascript \
--title "JavaScript Frontend Guide" \
--chapters setup dom events async react
# 3 秒完成過去要 3 小時的工作!ROI 計算:
投資:
- 第 1 份教材:3 小時手動設定(沒有工具)
- 第 2 份教材:2 小時複製修改
- 寫工具:1 小時
回報(從第 3 份開始):
- 每份教材節省:2.5 小時
- 10 份教材後:25 小時 - 1 小時 = 24 小時節省
- ROI: 2400%
症狀:第一次遇到問題就想寫「完美的通用工具」
範例:
# 只做了一次資料清理,就想寫通用工具
def clean_data_universal(
data,
method="auto", # 不知道有哪些 method
strategy="smart", # 不知道 smart 是什麼意思
options=None, # 不知道需要什麼 options
**kwargs # 萬用 kwargs
):
# ... 100 行試圖涵蓋所有情境的代碼問題:你還不知道需求,寫出來的工具反而不實用。
解決:
第 1 次:手動做,保留在專案分支
第 2 次:發現重複,寫簡單腳本
第 3 次:確認模式,泛化成工具
正確範例:
# 第 1 次(projects/study-1)
df = df.dropna()
df = df[df['age'] > 0]
df = df[df['value'] < 1000]
# 第 2 次(projects/study-2)- 發現重複
def clean_basic(df):
df = df.dropna()
df = df[df['age'] > 0]
df = df[df['value'] < 1000]
return df
# 第 3 次(main)- 泛化
def clean_by_rules(df, rules):
"""Apply validation rules to dataframe.
Args:
rules: dict like {"age": (">", 0), "value": ("<", 1000)}
"""
for col, (op, threshold) in rules.items():
if op == ">":
df = df[df[col] > threshold]
elif op == "<":
df = df[df[col] < threshold]
return df.dropna()症狀:工具裡有 hardcoded 的專案路徑或資料
範例:
# scripts/analyze.py (在 main,錯誤示範)
def run_analysis():
# ❌ Hardcoded 專案路徑
data = pd.read_csv("../../projects/study-1/data/input.csv")
# ❌ Hardcoded 專案特定邏輯
if "diabetes" in data.columns:
# 糖尿病特定處理
pass問題:這個工具只能在 study-1 用,失去了可重用性。
解決:
# scripts/analyze.py (正確)
def run_analysis(input_file, disease_type=None):
"""General analysis tool.
Args:
input_file: Path to input CSV
disease_type: Optional disease-specific processing
"""
data = pd.read_csv(input_file)
# 通用處理
results = general_analysis(data)
# 可選的特化處理
if disease_type:
results = apply_disease_specific(results, disease_type)
return results
# 使用時(在專案分支):
# python ../scripts/analyze.py \
# --input data/study-1.csv \
# --disease-type diabetes症狀:改了代碼,忘了更新文檔
範例:
# 三個月前
git log
commit abc123 "Add plot_data.py"
# docs/TOOLS.md 寫著:
### plot_data.py
Usage: python plot_data.py --input data.csv
# 現在
cat scripts/plot_data.py
# 參數已改成 --data,但文檔沒更新問題:未來的你(或同事)會浪費時間 debug「為什麼照文檔做不行」。
解決:Pre-commit hook 強制驗證
# .git/hooks/pre-commit
#!/bin/bash
echo "🔍 Checking documentation coverage..."
python3 scripts/validate_docs.py
if [ $? -ne 0 ]; then
echo ""
echo "❌ Commit blocked: Documentation out of sync"
echo ""
echo "Action required:"
echo "1. Update docs/TOOLS.md to document your changes"
echo "2. Or run: python scripts/generate_docs.py (if available)"
echo ""
exit 1
fi
echo "✅ Documentation is up to date"# 1. 初始化 repository
git init my-research-toolkit
cd my-research-toolkit
# 2. 創建基本結構(在 main 分支)
mkdir -p scripts templates docs .git/hooks
# 3. 創建文檔驗證工具
cat > scripts/validate_docs.py <<'EOF'
# (前面的驗證腳本)
EOF
# 4. 創建 pre-commit hook
cat > .git/hooks/pre-commit <<'EOF'
#!/bin/bash
python3 scripts/validate_docs.py || exit 1
EOF
chmod +x .git/hooks/pre-commit
# 5. 創建基本文檔
cat > docs/TOOLS.md <<'EOF'
# Tools Documentation
## Overview
This repository contains reusable tools for [your domain].
## Tools
(空的,等待第一個工具)
EOF
git add .
git commit -m "Initialize toolkit structure"
# 6. 開始第一個專案
git checkout -b projects/first-project
mkdir -p data analysis results# 工作流程
while doing_project; do
# 1. 手動做一次
vim analysis/exploratory.R
# 2. 發現可以自動化?
if is_repetitive; then
# 先在專案裡寫簡單版
vim analysis/helper.R
fi
# 3. 專案結束後決定是否提煉
if is_reusable; then
git checkout main
# 泛化並移到 scripts/
git checkout projects/first-project -- analysis/helper.R
generalize_and_document "helper.R" "scripts/my_tool.R"
git add scripts/my_tool.R docs/TOOLS.md
git commit -m "Extract tool from first-project"
fi
done# 開始新專案
git checkout main
git checkout -b projects/second-project
# 使用現有工具
ls ../scripts/ # 看到上次提煉的工具
# 發現工具不夠用?
# → 擴展工具(回到 main 改進)
# → 或創建新工具
# 循環往復當你累積了 10+ 個專案後:
main/
├── scripts/ # 20+ 個成熟工具
├── templates/ # 專案範本
├── docs/
│ ├── TOOLS.md # 自動驗證的文檔
│ ├── GETTING_STARTED.md
│ └── BEST_PRACTICES.md
└── tests/ # 工具的單元測試
projects/
├── 2024-q1-study-a/ # 已完成專案
├── 2024-q2-study-b/
├── 2024-q3-study-c/
└── 2025-q1-study-d/ # 當前專案
此時:
- ✅ 新專案啟動時間:從數天 → 數小時
- ✅ 重複性工作:從手動 → 自動化
- ✅ 知識流失:從「忘記怎麼做」 → 「工具裡有記錄」
- ✅ 團隊協作:從「問前任怎麼做」 → 「看文檔」
回到最開始的問題:為什麼專案模板總是用一次就丟?
因為我們對「模板」的期待錯了。
模板思維:
- ❌ 預先設計完美結構
- ❌ 複製貼上到新專案
- ❌ 每次都要手動調整
- ❌ 改進無法回饋到模板
生態系統思維:
- ✅ 從簡單開始,逐步演進
- ✅ 工具與資料分離(Git 分支)
- ✅ 在實踐中提煉可重用部分
- ✅ 文檔與代碼同步驗證
- ✅ 每次專案都讓下次更好
穿衣服改衣服的本質:
不是追求「完美的衣服」,而是建立「持續改進的裁縫能力」。
用 Git 分支策略,我們實現了:
- Main 分支 = 裁縫店(工具庫)
- Projects 分支 = 每次穿著(專案資料)
- Merge 流程 = 改衣服的經驗回饋到裁縫店
最終狀態:
第 1 個專案:從零開始,手動做一切(13 小時)
第 2 個專案:有 3 個工具可用(10 小時)
第 3 個專案:有 7 個工具可用(7 小時)
第 5 個專案:有 15 個工具可用(4 小時)
第 10 個專案:有 25 個工具可用(2 小時)
ROI: 13 小時投資 → 節省 90 小時 = 692% 回報
這不是魔法,是紀律:
- 🎯 工具與資料分離 - 用 Git 分支強制分離
- 🔄 演進式設計 - 接受不完美,邊做邊改
- 📚 文檔與代碼同步 - 用 hook 強制驗證
下次當你開始新專案時,不要問:
「有沒有完美的模板?」
而是問:
「這次我能從上次專案中提煉出什麼可重用的東西?」
這就是可持續工作流程的精髓。
- Git Branching Strategies: A successful Git branching model
- Documentation as Code: Write the Docs
- Refactoring: Martin Fowler's Refactoring: Improving the Design of Existing Code
- Evolutionary Architecture: Building Evolutionary Architectures
致謝:這篇文章的思想來自多次「專案結束後發現模板又不夠用」的痛苦經驗。感謝那些失敗的專案,讓我學會了「穿衣服改衣服」的哲學。
作者: [Your Name] 日期: 2025-02-06 授權: CC BY-SA 4.0
如果這篇文章對你有幫助,歡迎分享給同樣在專案管理中掙扎的朋友。