0%

本文整理自工程師 Charlie Hills 的部落格文章《Delete your CLAUDE.md》。文章源頭是 Anthropic Claude Code 負責人 Boris Cherny 曾透露:Anthropic 把系統提示砍掉超過 80%,模型反而表現更好(詳見前篇〈狠砍80%系統指令!Claude Code之父揭AI心法〉)。Charlie Hills 把這個反直覺的建議,變成一套自己也真的跑過一遍的具體操作流程。

核心論點:CLAUDE.md 不是越寫越好

大多數人維護 CLAUDE.md 的方式是「加法」——踩到一個坑,就補一條規則;AI 犯了一次錯,就多寫一段叮嚀。半年下來,檔案只會愈疊愈厚,卻很少有人回頭刪東西。

Charlie Hills 的主張很直接:這份文件應該定期整個調整重建,而不是無限累加。主要是三個主要的代價。

文件臃腫的三重代價

  1. 成本 — CLAUDE.md 是全局系統提示,每一行都會在每一次對話中被讀入、被計費,不管這次任務用不用得到。
  2. 衝突 — 規則堆多了難免互相矛盾,模型只能自己「隨便選一條」來遵守,行為變得不可預期。
  3. 注意力衰減 — 對應「Lost in the Middle」這類研究:長上下文中間段落的內容,模型的注意力本來就會下降。塞進 CLAUDE.md 中段的規則,很可能根本沒被認真「看到」。

作者的真實案例

Charlie Hills 攤開自己的 CLAUDE.md:305 行,遠超過 Anthropic 官方建議的 200 行上限(官方原話:「目標控制在 200 行以下,更長的文件會消耗更多上下文,降低遵守率」)。

裡面裝的是六個月份、東拼西湊的修正,還有 195 個 skill——但實際被呼叫過的只有 65 個,等於三分之二是根本沒人用的死代碼。

這不是特例,是每個長期用 Claude Code 的人都會自然走到的狀態。

Read more »

在前端 / 後端開發圈,「用 JWT 做登入驗證」幾乎已經是預設選項,很多教學、範例專案打開就是 jsonwebtoken 直接簽一包塞進 cookie 或 localStorage。但 Stop using JWTs! 這篇 Gist 提出了一個很直接的反對意見:

JWTs should not be used for keeping your user logged in. They are not designed for this purpose, they are not secure, and there is a much better tool which is designed for it: regular cookie sessions.

這篇文章記錄我讀完之後的整理,包含作者的核心論點、對常見反駁的回應,以及我自己在實務上觀察到的紀錄分享。

Read more »

本文整理自 Anthropic Claude Code 負責人 Boris Cherny 的分享。Boris 曾在 Meta 擔任五年首席工程師,著有《Programming TypeScript》一書,是 Claude Code 的創造者。

核心改變:系統提示大幅精簡

2026 年 7 月推出 Opus 5 後,Anthropic 把 Claude Code 的系統提示砍掉超過 80%

這是一個反直覺的決策——業界共識向來是「指令寫得越細越好」,但 Boris 反其道而行。

為什麼可以砍?

「那些指令原本是在修正模型『本來就該會、但當時不會』的行為,Opus 5 已經會了。」

模型能力提升後,過去那些詳細的約束反而成為多餘的負擔。Boris 提到,模型在沒有繁瑣提示的狀態下其實聰明一點

重心轉移:從「寫提示」到「驗證」

Boris 認為,真正該投資的不是提示詞工程,而是驗證——他形容這是「大多數人沒做對的那件最重要的事」。

現代 AI 編碼的技能核心在於:

給 Claude 一個看起來有點太難的任務,再讓它有辦法在過程中自己檢查成果

Read more »

本文整理自 Anthropic 技術成員 Thariq Shihipar 的實戰分享,探討如何有效撰寫 Claude Code Skills。
截至 2026 年 6 月,Anthropic 內部已有「數百個」skill 在活躍使用。

核心概念

Skill 是什麼?

