Skip to content

Instantly share code, notes, and snippets.

@htlin222
Created February 6, 2026 06:30
Show Gist options
  • Select an option

  • Save htlin222/5278bd8fa3cc56f678c5a713d574920e to your computer and use it in GitHub Desktop.

Select an option

Save htlin222/5278bd8fa3cc56f678c5a713d574920e to your computer and use it in GitHub Desktop.
穿著裝備去冒險:演進式專案模板的 Git 實踐 - 為什麼你的專案模板總是用一次就丟?

為什麼你的專案模板總是用一次就丟?

穿著裝備去冒險:演進式專案模板的 Git 實踐


引言:模板的困境

你是否有過這樣的經驗?

第一次做某類專案時,你興高采烈地 clone 了一個「完美的專案模板」。幾週後,你發現這個模板不符合你的需求:資料夾結構太死板、腳本太通用、文檔跟實際情況對不上。於是你開始修改,東補西改,最後整個專案變成一團糟。

專案結束後,你想:「下次我一定要做個更好的模板!」

然後下一個專案開始,你又 clone 了另一個「更完美的模板」,結果歷史重演。

問題的核心不在於「模板不夠好」,而在於我們對模板的期待本身就錯了。


穿衣服改衣服的哲學

讓我們換個比喻:你不會去買一件「完美的衣服」穿一輩子不改。你會:

  1. 穿上現有的衣服(使用基本模板開始)
  2. 發現哪裡不合身(在實際工作中遇到問題)
  3. 改衣服(邊做邊優化)
  4. 記住改動(提煉可重用的部分)
  5. 下次穿更合身的衣服(下個專案使用改進後的工具)

這不是「設計完美模板」的思維,而是**「持續改進工作流程」**的思維。

關鍵在於:

  • 接受不完美 - 第一次做不會完美,也不需要完美
  • 邊做邊學 - 從實際問題中提煉解決方案
  • 可持續迭代 - 每次專案都讓下次更好

而 Git,正是實現這個哲學的最佳工具。


三大原則

原則 1:工具與資料分離

問題: 為什麼專案模板用一次就丟?

因為我們把**「可重用的工具」「專案特定資料」**混在一起了。

舉個例子,假設你在做臨床研究:

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 分支改進,所有專案都能受益

原則 2:演進式設計(邊做邊改)

問題: 為什麼預先設計的模板總是不夠用?

因為你不可能在第一次做之前就知道所有需求

傳統做法(瀑布式模板設計):

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 次:工具越來越成熟

原則 3:可持續維護(文檔與代碼同步)

問題: 為什麼六個月後你看不懂自己的專案?

因為代碼會變,但文檔沒跟著變

傳統做法:

# 寫完腳本後
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.csv

Added: 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, 糖尿病特定

⚠️ 泛化後移到 main

# 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))
  # ...
}

實際案例:從混亂到秩序

案例 1:臨床研究工作流程

背景:你在做一系列臨床試驗資料分析。

第一個研究(糖尿病藥物):

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 個研究提煉

案例 2:Quarto 教學材料庫

背景:你在寫一系列技術教學。

第一份教材(Python 入門):

projects/python-intro/
├── chapters/
│   ├── 01-setup.qmd
│   ├── 02-variables.qmd
│   └── ...
├── _quarto.yml               # 手寫配置
├── custom.scss               # 手寫樣式
└── index.qmd

手動設定一切,花了 3 小時調整格式。

第二份教材(R 資料分析):

你複製 _quarto.ymlcustom.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%

常見陷阱與解決方案

陷阱 1:過早泛化

症狀:第一次遇到問題就想寫「完美的通用工具」

範例

# 只做了一次資料清理,就想寫通用工具
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()

陷阱 2:工具與專案耦合

症狀:工具裡有 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

陷阱 3:文檔腐爛

症狀:改了代碼,忘了更新文檔

範例

# 三個月前
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"

實戰清單:建立你的可持續工作流程

Phase 1:建立基礎結構(第一個專案)

# 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

Phase 2:邊做邊累積(專案進行中)

# 工作流程
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

Phase 3:持續改進(後續專案)

# 開始新專案
git checkout main
git checkout -b projects/second-project

# 使用現有工具
ls ../scripts/  # 看到上次提煉的工具

# 發現工具不夠用?
# → 擴展工具(回到 main 改進)
# → 或創建新工具

# 循環往復

Phase 4:成熟生態系統

當你累積了 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 分支策略,我們實現了:

  1. Main 分支 = 裁縫店(工具庫)
  2. Projects 分支 = 每次穿著(專案資料)
  3. Merge 流程 = 改衣服的經驗回饋到裁縫店

最終狀態

第 1 個專案:從零開始,手動做一切(13 小時)
第 2 個專案:有 3 個工具可用(10 小時)
第 3 個專案:有 7 個工具可用(7 小時)
第 5 個專案:有 15 個工具可用(4 小時)
第 10 個專案:有 25 個工具可用(2 小時)

ROI: 13 小時投資 → 節省 90 小時 = 692% 回報

這不是魔法,是紀律

  • 🎯 工具與資料分離 - 用 Git 分支強制分離
  • 🔄 演進式設計 - 接受不完美,邊做邊改
  • 📚 文檔與代碼同步 - 用 hook 強制驗證

下次當你開始新專案時,不要問:

「有沒有完美的模板?」

而是問:

「這次我能從上次專案中提煉出什麼可重用的東西?」

這就是可持續工作流程的精髓。


延伸閱讀


致謝:這篇文章的思想來自多次「專案結束後發現模板又不夠用」的痛苦經驗。感謝那些失敗的專案,讓我學會了「穿衣服改衣服」的哲學。


作者: [Your Name] 日期: 2025-02-06 授權: CC BY-SA 4.0


如果這篇文章對你有幫助,歡迎分享給同樣在專案管理中掙扎的朋友。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment