作者: tm731531

  • Hacker News 每日精選 – 2026-03-28

    👋 歡迎回到今日的科技趨勢觀察。今日的技術焦點集中在 AI 代理程式 (Agents) 的架構演進 以及 硬體效能與能耗管理的極致突破。從史丹佛大學提出的 JAI 層概念到 AMD 與 LG 在硬體層面的創新,我們正見證著軟硬體如何同時為了更高效的未來進行典範轉移。

    🤖 AI / 機器學習

    1. 擁抱 AI 代理程式,而非糾結於檔案系統 (JAI)

    這是一項來自史丹佛大學的研究計畫,主張我們應該專注於建構 JAI (Just-in-time AI) 層。這項提案認為,AI 代理程式不應該直接在複雜的傳統檔案系統中掙扎,而是透過一個抽象的、專為 AI 設計的上下文管理層來運作,從而提升代理程式在處理大型專案時的效率與精確度。

    🔗 閱讀原文:Go hard on agents, not on your filesystem

    2. 深度解析:.claude/ 資料夾的內部構造

    這篇文章深入剖析了 Claude 桌面應用程式在本地生成的 .claude/ 資料夾。作者詳細解釋了該資料夾如何儲存本地上下文、設定檔以及代理程式的執行紀錄,對於想要理解現代 AI 輔助開發工具如何管理狀態與隱私的開發者來說,是非常具價值的技術參考。

    🔗 閱讀原文:Anatomy of the .claude/ folder

    💻 開發工具與開源專案

    1. Velxio 2.0 – 在瀏覽器中模擬 Arduino、ESP32 與 Raspberry Pi

    Velxio 2.0 提供了一個強大的瀏覽器環境,讓開發者無需實體硬體即可運行與測試嵌入式系統代碼。它支持 Raspberry Pi 3 等高性能硬體的模擬,極大降低了嵌入式教學與快速原型的門檻,展現了 WebAssembly 在硬體模擬領域的巨大潛力。

    🔗 閱讀原文:Velxio 2.0 – Emulate Arduino, ESP32, and Raspberry Pi 3 in the Browser

    2. APK 本質上就是 ZIP:修復遺棄硬體的軟體黑客技術

    這段影片展示了如何透過修改 APK 檔案(其實就是壓縮檔)來讓過時或被廠商遺棄的硬體重新煥發生機。這不僅是一個技術教學,更探討了數位考古、硬體權利以及在法律邊緣探索技術自由的極客精神。

    🔗 閱讀原文:.apks are just .zips; semi-legally hacking software for orphaned hardware

    3. 以反諷方式讓 macOS 的介面變得「一致地糟糕」

    這是一個充滿諷刺意味的專案,作者透過修改系統設定與使用特定工具,將 macOS 那些過於平滑或令人不適的 UI 設計推向極致。這篇文章引發了關於現代作業系統設計退化、使用者自定義權利以及 UI/UX 一致性的熱烈討論。

    🔗 閱讀原文:Make macOS consistently bad unironically

    🚀 創業、商業與硬體

    1. AMD Ryzen 9 9950X3D2:單晶片塞入 208MB 快取

    AMD 再次推高了處理器效能的上限,這款新型 Dual Edition 晶片透過 3D V-Cache 技術,在單一封裝內提供了高達 208MB 的快取容量。這對於極度依賴記憶體頻寬與快取的運算任務(如大型遊戲與科學模擬)來說,將會是革命性的效能飛躍。

    🔗 閱讀原文:AMD’s Ryzen 9 9950X3D2 Dual Edition crams 208MB of cache into a single chip

    2. LG 的 1Hz 顯示器:筆電續航力的秘密武器

    LG 發表了一款具備 1Hz 低刷新率技術的面板。這項技術能在靜止畫面下大幅降低功耗,這可能是未來高效能筆電解決電池續航焦慮的核心解決方案。這顯示了顯示技術正從單純追求高刷,轉向更靈活的智慧功耗控制。

    🔗 閱讀原文:LG’s new 1Hz display is the secret behind a new laptop’s battery life

    🎨 其他有趣發現

    1. Twitch Roulette – 發現那些最需要觀眾的實況主

    這個有趣的小工具旨在打破大型實況平台的演算法壟斷。透過隨機匹配,它能幫助使用者找到那些正在直播但幾乎沒有觀眾的創作者。這是一種對社交媒體演算法的微型反抗,也為小型創作者提供了被看見的機會。

    🔗 閱讀原文:Show HN: Twitch Roulette – Find live streamers who need views the most

    2. 納許維爾圖書館推出「記憶實驗室」協助數位化家庭錄影帶

    公共服務與科技的結合!納許維爾圖書館提供了專業設備,讓市民可以免費將老舊的家庭錄影帶數位化。這項計畫展示了圖書館在數位時代的新角色:成為社區文化資產保存的技術中心。

    🔗 閱讀原文:Nashville library launches Memory Lab for digitizing home movies

    3. 全人類都想拯救的那隻蜜蜂

    這篇動人的文章介紹了「鏽斑熊蜂」(Rusty-patched Bumble Bee) 的處境。它不僅討論了生態多樣性,也反思了當一個物種成為保護象徵時,我們如何平衡科學、情感與資源的分配。

    🔗 閱讀原文:The bee that everyone wants to save

    🌟 今日觀點:效能與效率的雙重奏

    今日的科技趨勢顯示出一個明確的雙軌走向:一方面是在硬體層面透過快取堆疊 (AMD)變頻顯示 (LG) 瘋狂壓榨物理效能;另一方面則是在軟體架構上,試圖為 AI 代理程式 (JAI/Claude) 建立更合理的執行框架。

    💡 給讀者的行動建議:

    • 關注「AI 上下文管理」: 隨著 AI 代理程式日益普及,學習如何有效地管理你的本地 .claude/ 或類似檔案,將成為開發者的基本功。
    • 硬體選購新標準: 下一代筆電的競爭力將不再只是 CPU 時脈,而是能降低能耗的 LTPO 面板技術。
    • 支持去中心化發現: 試試看像 Twitch Roulette 這樣的工具,主動跳出演算法的同溫層,或許會有意想不到的收穫。
  • Spring Boot OMS Code Review 實戰:20 個 Bug 與事件驅動架構的一課

    重點摘要

    • 三輪 Code Review 共找出 20+ 個問題,從 NPE 連鎖到種子資料欄位名稱全錯
    • Kafka Seeder 寫了三個版本,每次重寫都是對「事件驅動架構正確入口」理解的加深
    • 能走事件流就走事件流:API → Kafka → Consumer → DB,每一層都有可追蹤、可重試的意義
    • 21 個 Java 容器沒有 JVM 記憶體限制,用 JAVA_TOOL_OPTIONS 一行解決,不需改 Dockerfile

    這是 多通路電商 OMS 系統開發過程中的一天工作紀錄。系統整合了 Momo、Shopee、Yahoo 等電商平台,透過 Kafka 事件流處理訂單同步、退貨與統計。今天的目標:完成 feature/stats-pipeline 分支上的所有待辦修復,讓系統能順利 docker compose up,並驗證端對端資料流。

    三輪 Code Review:每一輪都有新發現

    第一輪:已知清單上的 7 個問題

    進入狀態之前就有一份清單,分為 Critical、Warning、Info 三個等級:

    等級 問題 修復方式
    CriticalOrderUpsertConsumer .get() NPE 連鎖.path() + 加 orderDataJson null 守衛
    Criticaldaily_statistics.id 缺 NOT NULL加約束 + DEFAULT partition
    CriticalReturnUpsertConsumer 未寫 stats dirty marker新增 Redis ZSET 寫入
    WarningDailyStatisticsService early return 留舊資料改成刪除過時的 stats 列
    Warningenum 預設值小寫 'pending'改大寫 'PENDING',與 JPA EnumType.STRING 對齊
    WarningRetryJobConsumer MissingNode cast.isObject() 判斷再 cast

    其中 enum 大小寫這個問題值得特別說明。Java 的 @Enumerated(EnumType.STRING) 在讀取時呼叫 Enum.valueOf(),這個方法是 case-sensitive 的。資料庫預設值寫 'pending',但 enum 常數叫 PENDING,啟動時不會出錯,但一讀到有 DEFAULT 值的列就會拋 IllegalArgumentException

    第二輪:種子資料是另一個地雷區

    Schema 修完了,以為大功告成,結果種子資料(02-seed-data.sql)是第二個地雷區:

    1. BCrypt hash 是假的$2a$10$dummyhashfordevonly... 根本不是有效的 BCrypt hash,Spring Security 的 passwordEncoder.matches() 永遠回傳 false,登入 100% 失敗。
    2. 訂單狀態小寫'completed''shipped' — 和上面一樣的 case-sensitive 問題,這次在資料列而不是 DEFAULT 值。
    3. daily_statistics 欄位名稱全錯:用了 order_counttotal_amount 這些不存在的欄位名,docker compose up 的 DB 初始化階段會直接 fail。

    這些問題的共同特徵是:compile time 抓不到,schema validate 也抓不到。Hibernate 的 ddl-auto: validate 只單向檢查「entity 中有 mapping 的欄位是否存在於 DB」,不會反向驗證 SQL 腳本的正確性。唯一的防護是跑起來測試。

    第三輪:21 個容器,一個 JVM 記憶體問題

    系統在開發機上跑 21 個 Java 容器(Spring Boot services),沒有任何 JVM heap 限制。JVM ergonomic sizing 預設使用系統 RAM 的 25%,7.4GB 可用 RAM 很快就會不夠。

    解法是在 docker-compose.yml 每個服務加 JAVA_TOOL_OPTIONS

    environment:
      JAVA_TOOL_OPTIONS: "-Xmx256m -XX:+ExitOnOutOfMemoryError"

    JAVA_TOOL_OPTIONS 是 JDK 標準環境變數,JVM 啟動時自動讀取,不需要修改 Dockerfile 的 ENTRYPOINT-XX:+ExitOnOutOfMemoryError 讓容器在 OOM 時立刻崩潰(而不是卡死),對 Docker 的 restart: unless-stopped 友好,等於有了自動恢復機制。

    Seeder 的三次重寫:對事件驅動架構的理解之旅

    今天最有收穫的插曲。目標是「準備一個 Docker 服務,打假訂單資料,確認整體資料流順暢」。這個任務看起來很簡單,結果寫了三個版本。

    第一版:直接打 Kafka(被打槍)

    第一直覺:用 kafka-python 直接連 kafka:9092,組好 ORDER_UPSERT 訊息送到 order.process topic。快速、直接。

    問題:系統對外只有 API,直接操作 Kafka 是繞過了系統設計的邊界。內部基礎設施不應該是外部系統的接入點。

    第二版:打 POST /api/orders(沒走事件流)

    改用 REST API。先 login 拿 JWT,再 POST /api/orders

    問題:OrderController.createOrder() 是直接寫資料庫,跳過了整個 Kafka pipeline。Stats dirty marker 不會被寫入,DailyStatisticsService 不會被觸發,daily_statistics 表不會更新。雖然訂單進了 DB,但「整體資料流」沒有跑通。

    第三版:新增正確的 API 端點(走完整事件流)

    UserOrderController 新增 POST /api/user/orders,接收訂單資料後發布 ORDER_UPSERT 到 Kafka,回傳 202 Accepted:

    POST /api/user/orders  (帶 JWT)
      → 查 Channel → Platform(取得 platformId)
      → 組 ORDER_UPSERT 訊息(header + body + hash)
      → kafkaTemplate.send("order.process", ...)
      → 回傳 202 Accepted
    
    接著:
      Kafka order.process
        → OrderUpsertConsumer(Redis 去重 → INSERT/UPDATE)
            → stats dirty marker 寫入 Redis ZSET
                → StatsRecalcHandler(定時掃)
                    → DailyStatisticsService.recalculate()
                        → daily_statistics 更新

    端到端,一條不少。

    為什麼「能走事件流就走事件流」不只是口號

    三次重寫讓這個原則從抽象變得具體。走事件流的好處不只是「解耦」這個詞能涵蓋的:

    層面 直接寫 DB 走 Kafka 事件流
    可追蹤性只有 DB recordKafka UI 可看完整訊息歷史,帶 traceId
    錯誤處理拋 exception,呼叫方看到 500失敗走 task.failed → retry → task.dlt
    去重需要自己實作Consumer 有 Redis + DB 兩層去重
    統計觸發需要額外呼叫Consumer 自動寫 dirty marker,批次計算
    一致性邏輯分散在多處無論來源(channel job / API),走同一套邏輯

    最後一點是最重要的:一致性。不管訂單是從 Shopee channel job 來的,還是透過 API 手動新增的,都走同一個 OrderUpsertConsumer,同一套去重邏輯,同一套 stats pipeline。系統裡沒有「繞過」的快捷路徑。

    今日修改摘要

    檔案 類型 說明
    01-schema.sqlBug FixNOT NULL、DEFAULT partition、enum 大小寫
    02-seed-data.sqlBug FixBCrypt hash、訂單狀態大小寫、daily_statistics 欄位名稱
    OrderUpsertConsumerBug Fix.get().path(),移除 unused import
    ReturnUpsertConsumerBug Fix加 stats dirty marker、移除 unused import
    DailyStatisticsServiceBug Fixearly return 時刪除過時 stats 列
    OrderServiceBug FixNOT NULL 欄位的 null 守衛
    docker-compose.ymlInfra所有 21 個 Java 容器加 JAVA_TOOL_OPTIONS
    UserOrderControllerFeature新增 POST /api/user/orders → Kafka pipeline
    docker/test-data-generator/FeaturePython seeder,透過 API 打假訂單

    結語:追蹤路徑比結果更重要

    今天花最多時間的不是寫 code,而是「把對的事情弄清楚」。Seeder 寫了三個版本,不是因為技術難,而是因為對系統的理解在逐漸深化。

    一個好的事件驅動系統,它的「正確入口」只有一個。找到那個入口,比快速把功能做出來更重要。這條原則同樣適用於大系統的任何角落:追蹤路徑比結果更重要,因為你下次出問題的時候,你需要知道訊息從哪裡來、往哪裡去。

    能走事件流就走事件流。能用快取盡量快取。這不是教條,是讓大系統在出問題時還能被追蹤、被診斷、被修復的保險。

  • iDempiere Plugin 開發完整指南:踩遍台灣統一發票的 10 個坑

    重點摘要

    • 我們用兩天為 iDempiere 12.0 從零開發了台灣統一發票 OSGi Plugin,踩了至少 7 個主要的坑
    • 最燒時間的 bug:SeqNoGrid 缺失導致 Grid View 在按「新增」時拋出 IndexOutOfBoundsException——官方文件完全沒提
    • EventHandler 模式是 iDempiere 12 的正確驗證方式,ModelValidator 不透過 OSGi DS 登錄根本不執行
    • 2Pack ZIP 結構、事件主題比較、雙 JVM 部署問題——每一個都能讓你白白浪費半天

    這篇文章記錄了我們為 iDempiere 12.0 從零開發台灣統一發票(統一發票)與營業稅申報 Plugin 的完整過程。如果你正在開發 iDempiere Plugin,這篇文章能幫你少掉至少一天的除錯時間。

    為什麼要做這個 Plugin?

    台灣的統一發票制度獨特而嚴格:每兩個月一期的雙月申報週期、財政部核配的字軌號碼(如 AA01234567)、三聯式(B2B)和二聯式(B2C)的不同計稅方式、以及嚴格的 FLOOR 取捨規定。

    這些都是 iDempiere 標準功能完全沒有覆蓋的。既有的 C_Invoice 系統不知道什麼是「字軌」,不知道進項折讓要在哪一期申報,更不知道 401 申報表長什麼樣子。所以我們做了這個 Plugin。

    系統架構概覽:四張表對應四個階段

    iDempiere Plugin 開發的核心是理清業務模型。台灣統一發票有清楚的生命週期,我們用四張資料表對應:

    財政部核配字軌
          │
          ▼
    TW_InvoicePrefix      ← 字軌管理(AA、AB 等,號碼範圍、有效期)
          │ 開立發票時
          ▼
    TW_Invoice_Prefix_Map ← 每張發票的字軌號碼對應(含買方統一編號)
          │ 如有退貨/折讓
          ▼
    TW_InvoiceAdjustment  ← 銷項/進項折讓(方向、期別、超期申報)
          │ 期末
          ▼
    TW_TaxStatement       ← 401 申報表(銷項稅、進項稅、留抵、應納稅額)

    技術層面:OSGi bundle(Equinox),2Pack 管理 dictionary,AbstractEventHandler 做 PO 事件驗證,服務層純 Java 方便單元測試。最終成品:87 個測試全部通過,4 張 TW_* 資料表,4 個 iDempiere 視窗。

    把台灣稅法翻成 Java 程式碼

    稅額計算:FLOOR,不是 ROUND

    這是財政部的明確規定,一律捨去,不四捨五入。聽起來簡單,但在邊界值差一塊錢,申報出去就是對不上帳:

    // 二聯式(B2C):含稅金額已知,反推銷售額和稅額
    BigDecimal saleAmount = grossAmount
        .divide(new BigDecimal("1.05"), 0, RoundingMode.FLOOR);
    BigDecimal taxAmount = saleAmount
        .multiply(new BigDecimal("0.05"))
        .setScale(0, RoundingMode.FLOOR);
    
    // 注意:taxAmount ≠ grossAmount - saleAmount
    // 財政部規定:用前者(先算銷售額,再乘 5%)

    字軌狀態機:單向前進

    字軌狀態只能往前走,不能回頭:I(未啟用)→ A(使用中)→ C(已用完)。這是台灣稅法要求,已啟用的字軌必須被追蹤,不能撤回。

    // EventHandler 在 PO_BEFORE_CHANGE 攔截
    if ("A".equals(oldStatus) && "I".equals(newStatus))
        throw new AdempiereException("使用中字軌不可降回未啟用(台灣稅法)");
    if ("C".equals(oldStatus))
        throw new AdempiereException("已用完字軌不可再變更狀態");

    雙月申報期別計算

    // 從發票月份算期別
    int period = (month - 1) / 2 + 1;  // 1=1-2月, 2=3-4月 ... 6=11-12月

    七個真實踩坑紀錄

    坑 1:事件主題比較,一個字毀掉一切

    這個 bug 讓狀態驗證完全靜默失效了很久。症狀是:程式碼看起來完全正確,但驗證從來不觸發。

    // 錯誤 — 永遠不會觸發
    if (topic.endsWith("po_before_change")) { ... }
    
    // 正確
    if (IEventTopics.PO_BEFORE_CHANGE.equals(topic)) { ... }

    iDempiere 的 topic 格式是 org/adempiere/po/PO_BEFORE_CHANGE(全大寫),endsWith 配對小寫後綴當然不匹配。靜默失效最可怕——不報錯,只是所有驗證都沒執行。規則:事件主題永遠用 IEventTopics 常數。

    坑 2:ModelValidator 是個陷阱

    Validator 類別一開始實作了完整的 ModelValidator 介面(initialize、modelChange、docValidate、login…),程式碼寫得很整齊,但驗證從來不執行。

    原因:ModelValidator 需要透過 ModelValidationEngine.addModelValidator() 主動登錄。單純 implements ModelValidator + @Component 什麼都不會發生。

    正確的 iDempiere 12 Plugin 驗證模式:

    // *Validator.java    → 純靜態方法,不實作任何介面
    // *EventHandler.java → extends AbstractEventHandler,OSGI-INF/*.xml 登錄為 DS 服務
    
    // EventHandler 捕捉 OSGi 事件,呼叫 Validator 的靜態方法做驗證
    @Component(immediate = true, service = IEventHandler.class)
    public class InvoicePrefixEventHandler extends AbstractEventHandler {
        @Override
        protected void doHandleEvent(Event event) {
            String topic = (String) event.getProperty(IEventTopics.EVENT_TOPIC);
            if (IEventTopics.PO_BEFORE_CHANGE.equals(topic)) {
                String err = InvoicePrefixValidator.validateStatusTransition(...);
                if (err != null) throw new AdempiereException(err);
            }
        }
    }

    坑 3:2Pack ZIP 打包方式,犯了兩次

    iDempiere 的 PackIn 解壓 ZIP 到 /tmp/,預期路徑是 /tmp/tw_invoice_system/dict/PackOut.xml

    第一次:ZIP 裡多了 2pack/ 前綴層。第二次:用了 zip -j(junk paths),把所有目錄剝掉,路徑變成 /tmp/PackOut.xml/dict/PackOut.xml——把文件名當目錄了。

    # 正確打包方式
    mkdir -p /tmp/b/tw_invoice_system/dict
    cp PackOut.xml /tmp/b/tw_invoice_system/dict/
    cd /tmp/b && zip -r 2Pack_1.0.10.zip tw_invoice_system/
    
    # 永遠要驗證結構
    unzip -l 2Pack_1.0.10.zip

    坑 4:SeqNoGrid 缺失 → Grid View 崩潰(最燒時間)

    這是整個過程中最「無辜」的 bug——不是邏輯錯誤,是 XML 少了兩個欄位。所有 73 個 AD_Field 元素都缺少 <SeqNoGrid> 和正確的 <IsDisplayedGrid>

    iDempiere 12 的 Grid View 渲染器在初始化行編輯器時需要 SeqNoGrid 做排序依據,全部 NULL 導致:

    java.lang.IndexOutOfBoundsException: Index: 2
        at GridTabRowRenderer.editCurrentRow

    任何視窗,只要在 Grid 模式下按「新增」,必爆。這個 bug 在官方文件裡完全沒有提到。修復:補上這兩個欄位。

    <SeqNo>10</SeqNo>
    <SeqNoGrid>10</SeqNoGrid>        <!-- = SeqNo,顯示欄位 -->
    <IsDisplayedGrid>Y</IsDisplayedGrid>
    
    <!-- 隱藏欄位(SeqNo=0)-->
    <SeqNoGrid>0</SeqNoGrid>
    <IsDisplayedGrid>N</IsDisplayedGrid>

    坑 5:兩個 JVM 搶 Port,Deploy 進錯的那個

    症狀很奇怪:OSGi telnet console 顯示 bundle ACTIVE ✅,但 Web Console 找不到這個 bundle ❌。

    最後發現機器上跑著兩個 iDempiere JVM:

    JVM 擁有 Port 說明
    JVM-A(舊的)8080/8443Web 瀏覽器連這個
    JVM-B(新的)12612OSGi telnet 連這個

    deploy.sh 透過 telnet 把 bundle 裝進了 JVM-B,但使用者看的 Web Console 是 JVM-A。原因是 idempiere-server.sh 有 restart loop,systemctl restart 啟動了新 JVM,但舊 JVM 沒死。

    # 診斷方法
    ps aux | grep java | grep -v grep | wc -l  # > 1 就是這個問題
    
    # 修復:kill 兩個 JVM,等 30 秒讓 port 釋放,再 restart
    sudo kill <pid1> <pid2>
    sleep 30
    sudo systemctl restart idempiere

    坑 6:AD_Field UUID 衝突

    2Pack 重裝時,iDempiere 已為新建的 Tab 自動插入標準欄位(AD_Client_ID、AD_Org_ID、IsActive)。當 PackIn 再次嘗試用不同 UUID 插入同一個 (tab_id, column_id) 組合時,違反了 UNIQUE(ad_tab_id, ad_column_id) 約束,拋出 POSaveFailedException

    解法:對這類「系統可能已存在」的標準欄位,PackOut.xml 使用固定的 placeholder UUID。PackIn 遇到重複就 UPDATE 而不是 INSERT。升版 UUID 策略:已存在的 field → 保留原 UUID;新增的 field → 才用新的 uuid4。不要為了「整齊」換掉既有 UUID。

    坑 7:清除 DB 做全新安裝——FK 刪除順序

    當要做「完整卸載 → 清 DB → 重裝」的驗證時,刪資料的順序錯了好幾次。

    正確順序:AD_Field → AD_Tab → AD_Window,每一步都有 FK 指向下一個。特別注意:AD_PreferenceAD_Menu 也會 FK 到 AD_Window,忘記刪這兩個就卡住。另外:ad_package_imp 記錄了 2Pack 安裝歷史,不清掉的話 Incremental2PackActivator 判定「已安裝過同版本」就跳過不執行。

    如果重來一次,我們會怎麼做

    1. 先建立 PackOut.xml 驗證腳本

    每次 2Pack 安裝後自動跑這些 SQL,出問題立刻知道,不用等到 UI 爆炸:

    -- SeqNoGrid 是否設定
    SELECT tablename, count(*) FILTER (WHERE seqnogrid > 0) ok
    FROM ad_table JOIN ad_tab ... JOIN ad_field ...
    WHERE tablename LIKE 'TW_%' GROUP BY tablename;
    
    -- _UU 欄位是否 updateable
    SELECT columnname, isupdateable FROM ad_column
    WHERE tablename LIKE 'TW_%' AND columnname LIKE '%_UU';

    2. 把 ZIP 結構驗證寫進 Maven build

    <plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>exec-maven-plugin</artifactId>
      <executions>
        <execution>
          <phase>verify</phase>
          <goals><goal>exec</goal></goals>
          <configuration>
            <executable>bash</executable>
            <arguments>
              <argument>-c</argument>
              <argument>unzip -l resources/META-INF/2Pack_*.zip | grep -q "dict/PackOut.xml"</argument>
            </arguments>
          </configuration>
        </execution>
      </executions>
    </plugin>

    3. 每次 commit 前跑 deploy.sh –check

    加一個 dry-run 模式,只驗證 JAR 可部署、OSGi console 可連線、bundle 存在,不實際更新。讓這個檢查成為 commit hook。

    給下一個要做 iDempiere Plugin 的人

    iDempiere 的文件很少,很多行為只有讀原始碼才能理解。以下是花最多時間搞懂的六件事:

    # 規則 說明
    1ModelValidator 不是 OSGi 服務@Component 不會讓它跑起來,要用 EventHandler
    2SeqNoGrid 是 Grid View 必備欄位缺了不報錯,只在按「新增」時崩潰
    32Pack ZIP 結構有嚴格要求打包後一定要 unzip -l 驗證,絕不用 zip -j
    4_UU 欄位必須 IsUpdateable=Y否則 UUID 永遠 NULL
    5IEventTopics 常數不要用字串字面量比較 topic,靜默失效
    6Incremental2PackActivator 記住安裝歷史同版本不重裝,升版要改 ZIP 檔名

    後記:實作兩個流程(又踩了三個坑)

    文章發完當天,隨即著手實作原本標注為「計劃中功能」的兩支 SvrProcess

    • GenerateTaxStatementProcess — 聚合 TW_Invoice_Prefix_Map 的銷售資料、折讓,產生 TW_TaxStatement 申報記錄
    • ExportTaxReportProcess — 讀取 TW_TaxStatement,輸出財政部格式 CSV

    同時也需要把這兩個流程透過 2Pack 登錄到 iDempiere 的 AD_Process / AD_Menu,讓使用者可以從選單觸發。這一輪又多踩了幾個坑。

    坑 8:AD_Process_Para 的 AD_Process_ID 必須明確寫入

    2Pack 的元素處理器(ElementHandler)有一個隱藏的不一致性

    AD_Tab 時,把它放在 <AD_Window> 元素內,Tab 的 AD_Window_ID 會自動繼承父元素——不需要重複宣告。大多數巢狀元素都這樣工作。

    AD_Process_Para 不同。ProcessParaElementHandler 呼叫 filler.autoFill() 處理子元素,但不從父 <AD_Process> 讀取 context 填入 AD_Process_ID

    結果:2Pack 安裝過程看起來正常執行,沒有任何錯誤訊息,但 Process Para 根本沒有存入。直到手動查 DB 才發現空的。真正的錯誤需要翻 PostgreSQL log:

    tail -f /var/log/postgresql/postgresql-16-main.log
    # ERROR: null value in column "ad_process_id" violates not-null constraint

    修正:每個 <AD_Process_Para> 元素內都必須顯式宣告 AD_Process_ID

    <AD_Process_Para type="table">
      <AD_Process_ID reference="uuid" reference-key="AD_Process">{process-uuid}</AD_Process_ID>
      <Name>Statement Year</Name>
      ...
    </AD_Process_Para>

    坑 9:FieldLength NOT NULL(但 iDempiere log 不說)

    修好 AD_Process_ID 之後,再次安裝,再次失敗,再次查 PostgreSQL log:

    ERROR: null value in column "fieldlength" violates not-null constraint

    FieldLengthad_process_para 資料表中是 NOT NULL 欄位,但如果 PackOut.xml 沒有提供,iDempiere 的 log 只會說 Failed to save ProcessPara——沒有欄位名稱,沒有 SQL,什麼都沒有。

    不同 Reference 型別的預設長度:

    AD_Reference_ID 型別 FieldLength
    11Integer10
    17List1
    10String實際最大長度

    教訓:2Pack 的錯誤訊息有時候刻意模糊。遇到 Failed to save 之類的通用錯誤,直接去查 PostgreSQL log,那裡才有真相。

    坑 10:iDempiere 物理表在 adempiere schema,不在 public

    寫完 clean_reinstall.sh(一個用來清除所有 TW_* 字典並重新部署的腳本)後,發現 DROP TABLE 完全沒作用:

    DROP TABLE IF EXISTS TW_InvoicePrefix;  -- 執行成功,但表還在

    原來 iDempiere 12.0 把物理表建在 adempiere schema,而不是 public。用 information_schema.tables WHERE table_schema='public' 查詢,一張 TW_* 表都找不到。

    正確用法:

    -- 確認表存在
    SELECT tablename FROM pg_tables WHERE schemaname='adempiere' AND tablename ILIKE 'tw_%';
    
    -- 刪除表
    DROP TABLE IF EXISTS adempiere.TW_InvoicePrefix;
    DROP TABLE IF EXISTS adempiere.TW_Invoice_Prefix_Map;

    這也影響 psql 互動查詢——連線後預設 search_path 如果不含 adempiere,直接 SELECT * FROM TW_InvoicePrefix 會找不到表。

    SvrProcess 實作要點

    SvrProcess 的標準結構很直覺——prepare() 讀參數、doIt() 做事。幾個需要注意的地方:

    • 不要呼叫 ps.setAD_Client_ID():那是 PO 類別的 protected 方法,不是 PreparedStatement 的方法。在 SvrProcess 裡用 getAD_Client_ID() 取值,直接 ps.setInt(1, getAD_Client_ID())
    • DB.prepareStatement() 而不是 JDBC 直接連線:iDempiere 的 DB class 管理連線池和事務,不要繞過它。
    • 記得 DB.close(rs, ps):在 finally block 釋放資源。

    clean_reinstall.sh — 開發期間的救命工具

    開發期間反覆修改 PackOut.xml,每次都要手動清資料庫重裝,非常繁瑣。寫了一個腳本自動化整個流程:

    1. 依 FK 順序刪除所有 TW 字典(Window_Access → Process_Access → Field → Tab → Menu → Window → Process_Para → Process)
    2. Drop 物理表(adempiere.TW_*
    3. 清除後設資料(Column → Table → Sequence → Element → Reference → EntityType)
    4. 清 AD_Package_Imp_Detail / AD_Package_Imp(注意:Detail 要先刪,因為有 FK)
    5. 呼叫 deploy.sh 重新部署

    FK 刪除順序是最麻煩的部分——錯一個順序就會碰到 FK 違規,整個腳本失敗。從失敗中整理出來的正確順序如上。

    最終成果(v1.0.11)

    Bundle:    tw.idempiere.invoice.tax v1.0.0 (2Pack v1.0.11)
    Tests:     89 個,全數通過
    Tables:    4 張 TW_* 資料表
    Windows:   4 個 iDempiere 視窗
    Processes: 2 個(GenerateTaxStatement + ExportTaxReport)
    Fields:    73 個 AD_Field(含正確 SeqNoGrid)
    Menu:      7 個項目(1 父選單 + 4 視窗 + 2 流程)
    Status:    ACTIVE,Grid View 正常,流程可從選單觸發
  • Hacker News 每日精選 – 2026-03-27

    🚀 科技趨勢週報:從 AI 自主化到硬體時代的終結

    今日的科技圈呈現出極為動態且多元的面貌,從 AI 代理人(Agents)技術的快速演進到蘋果桌機產品線的重大策略轉向,揭示了軟體定義一切的時代正進入深水區。讀者應關注這些變革,因為它們不僅改變了我們的開發工作流,更重新定義了安全防禦與硬體設備的價值觀。

    🤖 AI / 機器學習

    🛡️ LiteLLM 惡意軟體攻擊的即時應對紀錄

    這是一篇驚心動魄的技術日誌,詳細記錄了開發團隊面對 LiteLLM 遭受惡意軟體攻擊時的每一分鐘反應。作者揭露了現代 AI 基礎設施在供應鏈攻擊面前的脆弱性,以及在壓力下進行漏洞修補與社群溝通的實戰經驗。這對於所有依賴開源 AI 工具的開發者來說,是一記重要的安全警鐘。

    原文連結:My minute-by-minute response to the LiteLLM malware attack

    🔍 Chroma Context-1:訓練一個具備「自我編輯」能力的搜尋代理

    Chroma 團隊發佈了 Context-1 的研究成果,這是一個能夠在執行任務時自主調整與編輯檢索上下文的 AI 代理。與傳統 RAG(檢索增強生成)不同,它能識別資訊的相關性並動態優化搜尋策略。這代表著 AI 正在從單純的資訊讀取者,進化為具備決策能力的資料分析官。

    原文連結:Chroma Context-1: Training a Self-Editing Search Agent

    💻 AI 代理之間的配對編程(Agent-to-Agent Pair Programming)

    如果讓兩個 AI 代理互相協作寫程式會發生什麼事?這篇文章探討了多代理系統(Multi-agent systems)在軟體開發中的潛力。透過角色分工(如一個負責寫 code,另一個負責審核),這種模式能顯著降低 LLM 的幻覺問題,並提高代碼生成的複雜度與準確性。

    原文連結:Agent-to-agent pair programming

    🌐 在每月 7 美元的 VPS 上透過 IRC 運行 AI 代理

    這是一個極具駭客精神的專案,開發者成功在極低預算的虛擬主機上部署了 AI 代理,並選擇歷史悠久的 IRC 作為其傳輸層。這證明了 AI 的部署不一定需要昂貴的基礎設施,透過輕量化架構與創新的通訊協定,AI 應用的門檻正大幅降低。

    原文連結:Show HN: I put an AI agent on a $7/month VPS with IRC as its transport layer

    🛠️ 開發工具與平台

    ⏰ Claude:網頁排程任務指南

    Anthropic 的 Claude 平台推出了網頁排程任務的官方文檔,指導開發者如何有效地在 Web 環境中執行長時間運行或循環性的任務。這標誌著大型語言模型平台正進一步整合自動化工作流,讓開發者能直接在生態系內構建更複雜的非同步應用。

    原文連結:Schedule tasks on the web

    🏠 Dobase – 你的工作空間,你的伺服器

    Dobase 是一個強調主權與隱私的現代工作空間解決方案,讓使用者能輕鬆地在自己的伺服器上部署協作工具。它試圖在 Notion 的便利性與自託管(Self-hosting)的安全性之間取得平衡,對於重視數據掌控權的團隊來說非常具吸引力。

    原文連結:Dobase – Your workspace, your server

    💼 創業、商業與硬體

    🍏 蘋果傳出將停產 Mac Pro

    根據最新的市場消息與分析,蘋果可能決定停止其頂級工作站 Mac Pro 的產品線。這反映了 Apple Silicon 晶片強大的集成能力已使 Mac Studio 足以應付絕大多數專業需求,Mac Pro 的擴充性優勢逐漸消失,象徵著一個硬體擴充時代的終結。

    原文連結:Apple discontinues the Mac Pro

    🎨 設計與技術考古

    🎨 為什麼控制室大多採用「海泡綠」?

    這是一篇迷人的色彩心理學與工程史研究,探討了 20 世紀中葉控制室廣泛使用海泡綠(Seafoam Green)的原因。文章深入分析了減輕視覺疲勞、營造鎮靜效果以及當時的工業審美。這種顏色在核電廠、潛艇與化學工廠中的盛行,是人因工程學發展的重要見證。

    原文連結:Why so many control rooms were seafoam green (2025)

    📖 襯線體與無襯線體字型的易讀性研究

    這份 2022 年的研究報告針對長久以來的字型爭論提供了科學實證。它探討了在數位與印刷媒介上,襯線(Serif)與無襯線(Sans Serif)字型對閱讀速度與理解能力的影響。對於 UI/UX 設計師與內容創作者來說,這提供了重要的排版參考依據。

    原文連結:The Legibility of Serif and Sans Serif Typefaces (2022)

    🎮 其他創意專案

    📡 DOOM Over DNS:透過 DNS 協定玩遊戲

    「這能跑 DOOM 嗎?」這次的答案是透過 DNS 協定。這個專案展示了如何利用 DNS 的請求與響應機制來傳輸遊戲數據。雖然這極度不具效率,但它體現了極致的駭客創造力,挑戰了網路協定的極限。

    原文連結:DOOM Over DNS

    💡 今日觀點:自主性與安全性的雙重賽跑

    綜觀今日的熱門趨勢,我們可以看到兩個核心主題:自主代理人(Autonomous Agents)的崛起軟體供應鏈的脆弱性。當我們開始嘗試讓 AI 代替人類進行配對編程、自我編輯搜尋脈絡、甚至是獨立在低廉伺服器上運作時,我們也正面臨著像 LiteLLM 遭受攻擊那樣的新型安全威脅。

    “未來的開發者將不再只是代碼的撰寫者,而是 AI 協作網絡的編排者與安全防衛者。”

    🛠️ 給讀者的行動建議:

    • 安全審查: 檢查你的 AI 開發工具鏈,確保對第三方開源組件有適當的監控與隔離。
    • 實驗 Agentic Workflow: 嘗試將 AI 從「對話框」移出,思考如何將其整合進自動化排程(如 Claude 提到的 Tasks)或多代理協作流程中。
    • 簡化硬體思維: 隨著 Mac Pro 等高度擴充設備可能淡出,將重心轉向雲端擴展性與高性能、低功耗的集成運算單元。

    感謝閱讀!我們下次再見。 👋

  • Hacker News 每日精選 – 2026-03-26

    🚀 科技趨勢週報:從 AGI 基準測試到特斯拉硬體拆解

    今日的科技圈展現了極大的跨度,從挑戰人工智慧推理極限的 ARC-AGI,到極客玩家將特斯拉電腦搬上辦公桌的硬體實驗。這些發展揭示了一個核心趨勢:我們正在不斷推開「黑盒」,無論是大型語言模型的思考邏輯,還是封閉式硬體與個人隱私的邊界。

    為什麼你該關心?因為這些討論不僅涉及技術底層的演進,更直接影響到未來我們如何衡量智慧、保護隱私,以及在 AI 輔助開發的時代中,我們產出的程式碼價值何在。

    🤖 AI / 機器學習

    【通用人工智慧新挑戰】ARC-AGI-3 基準測試發佈

    ARC-AGI 是由 Google 研究員 François Chollet 提出的基準測試,旨在衡量 AI 是否具備真正的「推理能力」而非僅僅是「記憶」。最新的第三版挑戰賽繼續推動研究者開發能應對前所未見任務的演算法,而不僅僅是依賴海量數據訓練。這對於判斷我們距離真正的 AGI(通用人工智慧)還有多遠具有決定性的指導意義。

    🔗 閱讀原文

    【AI 影響力分析】90% 與 Claude 相關的程式碼產出流向低星 GitHub 專案

    一項針對 Claude AI 工具生成內容的追蹤顯示,高達 90% 的程式碼最後都提交到了星數(Stars)小於 2 的 GitHub 倉庫中。這反映了 AI 正極大地降低了個人開發與微型專案的門檻,但同時也引發了關於 AI 是否正在製造大量「數位廢棄物」或僅僅是輔助業餘愛好者的討論。這對軟體工程的未來生態提供了獨特的觀測數據。

    🔗 閱讀原文

    🛠️ 開發工具與硬體控制

    【底層效能探索】編譯器優化的兩項深度研究

    這篇文章深入探討了編譯器在代碼優化過程中的新舊挑戰,重點分析了自動向量化(Auto-vectorization)與指令調度。對於追求極致效能的開發者來說,理解編譯器如何轉換程式碼,是提升系統吞吐量與減少延遲的關鍵。文章透過具體的數據實驗,展示了現代優化技術的極限與潛力。

    🔗 閱讀原文

    【硬體極客】自製 FPGA 開發板成功運行《雷神之錘 II》

    這是一個硬體工程的壯舉。作者分享了從零開始設計 FPGA 板卡,並最終讓經典 3D 遊戲《Quake II》流暢運行的心路歷程。這不僅涉及硬體電路設計,更包含了圖形渲染管線在硬體層級的實現,是所有對硬體描述語言與嵌入式系統開發感興趣讀者的必讀佳作。

    🔗 閱讀原文

    💼 創業、商業與社會科學

    【學術警訊】廣為引用的商學院論文被指控數據造假

    哥倫比亞大學統計學教授發文揭露了一篇在商學院領域極具影響力的論文存在嚴重的虛假陳述。這起事件再次敲響了學術誠信的警鐘,特別是在依賴量化數據做出決策的商業環境中。這提醒創業者與決策者,在引用所謂的「科學證據」時,必須具備更敏銳的批判性思考與查核意識。

    🔗 閱讀原文

    【品牌悖論】雷蒙斯合唱團的真相:T恤賣得比唱片還多

    傳奇龐克樂隊 Ramones 雖然音樂影響深遠,但其商業成功的核心竟然是「周邊商品」。這篇文章探討了符號化消費如何超越了產品本身,對於當今想要建立品牌、經營社群的創業者來說,這是一個關於品牌價值異化與轉化的經典商業案例。

    🔗 閱讀原文

    🌐 其他值得關注的主題

    【隱私保衛戰】歐盟仍未放棄掃描你的私人訊息與照片

    針對端到端加密(E2EE)的隱私爭議再次升溫。歐盟正在推動的新法案可能要求服務供應商掃描用戶的通訊內容以打擊犯罪。這引起了隱私倡議者的強烈反彈,認為這將徹底摧毀數位通訊的安全性。如果你關心數位人權與通訊加密,這是不容忽視的最新進展。

    「一旦開了後門,隱私便不復存在。」

    🔗 閱讀原文

    【硬體拆解】利用事故車零件在辦公桌上運行 Tesla Model 3 電腦

    作者透過從報廢車輛中搜刮的硬體零件,成功在非車載環境下啟動了特斯拉的媒體控制單元(MCU)。這篇技術部落格詳細記錄了電源供應、接線與繞過系統限制的過程,展現了高超的逆向工程技巧,也揭示了現代智慧車輛硬體架構的複雜性。

    🔗 閱讀原文

    【跨界驚喜】我的天文攝影出現在電影《火星任務》(Project Hail Mary) 中

    一位天文愛好者分享了他拍攝的星空圖像如何被選中並出現在好萊塢大片中的故事。這不僅是個人的驕傲時刻,也展示了在當今數位時代,高品質的個人創作如何透過社群媒介獲得跨領域的巨大價值。

    🔗 閱讀原文

    【科學新知】過度耕作會削弱土質,地震學家揭示農場危機

    華盛頓大學的研究指出,傳統的過度耕作方式會改變土壤結構,使其在面對地震等自然災害時變得更加脆弱。這項研究連結了農業技術與地質安全,提醒我們在追求糧食產量的同時,必須正視土地物理特性的長期損害。

    🔗 閱讀原文

    💡 今日觀點:透明度與完整性的保衛戰

    綜觀今日的熱門話題,「透明度」是一個反覆出現的幽靈。無論是 ARC-AGI 對 AI 黑盒的挑戰、學術造假的揭發、隱私權的保護,還是對特斯拉硬體的物理性拆解,我們都看到人們正努力看清系統運行的真相。

    給讀者的行動建議:

    • 對工具保持警覺: 善用 AI 加速開發,但要確保你是在創造價值,而非只是在 GitHub 上製造無人問津的「低星雜訊」。
    • 捍衛隱私權: 持續關注歐盟等地的加密政策修法,這將決定未來十年全球網路隱私的走向。
    • 動手做實驗: 像那位拆解特斯拉電腦的極客一樣,不要只滿足於軟體介面,深入了解你每天使用的硬體與底層技術,是保持競爭力的不二法門。
  • 為什麼 Claude 不遵守 Superpowers 的鐵律?理論與實務的落差分析

    重點摘要

    • Superpowers skills 有嚴格的鐵律,但實務上 Claude 常常無視它們
    • 根本原因:skills 是文字指令,沒有程式化的強制機制
    • 五個主要原因:skill 未載入、context 被壓縮、訓練行為覆蓋、無狀態計數、Haiku 能力不足
    • 每條規則附上 GitHub 原始碼出處,讓你自己驗證

    上一篇文章整理了 github.com/obra/superpowers 裡所有 skill 的隱藏規則。這篇要回答一個更根本的問題:這些規則白紙黑字寫得清清楚楚,為什麼 Claude 實務上常常不遵守?

    Skill 的規則是真實存在的

    先確認這些規則不是我們誤解或誤記的。以下是幾條最關鍵規則的原始出處:

    鐵律 1:修了三次還沒修好,停下來

    來源:skills/systematic-debugging/SKILL.md 第 195-197 行

    If < 3: Return to Phase 1, re-analyze with new information
    If ≥ 3: STOP and question the architecture (step 5 below)
    DON’T attempt Fix #4 without architectural discussion

    同一個檔案第 227 行:

    “One more fix attempt” (when already tried 2+)
    ALL of these mean: STOP. Return to Phase 1.

    鐵律 2:選最便宜的 model

    來源:skills/subagent-driven-development/SKILL.md 第 89-91 行

    Use the least powerful model that can handle each role to conserve cost and increase speed.
    Mechanical implementation tasks (isolated functions, clear specs, 1-2 files): use a fast, cheap model. Most implementation tasks are mechanical when the plan is well-specified.

    鐵律 3:沒有失敗測試不能寫 code

    來源:skills/test-driven-development/SKILL.md 第 31-34 行

    ## The Iron Law

    NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST

    鐵律 4:1% 機率就必須 invoke skill

    來源:skills/using-superpowers/SKILL.md 第 11-15 行

    If you think there is even a 1% chance a skill might apply to what you are doing, you ABSOLUTELY MUST invoke the skill.

    IF A SKILL APPLIES TO YOUR TASK, YOU DO NOT HAVE A CHOICE. YOU MUST USE IT.

    This is not negotiable. This is not optional. You cannot rationalize your way out of this.

    鐵律 5:宣稱完成前必須這個訊息內跑過驗證

    來源:skills/verification-before-completion/SKILL.md 第 16-22 行

    ## The Iron Law

    If you haven’t run the verification command in this message, you cannot claim it passes.

    規則是真實的,寫得清楚,甚至用了「Iron Law」、「ABSOLUTELY MUST」、「NOT negotiable」這些最強烈的措辭。

    那為什麼實務上 Claude 修了四次五次還在猜?為什麼還是會選 Haiku?為什麼不跑完驗證就說「完成了」?

    五個根本原因

    原因 1:Skill 是文字,沒有執行引擎

    Skills 的運作方式是:Claude 呼叫 Skill 工具 → 工具把 SKILL.md 的內容貼進 context → Claude 讀完然後「盡力遵守」。

    沒有 interpreter,沒有 runtime,沒有 if-then 邏輯,沒有計數器,沒有警報。Claude 違反規則不會有任何程式層面的後果。

    這就是為什麼 using-superpowers/SKILL.md 第 13 行要用大寫強調「YOU DO NOT HAVE A CHOICE」—— 因為它知道 Claude 有選擇,它在試圖用措辭彌補缺乏強制力這件事。

    原因 2:Skill 沒被載入就不存在

    using-superpowers skill 規定「每次對話開始前先查 skill」,但執行這條規則的還是 Claude 本身。如果 Claude 這次忘了 invoke Skill 工具,systematic-debugging 的三次上限根本不在 context 裡,等於不存在。

    這是一個自我參照的問題:「用 skill 確保 skill 被用」的規則本身也依賴 skill 被正確載入才能生效。

    原因 3:Context 壓縮吃掉規則

    長對話之後,Claude Code 會自動壓縮前面的 context。Skill 內容被載入進 context,但在第五次修 bug 的時候,第一次載入的 systematic-debugging 很可能已經被壓縮到只剩摘要,「三次停下來」這條規則不見了。

    Claude 不是故意無視規則,是規則已經不在它的視野裡了。

    原因 4:RLHF 訓練行為覆蓋 skill 規則

    Claude 的訓練讓它傾向:

    • 持續幫忙(不輕易放棄)
    • 樂觀估計(「這次應該可以」)
    • 不讓使用者失望(說「我不知道怎麼修」感覺像失敗)

    Skill 說「第三次停下來討論架構」,但訓練說「繼續幫用戶解決問題」。訓練是骨子裡的,skill 是貼上去的文字。骨子裡的贏。

    這個矛盾在 skill 作者知道:systematic-debugging/SKILL.md 第 195 行不只說「stop」,還用粗體、全大寫,以及「DON’T attempt Fix #4」的雙重否定,都是試圖用措辭強度來對抗訓練行為。

    原因 5:Haiku 沒有能力執行複雜的工作流程規則

    來源:skills/subagent-driven-development/SKILL.md 第 89-100 行告訴 Claude 選最便宜的 model。

    但兩階段 review(spec compliance → code quality)、四種 status 的不同處理邏輯、三次上限計數、context 打包給 subagent——這些都需要相當的推理能力。Haiku 被選來做「機械性」的工作,卻遇到需要判斷的情況時,它沒有能力忠實執行這些細緻規則,就退回最直覺的行為:繼續做、繼續猜。

    所以 skill 的 model 選擇規則本身,就是讓 skill 的其他規則失效的原因之一。

    一個具體的案例:兩階段 Review 的崩潰

    subagent-driven-development 規定兩階段 review,而且有嚴格順序(SKILL.md 第 247 行):

    Start code quality review before spec compliance is ✅ (wrong order)

    這個規定存在,是因為曾經出問題(skills/writing-skills/SKILL.md 第 154-156 行):

    Testing revealed that when a description summarizes the skill’s workflow, Claude may follow the description instead of reading the full skill content. A description saying “code review between tasks” caused Claude to do ONE review, even though the skill’s flowchart clearly showed TWO reviews (spec compliance then code quality).

    換句話說:

    1. Skill 的 description 寫了工作流程摘要
    2. Claude 讀了 description 就走捷徑,沒有讀完整 SKILL.md
    3. 看到「code review between tasks」→ 只做了一次 review
    4. 整個兩階段機制失效

    修法是把 description 改成只描述「什麼時候用這個 skill」,不描述工作流程。但這同時說明:一個措辭上的疏漏,就足以讓整個 skill 的核心機制失效,而且你不會知道。

    另一個案例:Permission 擋住了整個 Review Loop

    我們最近遇到的真實案例:在 ~/.claude/settings.json 裡,PermissionRequest hook 的 matcher 設定為 Agent|TeamCreate|Team,而 hook 的行為是對 Agent 工具直接回傳 deny

    結果:每次 LEAD 要 dispatch subagent(不管是 implementer、spec reviewer 還是 code quality reviewer),都被系統層面攔截拒絕。

    兩階段 review 的規則還在 context 裡,Claude 也打算遵守,但 Agent 工具每次都失敗。Skill 的工作流程完全無從執行。

    修法:把 Agent 從 hook matcher 移除(改成 TeamCreate|Team),並把 defaultMode 改成 bypassPermissions。這個設定層面的問題修好之後,Agent Team 才能真正按 skill 的設計跑。

    Skills 在什麼條件下才有效

    綜合以上,Skills 的效果受以下條件影響:

    條件 有利 不利
    Session 長度新開的 session,context 乾淨長對話,skills 被壓縮
    ModelSonnet / Opus,有推理能力Haiku,複雜 workflow 跟不上
    Permission 設定Agent 工具不被攔截Permission hook 擋住 subagent dispatch
    Skill 載入LEAD 主動 invoke 正確的 skill沒有 invoke,規則不在 context
    文件完整性AGENTS.md 明確指定每個 agent 的 model讓 Claude 自己判斷 model,選 Haiku

    你能做什麼

    Skill 的規則沒辦法完全靠 Claude 自己執行,但有幾件事可以提升遵守率:

    1. 在 CLAUDE.md 加覆寫規則(降低對 skill 的依賴)

    ## Superpowers Skill Override Rules
    
    ### Model Selection
    NEVER use haiku for implementation tasks. Follow:
    - opus: architecture, code review, cross-file reasoning
    - sonnet: all implementation tasks
    - haiku: file scanning, directory listing ONLY
    
    ### Git Branch
    No branch confirmation needed when already on non-main/master branch.
    
    ### Debugging Limit
    After 3 failed fix attempts, STOP and escalate to user.
    Do not attempt Fix #4 without discussion.

    2. 修好 settings.json(讓 Agent 工具不被攔截)

    {
      "permissions": {
        "allow": ["Agent"],
        "defaultMode": "bypassPermissions"
      },
      "feedbackSurveyRate": 0
    }

    3. 在 AGENTS.md 明確指定每個 agent 的 model

    | Agent | Model | Role |
    |-------|-------|------|
    | implementer | sonnet | Feature implementation |
    | spec-reviewer | sonnet | Spec compliance check |
    | code-reviewer | opus | Code quality review |

    4. 永遠從 dev branch 開工

    git checkout -b dev

    避開所有 skill 的 main/master 保護規則,減少一類不必要的確認詢問。

    一個誠實的評估

    Superpowers skill 系統的設計思路是正確的:把軟體開發的最佳實踐(TDD、系統性除錯、分層 review)轉化為 AI agent 的工作協議。規則本身大多有充分的理由。

    但在現實中,一個純粹依賴文字指令的系統,要對抗 LLM 的訓練行為、context 長度的物理限制、以及工具層的設定問題,注定只能是「盡力遵守」而非「強制執行」。

    理解這個落差,比假設 Claude 完全遵守更有用。知道規則存在、知道為什麼可能失效,讓你能夠在關鍵節點主動介入,而不是事後才發現 Claude 在第五次猜測同一個 bug。


    延伸閱讀:

  • Claude Code Superpowers 隱藏規則:4 個你不知道的坑與避開方法

    重點摘要

    • Superpowers skills 有幾條「隱藏規則」會在你不注意時改變 Claude 的行為
    • 最常見的問題:skill 自動選 Haiku 做本該 Sonnet/Opus 的工作
    • 三個簡單設定可以避開 90% 的干擾:dev branch、AGENTS.md 指定 model、關閉 feedback survey
    • 文末附 CLAUDE.md 覆寫規則範本,直接貼進去就能用

    用 Claude Code 跑 Agent Team 一段時間後,你可能會發現:明明設定都正確,Claude 還是會跑來問奇怪的問題、用錯 model、或在不該停的地方停下來。這篇文章整理了 Superpowers plugin 裡幾條「你不一定知道」的隱藏規則,以及最簡單的避開方法。

    為什麼 Agent Team 會「自己做決定」?

    Claude Code 的 Superpowers plugin 提供了一套 skill 系統,讓 Claude 在執行任務時有明確的工作流程可依循。這些 skill 設計上是給通用場景用的,因此內建了一些「省成本、保安全」的預設行為。

    問題是:這些預設行為有時候和你的需求相衝突,而且你很難從文件上看出來。

    隱藏規則 1:Skill 會自動選最便宜的 Model

    來源: subagent-driven-development skill

    這條規則直接寫在 skill 裡:

    “Use the least powerful model that can handle each role to conserve cost and increase speed. Mechanical implementation tasks: use a fast, cheap model.”

    也就是說,當 Claude 判斷一個 task 是「機械性的」(1-2 個檔案、規格清楚),它就會選 Haiku。這對一般的 CRUD 功能可能沒問題,但對複雜業務邏輯(像 iDempiere plugin、Taiwan 發票系統)來說,Haiku 根本不夠用。

    Skill 的 Model 選擇邏輯

    Task 類型 Skill 的選擇 適合複雜專案?
    Mechanical(1-2 files, clear spec)Haiku(最便宜)❌ 通常不夠用
    Integration(多 file、需判斷)Sonnet(標準)✅ 通常足夠
    Architecture / ReviewOpus(最強)✅ 正確

    避開方法:在 AGENTS.md 明確指定 Model

    最簡單的解法是在每個專案的 AGENTS.md 裡明確寫好每個 agent 的 model。Claude 在 dispatch subagent 時,如果 AGENTS.md 有寫,就應該照抄,不用自己判斷。

    | Agent | Model | Role |
    |-------|-------|------|
    | pack-builder | sonnet | Build 2Pack XML + ZIP |
    | model-fixer | sonnet | Fix Model classes |
    | idempiere-expert | opus | Technical reviewer |

    同樣地,在 IMPLEMENTATION_PLAN.md 每個 phase 標上 model,讓 LEAD 照單全收:

    ## Phase 0.1:建立 PackOut.xml [pack-builder / sonnet]
    ## Phase 0.2:Activator 邏輯 [activator-fixer / sonnet]
    ## Review Gate [idempiere-expert / opus]

    隱藏規則 2:在 main/master 上工作會被詢問確認

    來源: 幾乎所有 implementation skill

    所有 skill 都有這條規則:“Never start implementation on main/master without explicit user consent.” 只要你的 working branch 叫 mainmaster,Claude 每次開始實作前都會停下來問你確認。

    避開方法:永遠從 dev branch 開工

    一條指令解決:

    git checkout -b dev

    在 dev branch 上,這條保護規則不會觸發。這也是好的 git 習慣,順手為之即可。

    隱藏規則 3:Subagent 不繼承你的對話 Context

    來源: 所有使用 subagent 的 skill

    這是設計上的特性,不是 bug:每個 subagent 都是 fresh 啟動,完全不知道你之前跟 LEAD 說過什麼。LEAD 要把所有需要的 context 手動打包給 subagent。

    這代表你告訴 LEAD「不要動 X 檔案」、「記得用 Y 方式」,subagent 不一定會知道,除非 LEAD 把這些資訊包進去。

    避開方法:把規則寫進文件,不要只說給 LEAD 聽

    任何重要的約定,放進 AGENTS.mdCLAUDE.mdIMPLEMENTATION_PLAN.md。這些檔案 subagent 啟動時會讀到,不依賴 LEAD 的記憶。

    隱藏規則 4:Feedback Survey 會擋住畫面

    Claude Code 會隨機彈出「How is Claude doing this session?」的評分畫面。這個畫面不是 blocking prompt,按 Enter 沒有用,要按 0 才能 dismiss。

    更乾脆的做法是直接關閉它。在 ~/.claude/settings.json 加一行:

    {
      "feedbackSurveyRate": 0
    }

    設為 0 後,這個 survey 就永遠不會出現了。

    完整 CLAUDE.md 覆寫規則範本

    把以下內容加進你的 CLAUDE.md,可以覆寫 skill 的預設行為:

    ## Superpowers Skill Override Rules
    
    ### Model Selection (OVERRIDES subagent-driven-development skill)
    NEVER use haiku for implementation tasks. Follow this mapping regardless
    of what the skill says about "mechanical" tasks:
    - opus: architecture decisions, code review, cross-file reasoning
    - sonnet: all implementation tasks (CRUD, API, forms, tests, services)
    - haiku: file scanning, directory listing, config comparison ONLY
    
    When dispatching subagents, ALWAYS check AGENTS.md for the assigned model.
    If AGENTS.md specifies a model, use it. Do not override with cheaper model.
    
    ### Git Branch
    Never ask for branch confirmation if already on a non-main/master branch.
    Treat any branch that is not named "main" or "master" as pre-approved for
    implementation work.

    一次性設定清單

    以下是只需要做一次的設定,之後每個專案都受益:

    設定 位置 效果
    "feedbackSurveyRate": 0~/.claude/settings.json永久關閉 feedback survey
    "defaultMode": "bypassPermissions"~/.claude/settings.json工具不再逐一詢問確認
    Model 覆寫規則~/.claude/CLAUDE.md強制 sonnet 做實作工作

    以下是每個新專案開始時的習慣:

    1. git checkout -b dev(避開 main/master 保護規則)
    2. 建立 AGENTS.md 並寫好每個 agent 的 model
    3. IMPLEMENTATION_PLAN.md 每個 phase 標記 model

    為什麼這些規則存在?

    這些 skill 是為「通用場景」設計的,預設行為是合理的:

    • 省成本: 對大多數用戶來說,Haiku 做簡單任務確實夠用,費用少一半
    • 保安全: 不讓 Claude 在 main branch 亂動是好習慣
    • 隔離 context: Fresh subagent 避免舊 context 污染新任務

    問題不是規則本身,而是當你的專案比「通用場景」複雜時,這些預設值就不夠精準了。透過明確的文件(AGENTS.md、CLAUDE.md)和一次性的 settings.json 設定,你可以讓 Claude 按照你的規格工作,而不是照 skill 的預設值工作。


    延伸閱讀:

  • Agent Team 穩定的關鍵:spawn 之前先建好兩份文件

    重點摘要

    • Agent Team 不穩定的根本原因:agent 靠對話記憶工作,不靠文件
    • 解法:spawn agents 之前,必須先建好 CLAUDE.md 和 AGENTS.md
    • CLAUDE.md 寫專案現況,AGENTS.md 寫團隊規則,缺一不可
    • Agent 之間的工作交接必須透過實體檔案,不能靠口頭傳遞

    同樣是用 Claude Code 跑 Agent Team,有人的 team 順暢完成、互動極少,有人的 team 一直卡住、不斷要人介入。

    差距不在任務複雜度,不在模型,在一件事:有沒有在 spawn agents 之前先建好文件。

    為什麼 Agent Team 會卡住

    要理解這個問題,先理解 agent 的本質。

    當你 spawn 一個 agent,它只知道你在那一刻的 prompt 裡說了什麼。你的對話歷史、你之前說的規則、其他 agent 做了什麼——它全都不知道。

    這代表什麼?

    如果你沒有把規則寫進文件,你(orchestrator)就是整個 team 唯一的記憶體。你要記住所有規則,要在每個 spawn prompt 裡正確地說一遍,要把 agent A 的結果正確地轉述給 agent B。

    這就是卡住的來源:

    • Prompt 寫漏一條規則 → agent 做出不符合期望的結果
    • Agent A 的結果透過我轉述給 Agent B → 轉述過程中遺漏細節
    • 對話太長,舊的 context 被壓縮 → 規則消失
    • 某個 agent 失敗重啟 → 什麼都不記得,要重頭說
    • 多個 agent 平行跑 → 每個人收到的規則說法略有不同

    每一個都是可能的失敗點。越複雜的 team,失敗的機率越高。

    讓 Team 穩定的解法:兩份文件

    穩定的 Agent Team 做一件事:把所有 agent 需要知道的東西,從對話記憶移到磁碟上的文件。

    文件是客觀存在的。所有 agent 讀同一份,規則永遠一致。Agent 失敗重啟,讀一遍文件就恢復。多個 agent 平行跑,各自讀文件,不需要我轉述。

    需要兩份文件,職責不同:

    文件 寫什麼 類比
    CLAUDE.md專案現況:ID、路徑、已完成的項目、已知問題、版本新人入職的專案交接文件
    AGENTS.md團隊規則:誰做什麼、如何交接、鐵律、失敗怎麼處理公司的工作手冊

    CLAUDE.md 要寫什麼

    寫所有「agent 每次都要重新查,但其實不需要查」的東西:

    # 專案名稱
    
    ## 關鍵 ID 和路徑
    - WordPress 分類 ID:失智照顧=76、家屬心聲=78
    - 已發布文章:[Post ID 清單]
    - 輸出目錄:./content/drafts/
    
    ## 技術環境
    - 使用版本:XXX
    - API endpoint:[測試] / [正式]
    
    ## 已知的坑
    - 台灣失智症協會網站用 http:// 不支援 https
    - 這個 API 的日期格式是 YYYYMMDD 不是 ISO 8601
    
    ## 已完成 / 待處理
    - ✅ 已完成:XXX
    - ⚠️ 待處理:YYY

    沒有 CLAUDE.md,agent 每次都要重新查分類 ID、確認文章有沒有發過、試連結通不通。每一步都是潛在的失敗點。

    AGENTS.md 要寫什麼

    AGENTS.md 有四個必要部分:

    1. 鐵律(所有 agent 不可違反)

    ## 鐵律
    - 所有醫療資訊必須附上可驗證的來源 URL
    - 不能修改 iDempiere core 代碼,只能在 plugin 層擴充
    - 沒有測試的代碼不算完成

    2. 每個 agent 的定義

    ### researcher(研究員)
    職責:搜尋資料,每筆資訊必須記錄來源 URL
    工具:WebSearch, WebFetch, Read
    輸出格式:[明確定義]

    3. 交接協議(最關鍵、最常被忽略)

    ## 交接協議
    researcher 完成 → 存到 ./drafts/research-[topic]-[YYYYMMDD].md
    writer 開始前  → 必須讀取上面那個檔案
    writer 完成   → 存到 ./drafts/article-[topic]-[YYYYMMDD].md
    publisher 開始前 → 讀取 article 檔案,驗證 References 後才發布

    4. 失敗處理規則

    ## 失敗處理
    - 找不到可信來源:停止,回報「無法找到符合鐵律的來源」,等待指示
    - 需求不清楚:列出疑問,等確認後再開始,不要自行假設
    - 任何 agent:遇到不確定的事,停下來問,不要猜

    具體案例:iDempiere 台灣統一發票 Plugin

    用一個真實場景說明這套做法。假設要用 Agent Team 開發一個 iDempiere 的台灣統一發票 plugin,team 成員是 PM、RD、架構師。

    CLAUDE.md(專案現況)

    # iDempiere 統一發票 Plugin
    
    ## 技術環境
    - iDempiere 版本:11.0,Java 17
    - Plugin 目錄:/path/to/plugin
    - 財政部電子發票規格書版本:5.0(2025年)
    
    ## 已知 iDempiere 約束
    - Callout 用 @Callout annotation(見 idempiere-callout-generator skill)
    - 不能用 Spring,只能用 OSGi service
    - DB 操作只能透過 PO 或 DB class,不能直接 JDBC
    
    ## 已完成的模組
    - ✅ 發票開立
    - ⚠️ 作廢(待測試)
    - ❌ 查詢(未開始)

    AGENTS.md 的交接協議

    ## 新功能開發流程
    
    pm 寫需求 → 存到 ./specs/req-[feature].md
        ↓
    architect 設計 → 讀 req 檔案 → 存到 ./specs/arch-[feature].md
        ↓
    rd 實作 → 讀 req + arch 檔案 → 代碼 + ./specs/impl-[feature].md
        ↓
    architect review → 讀 impl 摘要 → 存到 ./reviews/review-[feature].md

    每個 agent 知道自己要讀什麼、存到哪裡。PM 和 RD 不需要「對話」,他們透過檔案交接。Architect 不需要等 PM 說完才知道需求,直接讀需求檔案。

    每次 spawn agent 的啟動句

    請先閱讀 CLAUDE.md 和 AGENTS.md,
    確認你的角色、鐵律和交接路徑後再開始工作。

    這一句話讓 agent 在開始工作前自己去讀規則,不需要我每次重複說一遍。

    不穩定 vs 穩定:對照表

    沒有文件(常見做法) 有文件(穩定做法)
    規則從哪來我的 prompt(每次可能不同)AGENTS.md(永遠一致)
    專案狀態我的記憶(可能過時或漏掉)CLAUDE.md(客觀存在)
    Agent 間交接我轉述(容易漏細節)實體檔案(完整保留)
    Agent 失敗重啟什麼都不記得讀文件即恢復
    平行跑多個 agent規則可能不一致讀同一份文件,完全一致
    需要人工介入頻繁只在真正需要決策時

    標準流程:每次建立 Agent Team 前

    1. 建立 CLAUDE.md:寫入專案現況(ID、路徑、已知問題、版本、已完成項目)
    2. 建立 AGENTS.md:寫入鐵律、每個 agent 的定義、交接協議、失敗處理規則
    3. Spawn agent 時第一句:「請先閱讀 CLAUDE.md 和 AGENTS.md,確認規則後再開始」
    4. 交接一律用實體檔案:agent 完成後存到指定路徑,下一個 agent 從那裡讀

    沒有做完這四步就開始 spawn,你就是在用對話記憶撐整個 team。任務越複雜,遲早會卡住。

    常見問題

    Q:簡單的任務也需要這兩份文件嗎?

    一個 agent 做一件事,不需要。兩個以上的 agent 有交接,就需要。判斷標準很簡單:如果你需要「把 A 做完的東西交給 B」,就要用文件定義這個交接。

    Q:CLAUDE.md 和 AGENTS.md 要多詳細?

    CLAUDE.md:詳細到「新加入的 agent 不需要問任何問題就能知道專案現況」。AGENTS.md:詳細到「每個 agent 知道自己的輸出要存到哪個路徑」。最常被忽略的就是交接路徑,這是 team 失敗最常見的原因。

    Q:文件要每次重寫嗎?

    AGENTS.md 的團隊結構通常固定,一次寫好後很少改。CLAUDE.md 的專案狀態會變,每次任務完成後更新「已完成項目」。把更新 CLAUDE.md 當作任務完成的一部分,下次 team 啟動時文件就是最新的。

  • 舊系統整合場景下,會用 vs 不會用 Claude Code 的差距

    重點摘要

    • 舊系統整合最大的陷阱:Claude 看不到舊系統,但舊系統的約束決定了新功能能不能用
    • 不會用的人:讓 Claude 在沒有舊系統上下文的情況下做新功能,結果接不上去
    • 會用的人:先讓 Claude 讀關鍵接口,CLAUDE.md 記錄現有約束,設計文件定義接合點
    • 三個場景示範:醫療 HIS 接 LINE 掛號、舊報表接新數據、舊 ERP 接電商平台

    上一篇文章講的是新功能開發。但現實是,大多數人面對的不是一張白紙,而是一個已經在跑的舊系統。

    舊系統整合比全新開發難十倍,不是因為技術更難,而是因為有一大堆隱性約束 Claude 不知道。格式、命名、邊界、不能動的地方,這些都在舊代碼裡,Claude 看不到就無從遵守。

    這篇用三個場景直接模擬:有舊系統在的情況下,會用和不會用 Claude Code 的差距。

    根本差距:Claude 的上下文只有你給它的部分

    Claude Code 非常聰明,但它只知道你告訴它的東西。在全新專案,你說什麼格式就用什麼格式,問題不大。在整合舊系統時,如果你沒有把舊系統的關鍵結構告訴它,它會做出一個邏輯正確但無法接上的東西。

    這不是 Claude 的問題,是你的輸入不完整。

    舊系統整合的核心挑戰是:讓 Claude 在動手之前,先理解它不能動的邊界在哪裡。

    場景一:醫院 HIS 系統加掛 LINE 預約掛號

    情境

    一家地區醫院用了十年的 HIS 系統(Windows Server + MSSQL),所有掛號邏輯都在 stored procedures 裡。現在要加 LINE Chatbot 讓患者能線上預約,但 HIS 系統不能動,只能從外部透過 API 呼叫它。

    不會用的人怎麼做

    幫我寫一個 LINE Chatbot 預約掛號系統,要能讓患者選科別、選醫師、選時段。

    Claude 做出一個完整的 LINE bot,資料庫設計清楚,對話流程順暢。

    開始對接 HIS 系統時,問題一個接一個:

    • HIS 的患者 ID 是 8 位數字,Claude 設計的是 UUID — 整個 primary key 要換
    • HIS 的診次代碼格式是 YYYYMMDD-科別碼-序號(例如 20260325-INT-001),Claude 自己設計了完全不同的格式
    • HIS 只接受 stored procedure 呼叫,不開放直接讀表,Claude 設計的是直接 SELECT
    • 科別代碼是 HIS 裡的維護資料(內科=INT、外科=SUR),Claude 用了自己的命名

    每一個問題單獨看都能修,但修完之後發現下一個格式又不對。最後花了兩天在做格式轉換,而不是做功能。

    結果:開發 3 天,對接 2 天,還有格式轉換層要長期維護。

    會用的人怎麼做

    第一步:先讓 Claude 讀懂舊系統的接口

    把 HIS 系統對外開放的 SP 簽名整理成文件,餵給 Claude:

    請先閱讀以下 HIS 系統的接口文件,
    告訴我你對這個系統的理解,特別是資料格式和呼叫限制,
    再討論 LINE bot 的架構設計。
    
    --- HIS 接口文件 ---
    sp_GetAvailableSessions
      @DeptCode NCHAR(3)      -- 科別碼,參照 tb_Dept.DeptCode
      @DateFrom NCHAR(8)      -- YYYYMMDD 格式
      @DateTo   NCHAR(8)
      回傳: SessionId NCHAR(16), DoctorName, SessionDate, RemainSlots
    
    sp_CreateBooking
      @PatientId NCHAR(8)     -- 8位數字,不足補0
      @SessionId NCHAR(16)    -- 從 sp_GetAvailableSessions 取得
      @Phone     NVARCHAR(20)
      回傳: BookingNo NCHAR(12), Status (SUCCESS/FULL/DUPLICATE)
    
    sp_CancelBooking
      @BookingNo NCHAR(12)
      @Reason    NVARCHAR(100)
    ---

    第二步:CLAUDE.md 記錄現有系統的約束

    ## 整合約束(HIS 系統,不可更動)
    - PatientId:8 位數字字串,不足補 0(例如 "00012345")
    - SessionId:格式為 YYYYMMDD-DeptCode-NNN(例如 "20260325-INT-001")
    - 所有 HIS 呼叫只能透過 stored procedure,不允許直接查表
    - 科別代碼參照 tb_Dept,常用:INT=內科、SUR=外科、PED=小兒科
    
    ## 新系統原則
    - LINE bot 層只做對話邏輯
    - HIS 接口層做格式轉換(HisAdapter class)
    - 不在 LINE bot 層直接呼叫 HIS

    第三步:設計文件定義接合點,不是重新設計格式

    # 功能:LINE 掛號 → HIS 預約對接
    
    ## 接合點(Integration Points)
    LINE 用戶選擇診次 → HisAdapter.getAvailableSessions(deptCode, dateRange)
      → 呼叫 sp_GetAvailableSessions
      → 回傳格式轉為 LINE flex message 可用的結構
    
    用戶確認掛號 → HisAdapter.createBooking(lineUserId, sessionId, phone)
      → 查詢或建立患者檔(PatientId)
      → 呼叫 sp_CreateBooking
      → 處理 FULL / DUPLICATE 回傳狀態
    
    ## 不碰的東西
    - HIS 資料庫 schema 完全不動
    - 所有 PatientId 維持 8 位補零格式
    - SessionId 格式完全繼承 HIS
    
    ## Done When
    - HisAdapter 有完整的 unit test(mock SP 回傳)
    - LINE bot 不包含任何 HIS 格式邏輯(全在 Adapter)

    結果:1.5 天完成,無格式轉換層,後續維護只在 HisAdapter 這一層。

    場景二:舊版股市報表腳本接新即時數據

    情境

    一個量化分析師用了三年的 Python 腳本,每天手動下載 CSV,跑一堆計算,輸出報表。現在要改成自動化:自動爬取數據、自動計算、推播到 Line Notify。但舊腳本的計算邏輯非常複雜(含自訂指標、過去三年調整過的參數),不能改,只能把數據來源換掉。

    不會用的人怎麼做

    幫我把這個股市分析腳本改成自動化,自動抓數據然後推 LINE 通知。

    Claude 看了舊腳本,做了一個新的自動化版本。看起來很整齊,比舊腳本乾淨很多。

    跑了一週,發現結果跟舊版不一樣:

    • 舊腳本的 RSI 計算用的是 Wilder 平滑法,Claude 預設用了 SMA 版本,數值不同
    • 舊腳本對成交量有一個「過去 20 日剔除最高最低各兩天後的平均」的自訂邏輯,新版沒有
    • 舊腳本在除權息日前後有特別處理,新版沒有
    • 欄位名稱被 Claude 重新命名了(舊版 vol_adj 在新版變成 adjusted_volume),下游所有 Excel 公式全壞

    分析師說:「它幫我重寫了,但重寫出來的東西跟我的不一樣,我現在不知道以哪個為準。」

    結果:新舊結果不一致,花了一週在驗算差異,反而比手動下載多花時間。

    會用的人怎麼做

    關鍵原則:計算邏輯一行都不能動,只換數據來源。

    第一步:讓 Claude 讀懂舊腳本,先提取它的業務邏輯

    請閱讀這個舊腳本,告訴我:
    1. 它依賴哪些輸入欄位(欄位名稱和格式)
    2. 有哪些自訂計算邏輯(非標準指標)
    3. 最後輸出哪些欄位
    
    不要修改任何東西,只告訴我你的理解。

    這一步讓 Claude 自己找出所有隱性依賴,避免後面遺漏。

    第二步:CLAUDE.md 把舊腳本的約束凍結

    ## 舊系統相容性約束(不可更動)
    - 所有輸出欄位名稱必須與 legacy_report.py 完全一致
      (vol_adj、rsi_wilder、price_adj 等,不得重新命名)
    - RSI 計算必須使用 Wilder 平滑法(非 SMA),與舊版等價
    - 成交量平均:去掉最高最低各 2 天後的 16 日均量
    - 除權息調整邏輯:參見 legacy_report.py 第 87-134 行,禁止修改
    
    ## 本次修改範圍
    - 只修改數據來源層(DataFetcher class)
    - 所有計算邏輯保持不變
    - 輸出格式和欄位名稱保持不變

    第三步:設計文件只描述「換什麼」,明確說「不換什麼」

    # 功能:數據來源自動化
    
    ## 要換的
    舊:手動下載 CSV 放在 ./data/ 目錄
    新:DataFetcher 自動從 TWSE API 抓取,存成相同格式的 DataFrame
    
    ## 不換的
    - 所有 calculate_*() 函數:一行都不動
    - 所有欄位名稱
    - 輸出的 Excel 格式和公式
    
    ## 驗證方式
    用同一天的歷史數據,新舊兩版並排執行,
    所有欄位數值差異必須在浮點誤差範圍內(< 0.0001)

    「驗證方式」這一段給了 Claude 一個清楚的完成定義:不是「看起來跑得動」,而是「跟舊版數字一樣」。

    結果:1 天完成,新舊並排驗算通過,分析師信心十足上線。

    場景三:舊訂單系統接新電商平台(Shopee / momo)

    情境

    一家中型品牌商有自己的後台訂單系統(自建,PHP + MySQL,跑了七年),現在要同時開 Shopee 和 momo 的店,訂單要自動回拋到自建系統,庫存要即時同步。自建系統的代碼文件不齊全,但不能大改,只能在外面包一層。

    不會用的人怎麼做

    幫我串接 Shopee 和 momo 的訂單,自動同步到我們自己的系統。

    Claude 寫了一個整合服務,讀 Shopee/momo 的 webhook,轉換格式,打進自建系統的 API。

    測試時發現:

    • 自建系統的商品 SKU 格式是 PRD-XXXXXXXX,但 Shopee 上的 SKU 是當初手動輸入的,有些根本對不上
    • 自建系統的訂單狀態只有 5 種,Shopee 有 12 種,Claude 的對照表做了一半,有幾個狀態沒處理
    • momo 的訂單金額包含平台折扣,自建系統的商品價格是定價,兩邊金額對不上帳
    • 自建系統在創建訂單時會觸發一個庫存扣減的觸發器,Claude 不知道這件事,導致庫存被扣兩次

    最後一個問題最嚴重:庫存被扣兩次,一直到實際出貨才發現。

    結果:開發 4 天,測試又 3 天,上線後還是出現庫存問題。

    會用的人怎麼做

    舊系統不熟,先讓 Claude 幫你挖清楚它的邊界。

    第一步:讓 Claude 讀舊系統,主動找隱性行為

    請閱讀這個訂單系統的代碼,特別注意:
    1. 創建訂單時會觸發哪些副作用(觸發器、事件、其他表的更新)
    2. 庫存扣減在哪裡發生(是 API 層還是資料庫層)
    3. 訂單狀態的完整清單和流轉規則
    
    告訴我所有你發現的隱性行為,不要開始寫任何代碼。

    「隱性行為」這個說法很重要。Claude 在讀舊代碼時會主動找資料庫 trigger、事件監聽器、side effect,這些是最容易被遺漏的整合風險。

    第二步:用 Plan Mode 規劃整合架構,重點在「邊界」

    【背景】
    自建訂單系統(PHP + MySQL),創建訂單時資料庫層會自動扣減庫存(trigger)。
    Shopee 和 momo 各有自己的訂單狀態體系。
    
    【目標】
    Shopee/momo 新訂單自動進到自建系統,庫存只扣一次,狀態對照完整。
    
    【約束條件】
    - 自建系統的 trigger 不能改(沒有完整文件,風險太高)
    - SKU 對照表需要人工確認後再上線(不能用 Claude 猜測)
    - momo 的折扣金額要分開記錄,不能直接入自建系統的商品價
    
    請制定整合架構計劃,
    特別說明如何避免庫存被扣兩次,
    等我確認計劃後再開始實作。

    Plan Mode 裡,Claude 提出了三個方案,推薦的是:新建一個 OrderBridge 服務,只負責格式轉換和狀態對照,在呼叫自建系統 API 之前先把庫存檢查做在 Bridge 層(避免 API 呼叫失敗後 trigger 已執行的問題)。

    第三步:設計文件把 SKU 對照、狀態對照、金額拆分全部定義清楚

    ## SKU 對照策略
    - 對照表存在 sku_mapping 資料表,人工審核後才生效
    - 找不到對照的 SKU:訂單進 pending_review 佇列,不自動處理
    - 禁止猜測或模糊匹配
    
    ## 訂單狀態對照(Shopee → 自建)
    UNPAID        → 不同步(等付款)
    READY_TO_SHIP → processing
    SHIPPED       → shipped
    COMPLETED     → completed
    CANCELLED     → cancelled
    其他狀態      → 記錄 log,不同步,發警報
    
    ## momo 金額處理
    momo_original_price → 自建系統 unit_price
    momo_discount       → 另存 discount_record 資料表
    momo_final_price    → 自建系統 actual_amount
    (三個欄位分開記錄,不做合併計算)

    結果:2 天完成 OrderBridge,庫存零重複扣減,SKU 對照人工審核後上線,穩定運行。

    舊系統整合的核心技巧整理

    技巧 怎麼做 目的
    先讓 Claude 讀現有接口提供 API 簽名、schema、關鍵函數,要求 Claude 說出理解後再動手讓 Claude 在正確的上下文下設計,不做無效假設
    要求 Claude 找隱性行為明確說「找出所有 trigger、side effect、副作用,不要開始寫代碼」在整合前把地雷挖出來,不是做完才踩到
    CLAUDE.md 凍結不能動的邊界在 CLAUDE.md 明確寫「不得修改 X 格式 / Y 欄位名稱 / Z 計算邏輯」Claude 不會因為「更整齊」而擅自重新命名或重構
    設計文件定義接合點,不定義格式設計文件描述「新舊系統在哪裡接觸」,格式繼承舊系統,不重新設計避免做一個格式轉換層長期維護
    驗證方式要對比舊系統Done When 裡明確說「與舊系統同樣輸入,輸出結果必須一致」讓 Claude 自己驗算新舊等價性,不是跑起來就算完成

    一個可以直接用的提示模板

    每次要在舊系統上加新功能,用這個模板啟動:

    【現有系統】
    [描述現有系統的技術棧和關鍵接口,附上接口文件或關鍵代碼片段]
    
    【要加的功能】
    [一句話描述]
    
    【不能動的邊界】
    - [現有系統的格式、欄位名稱、計算邏輯等不能更動的項目]
    - [如果有 DB trigger 或其他隱性行為,也列在這裡]
    
    【請先做這兩件事,再開始設計】
    1. 告訴我你對現有接口的理解(特別是你覺得可能影響整合的部分)
    2. 指出你看到的整合風險(格式衝突、重複操作、狀態對照缺口)
    
    確認理解正確後,我們再討論設計。

    這個模板做了三件事:把舊系統的上下文給 Claude、凍結不能動的邊界、要求 Claude 在動手前先說出它的理解和風險判斷。

    常見問題

    Q:舊系統的代碼很亂,文件也沒有,怎麼辦?

    先讓 Claude 讀代碼,問它「這個系統對外暴露了哪些接口?資料庫有哪些關鍵的 table 和 trigger?」讓 Claude 幫你反向整理出一份接口文件,確認沒有遺漏後再進行整合設計。不要跳過這一步,這是整合成功的前提。

    Q:整合的範圍不確定,不知道要動到哪裡?

    先用 Plan Mode,給 Claude 目標和約束,讓它畫出影響範圍。Plan Mode 的價值在複雜整合場景特別高,因為它會列出所有相關的文件和狀態流,讓你確認範圍是否正確,再決定怎麼切入。

    Q:新功能做完,但舊系統有些邏輯說不清楚,怕 Claude 做錯?

    這種情況用「新舊並排驗算」:先在測試環境讓舊版和新版用同樣輸入各跑一次,比較輸出。在設計文件裡明確寫出這個驗證條件,Claude 就會把「新舊等價」列為完成定義的一部分。

    從不會用到會用:舊系統整合的具體升級路徑

    舊系統整合的升級路徑跟全新開發不太一樣:問題不是「我沒說邊界條件」,而是「Claude 根本不知道舊系統長什麼樣」。所以升級的重點是:在動手前把舊系統的關鍵資訊餵給 Claude

    第一步:今天就能做到(一個句子)

    在你的提示前面加上這段:

    我有一個現有系統,新功能必須跟它相容。
    
    現有系統的關鍵格式:
    [把你知道的接口格式、欄位名稱、資料格式貼在這裡]
    
    新功能不能更動上面這些格式。先告訴我你的理解,再開始設計。

    就算你只知道一部分格式,先寫一部分。有比沒有好。

    醫療 HIS 整合:Level 0 → Level 1

    不會用(Level 0) 初步會用(Level 1)
    幫我串接 HIS 系統做 LINE 掛號(見下方)
    我有一個舊的 HIS 系統(不能修改),需要在外面接一個 LINE 掛號 bot。
    
    HIS 現有接口(Stored Procedure):
    sp_GetAvailableSessions(@DeptCode NCHAR(3), @DateFrom NCHAR(8), @DateTo NCHAR(8))
    sp_CreateBooking(@PatientId NCHAR(8), @SessionId NCHAR(16), @Phone NVARCHAR(20))
      回傳 Status: SUCCESS / FULL / DUPLICATE
    
    格式約束(不能改):
    - PatientId 是 8 位數字字串,不足補 0
    - 所有呼叫只能透過 stored procedure,不能直接查表
    
    請先告訴我:
    1. 你對這個 HIS 接口的理解
    2. 你覺得 LINE bot 和 HIS 之間需要什麼樣的轉換層
    
    確認後再討論架構設計。

    舊報表腳本接新數據:Level 0 → Level 1

    我有一個舊的 Python 分析腳本,計算邏輯不能動,只要換掉數據來源(從手動 CSV 改成自動抓取)。
    
    舊腳本的輸入依賴(這些欄位名稱不能改):
    - date (YYYY-MM-DD)
    - open, high, low, close, volume
    - vol_adj(自訂欄位:去掉最高最低各2天的16日均量)
    
    計算邏輯不能動的原因:
    - RSI 用的是 Wilder 平滑法(非標準 SMA),三年來的報告都是這個版本
    - 改了數值會跟歷史記錄不一致
    
    請先告訴我:
    1. 你理解哪些東西不能動
    2. 你打算怎麼讓新舊版本在同樣輸入下輸出一致的結果
    
    確認後再開始實作數據來源層。

    舊 ERP 接電商平台:Level 0 → Level 1

    我有一個舊的訂單系統(PHP + MySQL),要串接 Shopee 的訂單進來。
    舊系統不能大改,只能在外面包一層。
    
    已知的舊系統行為:
    - 創建訂單時,資料庫層有 trigger 會自動扣庫存
    - 訂單狀態:pending / processing / shipped / completed / cancelled(只有這五種)
    
    Shopee 訂單狀態有 12 種,需要做對照。
    
    我擔心的問題:
    - 庫存被扣兩次(trigger + 外面的 API 都扣)
    - 狀態對照不完整,有些 Shopee 狀態沒有對應的舊系統狀態
    
    請先幫我分析這兩個風險,告訴我你的處理方案,再開始設計架構。

    注意最後一段:把你擔心的問題說出來。這讓 Claude 優先處理你知道有風險的地方,而不是從它認為重要的地方開始。

    第二步:下週可以做到(CLAUDE.md 記錄舊系統約束)

    舊系統整合的 CLAUDE.md 要特別加一個「整合約束」區塊:

    # 整合約束(現有系統,不可更動)
    
    ## [系統名稱] 的格式規範
    - [欄位名稱] 的格式:[說明]
    - [接口規範]:[說明]
    - 禁止直接操作資料庫,只能透過 [API / SP / 特定方法]
    
    ## 已知的隱性行為
    - [觸發器 / 事件 / side effect 描述]
    - 注意:[操作 X] 會同時觸發 [Y],不要重複執行
    
    ## 新功能的邊界
    - 只修改 [Adapter / Bridge / 特定模組],不動舊系統
    - 欄位名稱繼承舊系統,不重新命名

    每次發現一個新的隱性行為(觸發器、計算邏輯、格式特例),就加進這個區塊。它會越來越完整,讓你以後的整合工作越來越省力。

    第三步:一個月後的進化

    進化 A:在開始任何整合前,先讓 Claude 找隱性行為

    請閱讀 [現有系統代碼/文件],找出所有:
    1. 資料庫 trigger 和它的作用
    2. 創建/更新/刪除記錄時的 side effect
    3. 對外暴露的接口(API / SP / 事件)和它們的格式
    
    告訴我你找到的所有隱性行為,不要開始設計,先讓我確認。

    這個步驟在整合開始前把地雷挖出來,比做完後踩到省 10 倍時間。

    進化 B:複雜整合用 Plan Mode,重點讓 Claude 畫出影響範圍

    【現有系統】[描述]
    【目標】[一句話]
    【不能動的邊界】[列清楚]
    
    請制定整合計劃,特別標出:
    - 會影響到的現有功能
    - 每個整合點的風險
    - 我需要在上線前人工驗證的項目
    
    等我確認計劃後再開始實作。

    進化 C:設計文件加「新舊等價性驗證」

    舊系統整合的完成定義,永遠要加這一條:

    ## 驗證方式
    用同一份歷史輸入,新舊兩版並排執行,
    [關鍵輸出欄位] 的結果差異必須在 [可接受範圍] 內。
    新版上線前,提供新舊對比報告。
    階段 做什麼 關鍵句
    Level 0直接說要串接什麼
    Level 1(今天)把舊系統格式和約束說清楚「這些格式不能改,告訴我你的理解再開始」
    Level 2(下週)CLAUDE.md 記錄舊系統約束和隱性行為每次整合後補一條「已知隱性行為」
    Level 3(一個月後)先讓 Claude 挖地雷,再 Plan Mode 確認影響範圍「找出所有隱性行為,不要開始設計」
  • 會用 vs 不會用 Claude Code:醫療、股市、電商三場景對比

    重點摘要

    • 差距不在「會不會用 AI」,在「有沒有給 AI 做事的結構」
    • 不會用的人:一句話丟需求 → 結果跑偏 → 多輪修正 → 累積沮喪
    • 會用的人:CLAUDE.md + 設計文件 + 分段驗證 → 第一次就對
    • 三個真實場景(醫療 / 股市 / 電商)示範具體差距

    「Claude Code 對我沒用,我試過,它做出來的東西都不對。」

    這句話我聽過很多次。幾乎每次深入問,問題都不在 Claude,在提問的方式。

    這篇文章用三個真實的軟體開發場景,直接模擬「不會用的人」和「會用的人」各自怎麼做,讓你看清楚差距在哪裡。

    核心差距:有沒有給 AI 做事的結構

    不會用 Claude Code 的人,把它當成「很聰明的搜尋引擎」,問一句,期待完美答案。

    會用的人,把它當成「需要完整簡報才能開工的新進工程師」:你給的資訊越清楚,他做出來的東西越準確。

    差距的本質是:你的輸入有沒有結構

    場景一:醫療預約系統(門診掛號)

    痛點

    診所要做線上掛號,需要處理:醫師排班、假日停診、同時段人數上限、患者重複掛號防止。每個規則都有例外,例外裡還有例外。

    不會用的人怎麼做

    直接開口:

    幫我寫一個門診預約系統的 API,要能掛號、取消、查詢。

    Claude 給了一個乾淨的 REST API,CRUD 完整,code 看起來不錯。

    開始整合後發現問題一個接一個:

    • 沒處理同時段上限(診所每診次只收 20 人,API 沒有 capacity 邏輯)
    • 沒有重複掛號檢查(同一個患者可以掛同一診次兩次)
    • 沒有假日判斷(系統不知道什麼是醫院休診日)
    • 時區沒處理(台灣 UTC+8,stored as UTC,前端顯示全錯)

    開始補需求:「喔對,要加上每診次人數上限」→ Claude 修改。「還有重複掛號要擋掉」→ Claude 再改。「假日要停診」→ 再改。每次修改都可能動到之前改好的邏輯,三輪之後代碼開始難以追蹤。

    結果:8 小時,仍有未發現的 bug,信心不足。

    會用的人怎麼做

    第一步:CLAUDE.md 裡早就寫好領域規則

    ## 醫療領域規則
    - 所有時間以 UTC+8 儲存,API 回應包含 timezone 欄位
    - 患者 ID 採用身分證字號格式,需驗證格式(字母+9位數字)
    - 診次 (session) 為最小預約單位,每診次有 capacity 上限
    - 相同患者在同一診次只能有一筆有效預約
    
    ## 錯誤處理標準
    - 業務邏輯錯誤:HTTP 422 + { code, message, field }
    - 驗證錯誤:HTTP 400 + 同上格式

    第二步:先寫設計文件(20 分鐘)

    # 功能:門診掛號 API
    
    ## 輸入
    { patientId: string, sessionId: string }
    
    ## 驗證順序(按此順序,遇錯即停)
    1. patientId 格式是否合法
    2. sessionId 是否存在且為未來時間
    3. 該診次是否已達 capacity
    4. 該患者是否已有此診次的有效預約
    
    ## 邊界條件
    - capacity 滿:422,code: SESSION_FULL
    - 重複掛號:422,code: DUPLICATE_BOOKING
    - 患者格式錯:400,code: INVALID_PATIENT_ID
    - 診次已過去:422,code: SESSION_EXPIRED
    
    ## Done When
    - 所有邊界條件有單元測試
    - integration test 驗證完整掛號流程
    - 0 TypeScript errors

    第三步:分段給任務

    請先閱讀 CLAUDE.md 和這份設計文件。
    閱讀完告訴我你的理解,特別是驗證順序的邏輯,再開始實作。
    先只實作 validateBooking() 函數和對應單元測試,不要動 API 層。

    確認驗證邏輯正確後,再說「現在依照這個驗證函數實作 POST /bookings endpoint」。

    結果:2.5 小時,第一次就通過所有測試,零修改。

    場景二:股市回測系統

    痛點

    想驗證一個交易策略:當 5 日均線上穿 20 日均線時買進,死亡交叉時賣出。聽起來簡單,但台股有漲跌停、只能在交易日交易、最小交易單位是 1000 股、手續費和證交稅要扣掉,每個細節都會影響回測結果。

    不會用的人怎麼做

    幫我寫一個台股回測程式,策略是 5 日均線上穿 20 日均線買進,死叉賣出。

    Claude 給了一個 Python 回測,用 pandas 計算均線,邏輯清楚。跑了一下,報酬率看起來很漂亮。

    仔細看才發現:

    • 買賣都用收盤價,但實際上收盤價買不到(要用隔日開盤)
    • 沒有漲跌停限制,假設任何價格都能成交
    • 沒扣手續費(0.1425%)和證交稅(0.3%)
    • 沒有最小交易單位,0.1 張也買
    • 跑到非交易日(週末)的資料也在交易

    這些問題加起來,讓回測結果虛報了約 30–40%。一個看起來賺錢的策略,修正後可能是虧損的。

    結果:6 小時修修改改,最後不確定結果是否可信。

    會用的人怎麼做

    設計文件把所有台股規則先寫清楚:

    # 功能:台股均線策略回測引擎
    
    ## 市場規則(必須完整實作)
    - 交易日:排除週末 + 台灣國定假日(用 holidays-tw 套件)
    - 成交價:訊號發生在收盤,執行在「隔一個交易日開盤價」
    - 漲跌停:每日最大漲跌幅 ±10%,超過範圍無法成交,標記 LIMIT_HIT
    - 最小交易單位:1000 股,不足整張無法下單
    - 手續費:買賣各 0.1425%(可設定),最低 20 元
    - 證交稅:賣出 0.3%(ETF 為 0.1%)
    
    ## 回測輸出格式
    {
      total_return_pct: float,    # 扣除所有費用後
      trades: [{ date, action, price, shares, cost, tax, net_pnl }],
      unfilled: [{ date, reason }],   # LIMIT_HIT 等無法成交紀錄
      sharpe_ratio: float,
      max_drawdown_pct: float
    }
    
    ## 邊界條件
    - 漲停無法賣出(LIMIT_HIT_SELL)
    - 跌停無法買入(LIMIT_HIT_BUY)
    - 資金不足一張:跳過,記錄 INSUFFICIENT_FUNDS
    - 最後持倉:回測結束日強制以收盤價平倉

    提示方式:

    請先閱讀這份設計文件,告訴我:
    1. 成交時間點的邏輯(訊號日 vs 執行日)你的理解
    2. 漲跌停時的處理流程
    
    確認理解正確後,先只實作 TaiwanMarketRules 類別和測試,
    包含漲跌停判斷、交易日判斷、手續費計算三個方法。
    不要實作策略邏輯或回測引擎。

    結果:3 小時,回測結果可信,且有完整的無法成交紀錄供分析。

    場景三:電商金流串接(綠界 ECPay)

    痛點

    要串接台灣最常用的金流服務商,支援信用卡一次付清 + ATM 虛擬帳號。牽涉到加密簽章驗證、非同步回調(ReturnURL / OrderResultURL)、以及各種付款失敗情境。

    不會用的人怎麼做

    幫我串接綠界金流,支援信用卡和 ATM 付款。

    Claude 寫了一個串接,看起來完整。測試環境跑起來。上線後三天,開始收到客訴:

    • 付款成功但訂單沒更新(沒有正確處理綠界的非同步通知)
    • ATM 超時未付款,訂單卡在「待付款」沒有自動取消
    • CheckMacValue 驗簽偶發失敗(特殊字元的 URL encode 方式不對)
    • 退款流程完全沒實作(以為付款和退款是同一組 API)

    每個問題單獨看都能修,但修一個又會影響另一個。金流 bug 是最難測試的,因為要模擬真實付款行為。

    結果:上線後 bug,緊急修補兩天,損失用戶信任。

    會用的人怎麼做

    先用 Plan Mode 讓 Claude 梳理所有狀態流:

    【背景】
    我要串接綠界 ECPay,使用 Node.js + PostgreSQL,
    CLAUDE.md 中規定所有金流操作要記 audit log。
    
    【目標】
    支援信用卡一次付清和 ATM 虛擬帳號,訂單狀態要與付款狀態完全同步。
    
    【約束條件】
    - 不能有「已付款但訂單未更新」的狀態
    - ATM 超過 3 天未付款自動取消訂單並釋放庫存
    - CheckMacValue 驗簽失敗必須記錄並告警
    
    請制定實作計劃,列出所有需要處理的狀態流轉,
    等我確認計劃後再開始實作。

    Claude 在計劃中列出了 14 個狀態節點,包含「非同步通知重複到達」、「驗簽失敗的處理」這些不明顯的情境。確認計劃後再開始做。

    設計文件特別標明的邊界條件:

    ## 邊界條件(金流特有)
    - ReturnURL(非同步)和 OrderResultURL(同步)可能「都」被呼叫
      → 用 idempotency key 確保同一筆交易只更新一次訂單
    - CheckMacValue 驗簽:URL encode 順序必須按綠界規範(非標準 encodeURIComponent)
      → 測試案例需包含含特殊字元的 MerchantTradeNo
    - ATM 逾期:用 pg-cron 排程,不要依賴前端觸發
    - 退款:獨立 API,與付款 API 完全分離,需要人工審核 flag

    結果:4 小時,上線零 bug,audit log 完整。

    差距總結

    面向 不會用的人 會用的人
    提示方式「幫我做 X」「背景 + 目標 + 約束條件 + 邊界條件」
    領域規則每次用到才補說寫進 CLAUDE.md,一次設定永久生效
    邊界條件做完後才發現沒處理設計文件裡最前面就列清楚
    複雜任務一次丟,結果跑偏再修Plan Mode 確認方向,分段執行
    驗證方式全部做完再整體測試每段完成後先確認,錯誤早期發現
    對話輪數8–15 輪2–4 輪
    結果品質能跑,但邊界不完整完整,測試覆蓋所有邊界

    為什麼大多數人卡在「不會用」

    不是因為懶,是因為直覺錯了。

    我們習慣搜尋引擎:問一句,得到答案。Claude Code 不是搜尋引擎,它是一個需要簡報才能開工的工程師。你給的資訊越完整,它做出來的東西越準確。

    轉換這個直覺需要時間,但一旦轉過來,你就不會想回頭了。

    從下一個任務開始,試著在開口前先回答三個問題:

    1. 這個功能的輸入和輸出各是什麼格式?
    2. 哪些邊界條件會讓它失敗?
    3. 我怎麼確認它做對了?

    把這三個問題的答案寫下來,你的 Claude Code 使用效果會立刻不一樣。

    常見問題

    Q:每次都要先寫設計文件,不會很花時間嗎?

    一份最小版設計文件 20 分鐘。省下的多輪修改至少 2–4 小時。這筆帳很好算。

    小功能(一個函數、修一個 bug)不需要設計文件,直接做。設計文件是為了「有多個邊界條件、或跨越兩層以上架構」的任務。

    Q:CLAUDE.md 不是每個專案都要重寫一遍嗎?

    寫一次,維護成本低。每次發現 Claude 做了一個你不喜歡的決定,把那條規則加進 CLAUDE.md,下次就不會再發生。它是越用越省力的資產。

    Q:我的需求很複雜,文件要寫到多詳細?

    詳細到「可以讓另一個工程師依照這份文件實作,不需要問你問題」。如果你寫完文件還是覺得有地方模糊,那就是還有沒想清楚的需求,先想清楚再開始比較省事。

    從不會用到會用:各情境的具體升級路徑

    看完上面三個場景,你可能知道差距在哪,但還不知道今天要改什麼。這一節給你一條明確的路,從第一步到進階,每步都有可以直接複製的東西。

    第一步:今天就能做到(不需要任何準備)

    在你現在的任何提示最後加上這一句:

    先告訴我你的理解和假設,再開始實作。

    就這一句。它會讓 Claude 在動手前先說出它的假設,你可以在它寫出一堆錯誤代碼前糾正它。成本 0,效果立竿見影。

    三個情境的具體升級示範:

    醫療場景:Level 0 → Level 1

    不會用(Level 0) 初步會用(Level 1)
    幫我寫門診預約系統(見下方)
    幫我寫門診掛號的 API。
    
    輸入:{ patientId: string, sessionId: string, phone: string }
    輸出:{ bookingNo: string, status: "SUCCESS" | "FULL" | "DUPLICATE" }
    
    必須處理:
    - 同一診次超過容量上限 → 回傳 FULL
    - 同一患者重複掛同一診次 → 回傳 DUPLICATE
    - 所有時間以 UTC+8 處理
    
    先告訴我你的理解,特別是這三種情況你打算怎麼處理,再開始實作。

    改變了什麼:加了輸入輸出格式、三個邊界條件、確認步驟。這 5 分鐘的準備,省掉事後發現「沒處理重複掛號」的 2 小時修改。

    股市場景:Level 0 → Level 1

    幫我寫台股均線回測。策略:5日均線上穿20日均線買進,死叉賣出。
    
    台股特有規則(全部都要實作):
    - 訊號發生在收盤,執行在隔交易日開盤價(不是同日收盤)
    - 漲跌停 ±10%,超過無法成交,記錄 LIMIT_HIT
    - 手續費:買賣各 0.1425%,最低 20 元
    - 證交稅:賣出 0.3%
    - 最小交易單位 1000 股,不足不下單
    
    先告訴我這些規則你的理解,特別是成交時間點的邏輯,再開始寫。

    電商金流場景:Level 0 → Level 1

    幫我串接綠界 ECPay,支援信用卡一次付清。
    
    必須處理的情況:
    - ReturnURL(非同步)和 OrderResultURL(同步)可能都會被呼叫,
      同一筆交易只能更新訂單一次(要有 idempotency 機制)
    - CheckMacValue 驗簽失敗:記 log,不更新訂單,回傳 0|Error
    - 付款成功後訂單狀態必須立刻更新
    
    先告訴我你打算怎麼處理「非同步和同步通知都到達」這個情況,再開始實作。

    第二步:下週可以做到(CLAUDE.md)

    把你的領域規則寫成一個檔案,放在專案根目錄,命名 CLAUDE.md。之後每次對話都不用再重複說這些規則。

    先從最小版開始,15 分鐘寫完:

    # 專案規則
    
    ## Tech Stack
    [填你用的技術]
    
    ## 這個領域的特殊規則
    [把你每次都要補充說明的規則寫在這裡]
    例:
    - 台股交易日排除週末 + 台灣國定假日
    - 醫療系統所有時間 UTC+8
    - 金流驗簽失敗一律記 log 不更新訂單
    
    ## 代碼標準
    [你每次都要糾正的習慣寫在這裡]
    例:
    - 禁止 any 類型
    - 每個函數必須有對應測試
    - API 錯誤格式統一:{ code, message, field }
    
    ## 邊界條件規則
    [這個專案特有的邊界處理方式]

    寫好之後,下次開口前說「請先讀 CLAUDE.md」或什麼都不用說(Claude Code 會自動載入)。

    第三步:一個月後的進化(設計文件 + Plan Mode)

    當你習慣 Level 1 之後,下一步是在動手前多寫一份設計文件。這份文件不是給人看的,是給 Claude 看的,20 分鐘寫完,節省的是事後 4–6 小時的修改。

    設計文件的最小格式:

    # 功能:[名稱]
    
    ## 輸入 / 輸出
    輸入:[JSON 範例]
    輸出:[JSON 範例]
    
    ## 邊界條件(必須處理)
    - [條件 A] → [預期行為]
    - [條件 B] → [預期行為]
    
    ## 完成定義
    - [ ] 所有邊界條件有對應單元測試
    - [ ] TypeScript 0 errors

    有了設計文件之後,任務的啟動提示變成:

    請先閱讀 CLAUDE.md 和 [設計文件名]。
    閱讀完告訴我你的理解,特別是邊界條件部分。
    確認後先只實作 [最小單元],不要動其他層。

    當任務更複雜(跨 5 個以上文件、不可逆的架構改動),改用 Plan Mode 啟動:

    請先制定實作計劃,列出方案選項和取捨,等我確認後再開始執行。
    階段 做什麼 時間投資 節省的來回
    Level 0直接說需求00
    Level 1(今天)加輸入/輸出/邊界 + 確認步驟5 分鐘/次2–4 輪
    Level 2(下週)CLAUDE.md 寫入領域規則15 分鐘(一次性)每次省 1–2 輪
    Level 3(一個月後)設計文件 + Plan Mode20 分鐘/功能第一次就對