Skill 可以理解成一種「脈絡工程」(context engineering):在對的時機,把對的資訊提供給 AI。

一、為什麼資料夾比單一檔案好用?

問題點

  • 如果把所有東西塞進一個檔案,等於每次都把整本說明書攤在 Claude 面前

解決方案

  • 改用資料夾結構,搭配「漸進揭露」(progressive disclosure)
  • 需要時才讀對應檔案,Claude 才不會被無關內容干擾

官方建議

把詳細 API 文件、範例、腳本等 supporting files 放在 skill 資料夾裡,並由 SKILL.md 說明何時讀取。


二、9 大 Skill 分類

Anthropic 盤點內部 skill 後,發現它們大致聚成 9 種類型。最好的 skill 通常只做一類;想做太多事的會橫跨多類,反而把 agent 搞混。

1. 函式庫與 API 參考

  • 教 Claude 正確使用某個函式庫、CLI 或 SDK
  • 附上常踩的雷

2. 產品驗證

  • 描述怎麼測試、驗證程式碼有沒有真的動
  • Anthropic 說這類對產出品質「可量測的影響最大
  • 值得花一週做到極好

3. 資料抓取與分析

  • 接上你的資料與監控系統
  • 內含抓資料的腳本、儀表板代號

4. 業務流程與團隊自動化

  • 把重複工作流收成一個指令
  • 例如:每日 standup、開票

5. 程式碼鷹架與範本

  • 自動生出新服務、新模組的樣板

6. 程式碼品質與審查

  • 強制團隊的程式風格
  • 協助 code review

7. CI/CD 與部署

  • 顧 PR、跑測試、漸進部署
  • 出問題自動回滾

8. Runbooks(故障排除手冊)

  • 接到一個症狀,走完多工具調查
  • 產出結構化報告

9. 基礎設施操作

  • 執行例行維運
  • 對破壞性動作加上護欄

三、最高價值的「Gotchas(陷阱)」區塊

核心觀念

任何 skill 裡訊號最高的內容,就是 Gotchas 區塊。

什麼是 Gotchas?

  • Claude 使用這個 skill 時最常踩的失敗點
  • 要隨時間累積、持續補充
  • 往往是「文件不會寫、但你踩過坑才知道」的細節

實例(以請假系統為例)

範例一:版本問題

「同一張假單可能改過好幾次,要抓『最後核准』那一版,不是最早送出的那張。」

範例二:術語對應

「業務部口中的『出差』,在人資系統裡叫『公假』,其實是同一件事,只是部門叫法不同。」

範例三:狀態判斷

「系統顯示『已送出』不代表假請成功了,還要看主管那關有沒有按下核准。」

重點

  • 把「平常沒人會特別告訴你、有出錯過而學到」的細節寫下來
  • skill 才會越用越準

四、描述(description)要寫給模型看

關鍵認知

description 欄位不是給人看的摘要,而是「什麼情況下該觸發我」的說明。

運作機制

  • Claude Code 啟動 session 時,會建立一份可用 skill 與 description 的清單
  • 用它判斷「使用者這個請求,有沒有對應的 skill 可以用」

實務技巧

把觸發詞直接寫進描述,命中率會更高

例如:

1
description: 彙整這週完成的工作,產生給主管看的週報。當使用者說「跑週報」「整理這週進度」時觸發。

五、給 Claude 腳本與檔案,讓它「組合」而不是「重造」

核心概念

把可重複的動作寫成腳本交給 Claude,它就能把每一回合花在「決定下一步做什麼」,而不是重打一次樣板。

進階技巧:隨選 hooks(on-demand hooks)

只在這個 skill 被呼叫時才生效

範例 1:/careful

  • 動正式環境時才開
  • 自動擋掉 rm -rfDROP TABLE、force-push 等危險指令

範例 2:/freeze

  • debug 時只准改特定資料夾
  • 避免手滑「修好」無關的程式碼

六、SKILL.md 完整範本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
---
name: weekly-report
description: 彙整這週完成的工作,產生給主管看的週報。當使用者說「跑週報」「整理這週進度」時觸發。
---

# 週報產生器

## 怎麼做

1.`config.json` 取得週報要寄給誰、用什麼格式;若還沒設定,先問使用者。
2. 彙整這週完成的事項,只列「跟上週相比的新進度」,不要把舊的重講一遍。
3. 把這次的週報存進 `reports.log`(只新增、不覆蓋),下次執行時讀自己的歷史,自動判斷哪些是新的。

## Gotchas(踩過的坑)

- 「標記完成」不等於真的結案,有些項目會被退回重做,狀態要看最新的那一版。
- 同一件事這週可能改過好幾次,只取最後定案的版本,別把過程中的草稿都列進去。

## 參考

- 欄位與格式範本見 `references/format.md`(需要時再讀,不必一開始全載入)。

七、動手前要知道的兩個前提

前提一:Skill 不是免費的

  • 一般 session 中,skill 的 description 會進入 context
  • 完整 SKILL.md 則是在使用者或 Claude 呼叫後才載入
  • 每多一個放進專案的 skill,都會增加一點模型需要掃描的脈絡

團隊規模建議

改用 plugin marketplace,讓成員自己挑要裝哪些,而不是全部塞進每個專案。

前提二:別想一次寫到完美

Anthropic 最好用的 skill 多半是從「寥寥幾行字加一個 gotcha」起步

迭代思維

  • 先做一個小的
  • 撞到坑就補一條 gotcha
  • 慢慢變強

核心價值

Skill 的價值不在你寫了多少,而在你有沒有告訴 Claude「它原本不知道、或老是做錯的那件事」。


八、總結與行動建議

撰寫 Skill 的黃金法則

  1. 用資料夾結構,不要單一檔案
  2. 一個 Skill 只做一類事
  3. Gotchas 區塊是最高價值內容
  4. description 要寫觸發詞,給模型看
  5. 提供腳本讓 Claude 組合,不是重造
  6. 從小開始,持續迭代

優先順序

產品驗證類 Skill 對品質影響最大,值得優先投入。


參考資料

這篇文章在談的是 JavaScript 的 Explicit Resource Management,也就是讓程式碼更一致地處理「資源的建立與清理」。

對前端工程師來說,這類問題很常出現在 WebSocket、fetch abort、Streams、IntersectionObserver、檔案處理、計時器或任何需要明確釋放的 API 上。文章的重點不是「控制垃圾回收」,而是讓我們用更一致、可預期的方式,告訴 JavaScript:這個資源什麼時候該收尾。

核心概念

作者把這件事拆成兩層:

  1. Implicit resource management
  2. Explicit resource management

前者是 JavaScript 既有機制,後者是新的語法與協定。

Read more »

在實際工作中,我發現同事在拿到名片後,仍然需要經過一連串人工流程:

  • 手機拍照、或是掃描名片
  • 傳到電腦
  • 手動輸入公司、姓名、電話
  • 還很容易漏資料或輸錯

因此我開始思考:
能不能在不架設伺服器、不導入複雜系統的情況下,把這件事自動化?

這篇文章記錄我實作的一套 名片自動建檔系統,核心完全建立在 Google 生態系上。

系統目標與整體概念

我的目標很單純:

只要用手機拍照上傳名片,就能自動解析內容,並寫入 Google Spreadsheet。

整體流程如下:

1.手機直接拍照,上傳到指定的 Google Drive 資料夾

2.由 Google Apps Script 定期掃描新圖片

3.使用 Gemini AI API 辨識名片內容(非單純 OCR),將結構化資料寫入 Google Spreadsheet

4.將處理完成的名片移動到封存資料夾,避免重複處理

使用到的工具與技術

  • Google Drive:名片圖片的儲存與狀態管理
  • Google Sheets:結構化資料表(輕量資料庫)
  • Google Apps Script:自動化中樞
  • Google Gemini Vision API:影像 + 語意理解

這個組合的優點是:
不需要額外主機
成本低
團隊成員幾乎都能上手
維護成本非常小

資料欄位設計

實際使用時,名片資訊往往比想像中複雜(多支電話、分機、圖示標註),
因此我最後採用以下欄位結構:

  • 狀態
  • 公司
  • 姓名
  • 職稱
  • Email
  • Mobile(手機)
  • Office Phone(公司電話 / 分機)
  • 地址
  • 建檔日期
  • 原始檔案網址

這些欄位會在 Google Spreadsheet 的第一列先設定好,後續 Apps Script 會依照固定順序寫入。

第一步:Google Drive 環境準備

先在 Google Drive 建立一個主資料夾,例如:名片管理系統

並在裡面建立兩個子資料夾:

  • Inbox(待處理):
    手機拍照後,名片圖片一律上傳到這裡

  • Archived(已建檔):
    名片處理完成後會自動移動到這個資料夾

重要

請記下這兩個資料夾網址列中 folders/ 後面的 ID,後續 Apps Script 需要使用。

Read more »

You are given two integer arrays nums1 and nums2, sorted in non-decreasing order, and two integers m and n, representing the number of elements in nums1 and nums2 respectively.

Merge nums1 and nums2 into a single array sorted in non-decreasing order.

The final sorted array should not be returned by the function, but instead be stored inside the array nums1. To accommodate this, nums1 has a length of m + n, where the first m elements denote the elements that should be merged, and the last n elements are set to 0 and should be ignored. nums2 has a length of n.

給定兩個整數陣列 nums1nums2,都按照非遞減順序排列,以及兩個整數 mn,分別代表 nums1nums2 中元素的個數。
請將 nums1nums2 合併為一個按非遞減順序排列的陣列。
最終的排序陣列不應該由函數返回,而是要儲存在陣列 nums1 內部。為了實現這一點,nums1 的長度為 m + n,其中前 m 個元素表示應該合併的元素,後 n 個元素設為 0 並且應該被忽略。nums2 的長度為 n

目標:將 nums2 合併於 nums1

  • 必須在 nums1 內部完成合併,不能使用額外的陣列空間
  • 不返回值:函數不需要 return,直接修改 nums1
Read more »

最近在部署專案到 Vercel 時,遇到了一個讓我困擾的錯誤訊息:「Unexpected token ‘<’」。這個錯誤通常表示我們預期收到的是 JSON 格式的資料,但實際上卻收到了 HTML 頁面。本文將分享我遇到這個問題的背景、原因,以及兩種解決方案,幫助大家在使用 Vercel 部署時能夠順利取得 API 資料。

部署至 Vercel 遇到「Unexpected token ‘<’」錯誤:原因與解法

在將專案部署到 Vercel 時,當我透過瀏覽器或 extension background fetch 呼叫 API,卻出現以下錯誤:

1
取得資料夾失敗: Unexpected token '<', "<!doctype "... is not valid JSON

那代表請求並沒有真正拿到 JSON,而是得到了 HTML 頁面。本文將說明這個問題的原因、背後的背景,以及兩種完整的解法。

問題說明

這次在 Arc 瀏覽器 的開發過程中,我嘗試從部署於 Vercel 的 API 端點取得資料,但 Console 顯示:

1
index.iife_dev.js:24922 取得資料夾失敗: Unexpected token '<', "<!doctype "... is not valid JSON

進一步到 Arc 的 Service-Worker DevToolsarc://inspect/#service-workers)中,打開 background worker 的 Console,可以看到請求的回應實際上是一份 HTML 登入頁面,而非 JSON 資料。


背景補充:Vercel 的 Deployment Protection

這個錯誤並非來自 CORS,而是因為 Vercel 的 Deployment Protection 機制。
當專案開啟「Vercel Authentication」後,所有 Preview Deployments 都需要登入才能訪問。

因此,extension background 在發出請求時,被導向至登入頁面(HTML),導致解析 JSON 時出現:

Unexpected token '<'

Read more »

題目描述

You are given the heads of two sorted linked lists list1 and list2.

Merge the two lists into one sorted list. The list should be made by splicing together the nodes of the first two lists.

Return the head of the merged linked list.

給定兩個已排序鏈結串列的頭節點 list1 和 list2。
將兩個串列合併為一個已排序的串列。該串列應透過拼接前兩個串列的節點來建立。
回傳合併後鏈結串列的頭節點。

Example 1:
Input: list1 = [1,2,4], list2 = [1,3,4]
Output: [1,1,2,3,4,4]

Example 2:
Input: list1 = [], list2 = []
Output: []

Example 3:
Input: list1 = [], list2 = [0]
Output: [0]

解題思路

我一開始想法,會需要 2 迴圈去比較,然後組出一個新的 list node,最後再將新的 list node 回傳。
但是這樣的話,時間複雜度會是 O(n^2),因為每次都要從頭開始比較。
後來想說,因為兩個 list 都是已排序好的,所以可以用雙指標的方式,一次比較兩個 list 的頭節點,將較小的節點加入新的 list node,然後將該指標往後移動一位,這樣時間複雜度就會是 O(n),因為每個節點只會被比較一次。

範例程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} list1
* @param {ListNode} list2
* @return {ListNode}
*/
var mergeTwoLists = function(list1, list2) {
let head = null
let nowNode = null
let list1pointer = list1;
let list2pointer = list2;
// 兩兩比較
if(list1 === null && list2 === null) return head
if(list1 === null && list2 !== null) return list2
if(list1 !== null && list2 === null) return list1


// 決定 head
// 因為當你選擇了 list1pointer 作為頭節點後,你應該:
// 移動 list1pointer 指針:list1pointer = list1pointer.next
// 設定 nowNode 為當前的尾節點:nowNode = head
if(list1pointer.val <= list2pointer.val ){
head = list1pointer
list1pointer = list1pointer.next
}else{
head = list2pointer
list2pointer = list2pointer.next
}
nowNode = head

//進入迴圈檢查
while(list1pointer !== null && list2pointer !== null){
if(list1pointer.val <= list2pointer.val){
nowNode.next = list1pointer
list1pointer = list1pointer.next
}else {
nowNode.next = list2pointer
// 移動指針
list2pointer = list2pointer.next
}
nowNode = nowNode.next;
}
// 最後要再檢查。其中一不為 null
if(list1pointer !== null){
nowNode.next = list1pointer
}else{
nowNode.next = list2pointer
}

return head

};
Read more »

Given an array of meeting time intervals where intervals[i] = [startᵢ, endᵢ], determine if a person could attend all meetings.

給定一個會議時間 intervals 陣列,其中 intervals[i] = [startᵢ, endᵢ],判斷一個人是否能夠參加所有會議。

Example 1:

Input:

intervals = [[0,30],[5,10],[15,20]]

Output:

false

Example 2:

Input:

intervals = [[7,10],[2,4]]

Output:

true

Constraints:

  • 0 <= intervals.length <= 10⁴
  • intervals[i].length == 2
  • 0 <= startᵢ < endᵢ <= 10⁶

解題思路

要判斷一個人是否能夠參加所有會議,關鍵在於檢查會議時間是否有重疊。若有任何兩個會議的時間區間重疊,則無法參加所有會議。

重疊情況分析

[1,5][2,4] 為範例:

  1. 假設會議時間按開始時間排序:將所有會議按照開始時間由早到晚排列
  2. 檢查相鄰會議:排序後,只需檢查相鄰的會議是否重疊
  3. 重疊條件:前一個會議的結束時間 > 下一個會議的開始時間

步驟

  1. 將所有會議按開始時間排序
  2. 遍歷排序後的會議陣列
  3. 檢查每個會議的結束時間是否晚於下一個會議的開始時間
  4. 若發現重疊,回傳 false;否則回傳 true

這樣的方法時間複雜度為 O(n log n)(主要是排序的時間),空間複雜度為 O(1)。

Read more »