📄 全部影片 🎧 語音解析 📝 筆記 ✍️ 練習

✍️ 練習

讀完只是消費,留下來的靠消化。每筆資訊已標好類別與該做的練習,做完就灰掉。

51 站更新於 2026-10-01 00:54

今天

🤖 想要有人帶著做?到 Claude Code 裡練習

網頁適合自己翻卡與打勾;P/A/C/E 需要一問一答。複製下面的 prompt 貼進 Claude Code,它會一次一題、用填空題帶你做,做完自動寫回進度。

① 先按「⬇ 匯出進度」,把 digest.state.json 放到 workspace/(不然 Claude Code 看不到你在網頁上做過的) ② 複製 prompt ③ 貼進 Claude Code

顯示 51 / 51 站按空白鍵或 Enter 加下一個條件;條件之間是 AND

Ruby, Rails, and the future of programming - Matz, DHH & Jeremy Daer

Ruby on Rails 工程職涯與架構角色 產生於 2026-09-30 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

別當 meat proxy:讓 agent 自己讀錯誤訊息
  1. 找出你最近一次在 agent 與終端機之間手動複製貼上的情境
  2. 改成讓 agent 直接執行那個指令並讀它自己的輸出
  3. 把這個指令寫進專案的 CLAUDE.md 或 agent 設定,下次它自己會跑
  4. 確認它失敗時會自己重試,而不是回來問你
今天就做 挑一個你今天手動貼過錯誤訊息的指令(跑測試、build、lint),改成讓 agent 自己跑自己讀,並寫進專案設定檔。
第 9 段 → Slop、slop grenade 與心理防衛 · meat proxy
像帶人一樣校準對 agent 的信任
  1. 先決定這個任務的查核深度:逐行讀、只讀 diff、還是只看結果
  2. 跑完後記下它錯在哪、錯的類型(邏輯/範圍/幻覺)
  3. 連續幾次沒錯就降一級查核,出錯就升一級
  4. 換模型版本時把查核拉回最嚴,重新校準一次
今天就做 拿你手上一個 agent 剛完成的任務,寫下你這次的查核深度與它實際出的錯,決定下一個任務要升還是降。
第 10 段 → 判斷要交出去多少:信任等於能力 · trust calibration
讓 agent 用你最好讀的語言解釋別的語言的程式碼
  1. 挑一段你讀不動的程式碼(Rust/C/別人的舊專案)
  2. 請 agent 用 Ruby(或你最熟的語言)改寫成等價的說明版
  3. 讀說明版、自己講一次演算法
  4. 回頭對照原碼,找出說明版省略掉的地方
今天就做 挑一段你看不懂的第三方原始碼,請 agent 用你最熟的語言重寫成可讀版,然後自己口述一次它在做什麼。
第 6 段 → 寫程式不再是職涯,能力卻被放大 · pseudocode
把「怎麼替 agent 定義目標」寫下來
  1. 回想最近一次 agent 做對的任務,寫下你當時給了哪些限制與驗收條件
  2. 再回想一次做壞的,寫下缺了哪一條
  3. 把這些條件整理成專案的 agent 指令檔
  4. 下次直接套用,再補上新學到的條件
今天就做 打開你專案的 CLAUDE.md(沒有就開一個),寫下三條「這個專案的 agent 必須遵守的驗收條件」。
第 8 段 → Matz 才是最早的 vibe coder · vibe coding

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

社群對 AI 的反應 ⇄ 悲傷五階段
新 社群面對 agent 取代手寫程式碼的反應  ⇄  像 Kübler-Ross 的悲傷五階段(否認/憤怒/討價還價/沮喪/接受)
第 2 段 → 悲傷五階段的 speedrun · five stages of grief
vibe coding ⇄ Matz 帶 Ruby core 的十五年
新 用自然語言指揮 agent 寫程式  ⇄  像 Matz 提案、core 開發者實作、Matz 驗收合併
第 8 段 → Matz 才是最早的 vibe coder · vibe coding organic intelligence
今天的 agent ⇄ 1 MHz 的 Commodore 64
新 現在的 agent 速度與 token 成本  ⇄  像 Commodore 64 的 1 MHz 與當年省位元組的寫法
第 13 段 → Commodore 64 時代:token 焦慮是短視 · Commodore 64 Moore's law
AI 取代寫程式 ⇄ 工業自動化與《摩登時代》
新 agent 接手大部分手寫程式碼  ⇄  像 八十年前機器接手生產、卓別林《摩登時代》的恐懼
第 3 段 → 身分活得比職業實踐更久
要求 agent 零缺陷 ⇄ 自駕車一次事故就是太多
新 社群要求 agent 寫出零 bug 的程式碼  ⇄  像 自駕車出一次車禍就被要求下架,人類每年撞死數萬人卻被原諒
第 10 段 → 判斷要交出去多少:信任等於能力 · algorithm aversion

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

面對不確定要操作機率,不要操作預測
probability over prediction
第 1 段 → 預測失效,只剩機率 · coding agent
人的價值從打字移到品味與比例感
software maker
第 4 段 → 從 software writer 到 software maker · taste software writer
agent 要的是短命行程,所以要事前編譯
ahead-of-time compilation
第 5 段 → Spinel:為 agent 重畫 Ruby 的界線 · Spinel Just-in-Time compilation
把軟體想成固定大小的餅,就會算錯需要多少人
lump of labour fallacy
第 7 段 → 餅不是固定的:軟體會爆量 · solution provider
提想法、別人實作、自己驗收——資深者一直都這樣工作
vibe coding
第 8 段 → Matz 才是最早的 vibe coder · organic intelligence
新技術一開始都像玩具,嘲笑玩具的人會一樣地錯
innovator's dilemma
第 8 段 → Matz 才是最早的 vibe coder
交出多少判斷,等於它展現多少能力
trust calibration
第 10 段 → 判斷要交出去多少:信任等於能力 · common sense algorithm aversion
元件看不見之後,就沒有人願意養它
tragedy of the commons
第 11 段 → 元件變透明之後,開源怎麼活 · software supply chain
抽象是為了補人腦的限制而存在的
abstraction
第 12 段 → 抽象層是為了人腦的限制而存在 · libC world model
token 效率跟靜態型別無關,跟訓練資料也無關
token efficiency
第 14 段 → Ruby 的 token 效率與 agent 的偏好 · static typing pre-training

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

Spinel 實測記憶體約為原本的二十分之一
Matz 2026 年三月開始做 Spinel,把 Ruby 編譯成原生執行檔;實測某個程式的記憶體用量約為 CRuby 的 1/20(字幕裡 20 MB / 25 MB 的絕對數字前後不一致,可靠的只有比例)。
證明 ahead-of-time compilation
演練 這個數字證明了 agent 時代什麼變重要?你會在什麼情況下拿它來說服人改用 AOT?
第 5 段 → Spinel:為 agent 重畫 Ruby 的界線
Matz 用手機讓 Claude 一小時解掉 12 個 issue
演講當天早上 Spinel 的 repo 還是 0 issue 0 PR,一小時內冒出約 20 個 issue 與一個 PR;Matz 用手機開 Claude app 指派,演講結束時看到 12 個問題已經被解掉。
證明 vibe coding
演練 這個例子成立的前提是什麼(誰定義驗收標準)?換到你的專案上,瓶頸會出現在哪裡?
第 9 段 → Slop、slop grenade 與心理防衛
Rails 第一版全自己寫,第六七版幾乎沒寫
DHH 說 Rails 第一版完全是他一個人寫的,第二版大概寫一半,到第六、第七版幾乎沒寫任何一行,只剩下方向、什麼算好、什麼該收進來。
證明 vibe coding
演練 這證明了資深者的價值在哪裡?你現在的工作裡,有哪一部分其實已經是這個形狀?
第 8 段 → Matz 才是最早的 vibe coder
美國每年 4–4.5 萬人死於車禍
DHH 引用:美國每年有 40,000–45,000 人死於交通事故;他說自駕技術「依他看到的資料」約比一般人安全八倍,若能套用就有三萬人不會死。八倍這個數字沒有公認來源,比較基準一換就會大幅變動。
證明 trust calibration
演練 這組數字支持什麼論點?它最弱的一環是哪裡(誰量的、怎麼量的)?
第 10 段 → 判斷要交出去多少:信任等於能力
13 種語言裡只有 Ruby、Python、JS 同時有效率
Matz 的同事比較了約 13 種語言(Ruby、Python、JavaScript、TypeScript、Rust、Kotlin、Go、Haskell 等),只有 Ruby、Python、JavaScript 同時在 token 數與執行時間上有效率,而靜態型別對兩者都沒幫助。這是一次非正式比較,方法未公開。
證明 token efficiency
演練 這個比較量的是「最終程式碼」還是「agent 完成任務的總成本」?漏掉的那一項會怎麼改變結論?
第 14 段 → Ruby 的 token 效率與 agent 的偏好
行動時代只有兩家,現在領先只以週月計
DHH 的對照:行動時代只有 Google 與 Apple,桌面時代只有 Windows 與 Apple;現在有數以百萬計的 token 供應者,Anthropic 與 OpenAI 的領先只以月甚至週計算,而開源權重模型已經非常好。
證明 probability over prediction
演練 這個對照支持哪一個結論?它混在一起的兩件事是什麼(模型分數落差 vs 整體成本結構落差)?
第 13 段 → Commodore 64 時代:token 焦慮是短視
Matz 列出的那串看不見的元件
Matz 舉的例子:即使用 agent 蓋軟體,成品仍站在 Rust 編譯器、Ruby 直譯器、Ruby on Rails、Linux、PostgreSQL 這些既有元件上;agent 用得愈多,這些元件對人愈透明。
證明 tragedy of the commons
演練 這串清單證明了什麼?除了贊助之外,agent 還切斷了哪一條免費的品質回饋迴路?
第 11 段 → 元件變透明之後,開源怎麼活

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

Spinel 是什麼
Spinel 是什麼?
Matz 2026 年起做的 Ruby AOT 編譯器,把 Ruby 編成原生執行檔
第 5 段 → Spinel:為 agent 重畫 Ruby 的界線
mruby 的定位
mruby 的定位是什麼?
給嵌入式/微型裝置用的輕量 Ruby 實作
第 5 段 → Spinel:為 agent 重畫 Ruby 的界線
JIT 對短命行程為什麼沒用
為什麼 JIT 幫不了 agent 常開的短命行程?
JIT 要跑熱才划算,短命行程根本熱不起來
第 5 段 → Spinel:為 agent 重畫 Ruby 的界線
MINASWAN 的全名
MINASWAN 是哪句話的縮寫?
Matz is nice and so we are nice
第 15 段 → 價值觀、部落,與必要的摩擦
Omarchy 是什麼
Omarchy 是什麼?
DHH 做的 Linux 桌面環境專案(字幕誤譯成 Omachi/Amashi)
第 15 段 → 價值觀、部落,與必要的摩擦
algorithm aversion 的意思
algorithm aversion 指什麼?
人對機器犯錯的容忍度明顯低於對人犯同樣的錯
第 10 段 → 判斷要交出去多少:信任等於能力

Rails World 2026 Opening Keynote - DHH

Ruby on Rails 工程職涯與架構角色 產生於 2026-09-29 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

把自己動手改程式碼當成流程缺陷的訊號來處理
  1. 記下每一次你放棄 agent、自己動手寫的位置
  2. 問一句:agent 為什麼做不出我要的東西(規格不清?工具不足?環境不對?)
  3. 把答案寫回那份 prompt/規格/agent 的工作環境,而不是只留下修好的程式碼
  4. 短期允許拿鉛筆補一下,但同一天要去修那台「工廠」
  5. 每週看一次這份清單,出現次數該往下掉
今天就做 今天挑一次你自己動手改的程式碼,寫下 agent 做不到的原因,並當場改一段 prompt 或規格,讓同樣的任務下次不必你出手。
第 11 段 → 37signals 宣布手寫程式碼收工
先讓 agent 出第一版,用重導取代事先想清楚
  1. 用一句意圖先要一版能跑的東西,不求對
  2. 打開它,只記下明確壞掉的地方,而不是你自己會怎麼寫
  3. 把這些觀察當成下一輪 prompt,要求二十分鐘內看到第二版
  4. 同一個地方卡到第三版,才回頭改規格而不是繼續重導
今天就做 挑一個你一直在腦中設計、還沒動手的小功能,今天讓 agent 直接出第一版,只看畫面或輸出就指出壞掉處,限自己 30 分鐘內看到第二版。
第 13 段 → HEY 不再是 web app:六個原生應用同時開工
用派工給同事的方式指派 agent:先寫驗收標準再送出
  1. 把任務寫成「要什麼結果」,不寫實作步驟
  2. 送出前先寫下驗收標準:怎樣算做完、怎樣算壞
  3. 送出後離開去做別的事,不看 token 一個一個吐出來
  4. 有成果才回來,照驗收標準決定收下或重導
  5. 把驗收時發現的漏洞補回下一次的派工模板
今天就做 今天挑一件事用非同步方式交出去:先寫三行驗收標準再送出,然後關掉視窗做別的事,等有成果才回來看。
第 15 段 → 非同步指派,不是坐在聊天室裡等
刻意帶著新手心態,在更高的抽象層次下 prompt
  1. 先寫下你想要的結果,不寫實作方式
  2. 刪掉所有「用哪個函式、哪個資料結構」的指定
  3. 只保留驗收得出來的條件:行為、輸出、限制
  4. 把成果當黑盒子從外面評估:合不合用,而不是寫得漂不漂亮
  5. 不合用時補的是條件,不是實作細節
今天就做 拿你最近寫過最詳細的一份 prompt,刪掉所有實作指定只留結果條件,重跑一次,比較兩份產出哪一份更合用。
第 16 段 → Ruby 與 Rails 剩下什麼位置:web、token 效率與新手心態
實測「保持同步的成本歸零」這個假設會不會漏改
  1. 挑一條散落在系統各處的規則:稅率、權限、日期格式都可以
  2. 自己先數出它出現在幾個地方,寫下來當答案
  3. 叫 agent 把它全部找出來並改掉
  4. 比對漏掉幾處、改錯幾處
  5. 記下漏改率,再決定你敢拆掉哪一層抽象
今天就做 今天挑一條散在你系統各處的規則,先自己數出處數,再叫 agent 全部找出來改掉,記下它漏掉幾處。
第 19 段 → 抽象化在 agent 時代要重新估值
給你的 app 一個 CLI,下週五之前要看得到
  1. 列出使用者在你的 app 裡最常做的三到五個動作
  2. 每個動作做成一個子命令,輸入輸出都是純文字
  3. 錯誤訊息也寫成給機器讀得懂的格式
  4. 補一份 --help,把用法寫到不必看文件
  5. 叫一個 agent 只靠 --help 完成一次真實任務,它卡住的地方就是要修的地方
今天就做 今天替手上的專案寫出第一個 CLI 子命令(挑最常用的那個動作),再叫 agent 只靠 --help 跑一次,看它卡在哪。
第 20 段 → 自備 agent:每個 app 都該有 CLI
把「決定要什麼」和「做出來」拆成兩次獨立委託
  1. 第一次委託只要方案:叫模型產出三個視覺或設計選項
  2. 自己挑一個,存成截圖或一段明確描述
  3. 第二次委託才要實作:叫 agent 照著那個選項做
  4. 驗收只比對「像不像你挑的那個」,不看實作
  5. 不滿意就回第一步換方案,而不是在實作裡反覆修
今天就做 你要做的下一個小東西,今天先叫模型給三個設計方案、挑一個截圖,再叫 agent 照著做,全程不看程式碼。
第 22 段 → 一次成形的應用,與半個 MB

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

攝影取代寫實肖像畫 ↔ agent 取代手寫程式碼
新 agent 取代手寫程式碼,工程師只剩決定做什麼、怎麼拆問題  ⇄  像 攝影取代寫實肖像畫,畫家集體轉向立體派與印象派
第 4 段 → 畫家轉行:寫實不再值錢,創造力反而爆發
2025/11/24 那一天 ↔ 1900 年的 Kodak Brownie
新 11/24 之後第一次有大量的人體驗到與另一種智慧結對寫軟體  ⇄  像 1900 年 Brownie 讓一美元就能拍照,攝影從專業變成日常
第 6 段 → 2025/11/24:我們這代的 Brownie
他說的四個月低谷 ↔ Gartner 原本的 trough of disillusionment
新 用低谷描述「能力曲線的短暫平台期」,而且只有四個月  ⇄  像 Gartner 的 trough of disillusionment:期待過高後的必然修正
第 7 段 → 失望之谷只撐了四個月
1968 年的 10x ↔ 他說的 100x 甚至 1000x
新 不用這些工具的最差者 vs 用工具的最佳者,差 100 到 1000 倍  ⇄  像 1968 年 ACM 論文:同一批程式設計師之間 5x–30x,平均 10x
第 8 段 → 從 10x 到 1000x:換算成職業的單位
Rails 省掉的決策 ↔ agent 省掉的實作
新 agent 省掉實作本身,五個月不寫碼也把每根煩人的小刺修掉  ⇄  像 Rails 的 convention over configuration 省掉路由、檔名這類決策
第 10 段 → 「看看我沒在做的事」:這是 Rails 時刻
把不懂的 Rust 當黑盒子 ↔ 18 世紀業主 commission 畫家
新 完全不懂 Rust,只從外部評估 agent 交出來的後端  ⇄  像 18 世紀業主出錢委託畫家,只用成果驗收,不管技法
第 16 段 → Ruby 與 Rails 剩下什麼位置:web、token 效率與新手心態
應用內建的 concierge ↔ 我自己帶來的管家
新 使用者帶自己的 agent 來操作你的 app,靠 CLI 串接上百萬個應用  ⇄  像 應用內建一個只懂自家產品的 AI 助理(concierge)
第 20 段 → 自備 agent:每個 app 都該有 CLI

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

技術跨過臨界點的徵兆是:使用它不再需要事前承諾
frictionless
第 5 段 → 技術是斷續前進的:Brownie 之後八十年沒動
分水嶺不是模型最聰明,是外殼讓智慧便宜到很多人用得起
harness
第 6 段 → 2025/11/24:我們這代的 Brownie
手寫程式碼變成例外狀態:出現一次就代表有東西壞了
pencils down
第 11 段 → 37signals 宣布手寫程式碼收工
重導成本低到可以把「做錯」當正常流程:先做出來再看
redirect cost
第 13 段 → HEY 不再是 web app:六個原生應用同時開工
我說要做什麼、你用 Rust 寫、我永遠不必看
black box
第 14 段 → 後端換成 Rust:永遠不必看,我就愛它
跟 agent 合作最好的方式是派任務出去,不是坐在聊天室裡等
asynchronous
第 15 段 → 非同步指派,不是坐在聊天室裡等
框架的意見在人類時代是約束,在 agent 時代是壓縮演算法
token efficiency
第 16 段 → Ruby 與 Rails 剩下什麼位置:web、token 效率與新手心態
抽象要重估:重複與同步的成本歸零,瓶頸的代價卻還在
abstraction
第 19 段 → 抽象化在 agent 時代要重新估值
我不要你的 concierge:每個 app 都該讓使用者的 agent 進得來
CLI
第 20 段 → 自備 agent:每個 app 都該有 CLI
既然沒人算得準,選擇樂觀是決策問題,不是預測問題
P(bloom)
第 24 段 → P(bloom) 勝過 P(doom):把不可知變成一個選擇

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

相機 1840 年就有,畫家的工作卻是 1900 年 Brownie 一美元才崩塌
1840 年前後相機問世,1840–1900 拍攝技術一路變好;1900 年柯達 Brownie 一美元就能拍照,寫實肖像才失去經濟基礎。Tuxen 1886 年那幅《King Christian IX》畫了三年,至今掛在哥本哈根。
證明 harness
演練 這六十年的落差證明致命的是能力門檻還是價格門檻?你手上哪個工具已經跨過「便宜到不必考慮」那條線?
第 3 段 → 三年一幅畫,然後 Brownie 出現 · Kodak Brownie democratization of technology
童年只有十張照片,2026 年一年拍兩兆張——同一個底層技術
1921 與 1979 年的家族照技術幾乎一樣,Brownie 之後八十年沒動;手機讓拍照零摩擦,2026 年一年兩兆張。底片時代你得先決定「這值得一張」。
證明 frictionless
演練 這組數字證明停滯的原因是能力還是摩擦?你團隊裡哪一道「要不要開票、排一個 sprint」的關卡正是同一種事前承諾?
第 5 段 → 技術是斷續前進的:Brownie 之後八十年沒動
這次的失望之谷只撐四個月,攝影那次撐了一百年
當時大家嫌新版本不如舊版、某次發表在 benchmark 上沒明顯進步、市場估值下跌;到六月 Fable 5 與 Mythos 出來就結束。分水嶺體感:從「指派任務」變成「指派結果」。
證明 harness
演練 拿你上一次交給 agent 的工作:成果還要不要逐行檢查?這個判準比 benchmark 更能告訴你自己跨過哪條線了嗎?
第 7 段 → 失望之谷只撐了四個月 · trough of disillusionment
20 個月寫掉前 21 年一半的碼,日常語言從 4 種變 12 種
2004–2024:647K 行、26,325 commit、Ruby 355K(55%);2025–2026:321K 行、7,176 commit、Ruby 只剩 10K(3%)。年均 31K → 2026 年 292K,日常使用語言 4 → 12。
證明 black box
演練 行數可以打折,但「同時碰十二種語言」要打折嗎?它證明了什麼成本歸零,而那是後端換 Rust 的前提嗎?
第 9 段 → 20 個月寫掉前 21 年一半的程式碼
20、30 個 PR 各自都合理,疊起來架構像瑞士乳酪
Basecamp 5 vibe coding 的失敗形狀:個別 PR 看起來都合理,20–30 個疊起來後架構被打成瑞士乳酪;當時的結論「技術還沒到、回去人工審查」他現在說是錯的。
證明 pencils down
演練 這個症狀是模型不夠強,還是沒有任何人被指派負責全域一致性?換更強的模型能讓它自動消失嗎?
第 12 段 → Basecamp 5 的失敗,與那個「錯誤的結論」 · vibe coding pull request
Windows 版第一個 prompt 吐回來不堪用,二十分鐘後就能看
HEY 六個原生應用一週前才啟動。Windows 第一版標題列文字互疊、清單擠成一團、搜尋框漂在奇怪位置;二十分鐘後的修正版已可用,macOS 版是完整原生收件匣。
證明 redirect cost
演練 舊世界丟掉一版 UI 等於丟掉數週人力,現在只要二十分鐘——這對「先想清楚再動手」這個習慣意味著什麼?
第 13 段 → HEY 不再是 web app:六個原生應用同時開工
後端改寫成 Rust 郵件伺服器後 CPU 少 99%、記憶體少 95%
前端變原生後後端不必再渲染 HTML,那段程式碼被改寫成它本質上就是的郵件伺服器:CPU 少 99%、記憶體少 95%,十台主機只為備援,尖峰粗估一台 Raspberry Pi 撐得住。
證明 black box
演練 這筆交易把「人要讀」的權重降到零來換效能——半夜出事時誰來讀那段 Rust?你願意在哪些系統上接受這個風險?
第 14 段 → 後端換成 Rust:永遠不必看,我就愛它
過去每年約三萬行 Ruby,去年八月一個月十五萬行
約六十倍,但他放任 agent 在 Rust 這類語言吐出比必要更多的碼,分母有灌水。語言分佈也從 Ruby 55% 變成沒有一種超過三成(Shell 26%、Python 13%、Go 13%、Ruby 3%)。
證明 pencils down
演練 分母被灌水之後,這個倍率還剩多少說服力?它真正支撐的是「寫更多」還是「涉足更廣」?
第 17 段 → 15 萬行,與一個叫英文的程式語言
只記得跟球鞋和 podcast 有關,五年前那封信幾分鐘就被找出來
HEY 用 Elasticsearch,勉強堪用但常找不到東西,尤其當你不知道自己在找什麼。透過 CLI 讓 agent 用概念而不是關鍵字搜尋:不記得對象、公司、年份,幾分鐘後信就出現。
證明 CLI
演練 這件事成立是因為模型更聰明,還是因為索引的維度不一樣?你手上哪個搜尋不好用的系統其實缺的是這個入口?
第 20 段 → 自備 agent:每個 app 都該有 CLI
Omarchy 安裝時間從 213 秒掉到 35 秒,實驗室裡已做到 9 秒
去年 Rails World 展示 3 分 33 秒還很自豪;上週在 AMD Halo 筆電上跑出 35 秒,實驗室裡 9 秒。被問為什麼 3 分鐘不夠快,他引 Mitchell Hashimoto:追求卓越不需要理由。
證明 frictionless
演練 安裝時間一台機器只發生一次、幾乎不產生商業價值,它會被優化的真正原因是什麼?你系統裡有哪些同類的東西?
第 21 段 → Omarchy:把整台電腦都修好
這場 keynote 的簡報軟體週四才開始寫,二進位檔只有半個 MB
Hype 建在 Markdown 上、週四才開始寫,binary 半個 MB。過去二十年電腦的進步都花在人的生產力上,代價是應用又慢又肥——因為一個程式設計師小時太貴,不值得花在優化體積。
證明 frictionless
演練 你電腦上那個 1.2 GB 的應用不是因為做不到才肥,而是因為一直不值得花人力——現在誰來付這個成本?
第 22 段 → 一次成形的應用,與半個 MB · binary size one-shot
引進 ATM 時恐慌行員失業,結果銀行開更多分行、雇更多人
當時恐慌約三萬名行員會失業;分行營運成本下降後,銀行反而開更多分行、雇更多行員。(年代與數字他記錯了,見勘誤。)
證明 P(bloom)
演練 這個機制的前提是省下的成本能轉化成新需求——當市場需求已經飽和時,同樣的自動化會變成什麼?
第 23 段 → 擔憂、安全,與預測為什麼總是錯 · ATM

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

10x programmer 的原始範圍是同一群人之間,不是人機對比
10x programmer 這個說法原本量的是什麼差距?
同一批程式設計師之間的個體差異,5x–30x(1968 年 ACM 論文),不是用工具與不用工具的差距。
第 8 段 → 從 10x 到 1000x:換算成職業的單位 · 10x programmer variance
DRY 講的是知識只有一個權威表述,不只是別複製貼上
DRY 的全名與主張是什麼?
Don't Repeat Yourself:同一份知識在系統中只該有一個權威的表述。
第 19 段 → 抽象化在 agent 時代要重新估值 · DRY
概念搜尋比對的是意義的鄰近性,不是字面相符
semantic search 和關鍵字搜尋差在哪?
關鍵字比對字面,semantic search 比對意義的鄰近性,所以只剩概念殘片的記憶也找得回來。
第 20 段 → 自備 agent:每個 app 都該有 CLI · semantic search Elasticsearch
效率提高會讓總消耗增加,而不是減少
Jevons paradox 說的是什麼?
提高某項資源的使用效率,反而因為變便宜而讓總消耗量增加。
第 24 段 → P(bloom) 勝過 P(doom):把不可知變成一個選擇 · Jevons paradox
CVE 是公開資安漏洞的全球唯一編號制度
CVE 是什麼?
為每個公開揭露的資安漏洞指派唯一編號的制度,便於全球追蹤與修補。
第 24 段 → P(bloom) 勝過 P(doom):把不可知變成一個選擇 · CVE
open-weight 是權重可下載自行運行,跟開源不是同一件事
open-weight 模型指的是什麼?
模型參數可公開下載、能在自己硬體上跑的發行方式;不必然附上訓練資料或程式碼。
第 6 段 → 2025/11/24:我們這代的 Brownie · open-weight

how to learn ANYTHING faster than anyone

Older Brother 學習方法 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

寫下你正在學的技能的 fundamentals 是什麼
  1. 列出這個領域所有子技能
  2. 問哪一個被最多其他東西依賴、或最常被用到
  3. 把清單依此排序
  4. 只專心練排最前面的那一兩項
今天就做 挑你正在學的一項技能,寫下它的 fundamentals 是什麼(被最多東西依賴、最常用到的那一小塊)
第 2 段 → 原則一:80/20 法則
用 active recall 練習一段認知型內容
  1. 闔上書或筆記
  2. 自問自答,把知道的講出來,像在教別人
  3. 卡住的地方立刻記下來
  4. 回頭只補那個缺口,不整段重讀
今天就做 今天闔上一份你在讀的資料,自問自答 5 分鐘,把卡住的地方寫下來
第 3 段 → 原則二:盡快失敗與 Active Recall
一次只專心練一個子技能到變成習慣
  1. 列出大目標底下的所有子技能
  2. 挑一個被最多其他子技能依賴的先練
  3. 重複練到不用刻意思考就能做出來(變成習慣)
  4. 才開始練下一個子技能
今天就做 這星期只挑一個子技能集中練,其他子技能先不碰,直到它變成不用想就會做
第 4 段 → 原則三:學慢一點才學得快

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

先學 fundamentals 像蓋房子先打地基,其他東西都靠在它上面
新 先學一個領域裡被最多東西依賴、使用頻率最高的那一小塊  ⇄  像 蓋房子先打地基,之後所有樓層都靠在地基上,地基沒打好蓋越高越危險
第 2 段 → 原則一:80/20 法則
認知容量集中在一個子技能,像把水都澆在一顆種子上才會發芽
新 固定的認知容量集中在一個子技能,才能推過變成習慣的門檻  ⇄  像 把同樣一壺水澆在很多顆種子上,每顆只濕一點都不會發芽;全部澆在一顆,那顆才發芽
第 4 段 → 原則三:學慢一點才學得快

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

把「學習」本身視為一項可以刻意練習、可以變強的技能
skill of learning
第 1 段 → 沒人教過你怎麼學 · skill of learning
大約 80% 的成果來自 20% 的投入,先找出並精通那 20% 最有槓桿的部分
80/20 rule
第 2 段 → 原則一:80/20 法則 · 80/20 rule
把盡快、一再地失敗當成練習目標,因為每次失敗都是可修正的回饋
fail fast
第 3 段 → 原則二:盡快失敗與 Active Recall · fail fast
闔上書主動從記憶裡把知道的講出來,卡住的地方就是要回頭補的缺口
active recall
第 3 段 → 原則二:盡快失敗與 Active Recall · active recall
經歷失敗→退一步反思哪裡錯→吸收更多資訊→改進,然後重複的四步流程
learning cycle
第 3 段 → 原則二:盡快失敗與 Active Recall · learning cycle
大腦一天能有效吸收、處理新資訊的量有限,用完就會疲勞
cognitive capacity
第 4 段 → 原則三:學慢一點才學得快 · cognitive capacity
一個子技能練到不需要刻意思考就能做出來的狀態
habit
第 4 段 → 原則三:學慢一點才學得快 · habit
相信能力不是固定的,不管起點在哪,只要投入努力就能進步
growth mindset
第 5 段 → 拼圖最後一塊:成長心態與沉浸 · growth mindset

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

英文最常用一千字約覆蓋日常對話八成詞彙量
英文最常用的一千個字約覆蓋日常對話八成左右的詞彙量,所以先學高頻字是依使用頻率排序,不是隨便挑
證明 80/20 rule
演練 這個數字證明「先學高頻字」為什麼有效?你正在學的領域,要怎麼找出類似「最常用一千字」的那一份清單?
第 2 段 → 原則一:80/20 法則
發文第一天就爆紅學不到東西;投籃不進才能退一步反思
思想實驗:如果發文從第一天就爆紅,你什麼都學不到;投籃例子:投不進,你才能退一步反思哪裡錯——學習只發生在失敗之後
證明 fail fast
演練 為什麼作者說「你不會從成功中學習」?這對你現在正在練的東西,意味著你該主動去找什麼樣的情境?
第 3 段 → 原則二:盡快失敗與 Active Recall
做菜算給你看:五個子技能一起練每項進步 1%,只練燒烤進步 20%
刀工、調味、醬汁、煮麵、燒烤一次練,每項只進步 1%;只專心烤一塊雞肉,燒烤進步 20%——整體快四倍
證明 cognitive capacity
演練 為什麼集中練一項會比分散練五項快四倍,而不是總量一樣、只是換個排法?你現在是不是同時在練太多子技能?
第 4 段 → 原則三:學慢一點才學得快
你已經做過這件事了:走路、說話、讀寫都是從零學會的
作者用「你已經從零學會走路、說話、讀寫」當作 growth mindset 的證據——這次學新東西沒理由不同
證明 growth mindset
演練 這個證據為什麼能支持「這次也能學會」?如果你現在對某項技能覺得自己學不會,這個論證怎麼反駁那個念頭?
第 5 段 → 拼圖最後一塊:成長心態與沉浸

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

domain knowledge 與 learning how to learn 的區分
學校只考哪一種、沒教哪一種?
學校只評量 domain knowledge(歷史、科學等內容),從沒明確教過 learning how to learn(後設的學習方法)
第 1 段 → 沒人教過你怎麼學
怎麼找出那 20%
80/20 rule 實作時要怎麼找出那關鍵 20%?
按使用頻率或依賴關係排序,先學被最多其他東西依賴、用得最頻繁的那一小塊
第 2 段 → 原則一:80/20 法則
subskill 的定義
subskill 指什麼?
大目標底下可以獨立拆出來、分開練習的小技能,例如做菜底下的刀工、調味、燒烤
第 4 段 → 原則三:學慢一點才學得快
immersion 的定義
immersion 指什麼?
把正在學的東西放進所有空檔——洗澡、睡前、發呆時都在想它,達到著迷的程度
第 5 段 → 拼圖最後一塊:成長心態與沉浸

You Wouldn't Believe These Developer Interview Mistakes...

Program With Erik 工程職涯與架構角色 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

花 30 分鐘把一項基本功練到能不看資料開口講清楚
  1. 挑你的方向(前端/SDE)對應的基本詞彙清單
  2. 逐條問自己能不能不看程式碼用一句話講清楚
  3. 挑 2-3 個具體場景題練習(例如某個動作該用哪個 HTTP verb)
  4. 找人或對著鏡子講一次
今天就做 花 30 分鐘,把 Box model 或 REST 的其中一題,練到能不看資料口頭講清楚一次
第 2 段 → 基本功:先能運球再談戰術
準備 4-6 個 STAR 故事素材庫
  1. 列出 4-6 段過去經歷(主動超出份內、跟人衝突、一次失敗、趕死線的取捨)
  2. 把每段拆成情境/任務/行動/結果四塊
  3. 結果盡量換算成可量化的數字
  4. 面試前練習依不同問法從同一段經歷挑角度重講
今天就做 挑一段你最近做過的工作,用 STAR 四塊寫下來,結果那一格換算成一個數字
第 4 段 → 技術過了、行為題掛了:拿不出 impact
建立每天固定時段的準備習慣
  1. 把準備時段固定在每天同一個時間、同一個地點
  2. 前一晚先決定好明天要做哪一題,坐下不用再思考
  3. 把時間切成練新題與回顧舊題兩塊
  4. 狀態差的日子也做最小單位(只讀一題解法)不中斷連續性
今天就做 今晚先定好明天準備的時段與要做的一題,設一個提醒鬧鐘
第 6 段 → 每天排定時間,把準備變成習慣

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

基本功題目像球隊每天要練運球,不管戰術多花俏
新 Box model、REST 這類基本功題目考的是「你是否真的動手做過」,不是知識量  ⇄  像 球隊每天都要練運球、傳接這些基本動作,不管球隊的戰術設計多複雜
第 2 段 → 基本功:先能運球再談戰術
職級不對應像地方球隊隊長換到國家隊可能只是替補
新 小公司的 senior 到大廠可能只是 mid-level,因為衡量基準是影響半徑不是能力  ⇄  像 地方球隊的隊長換到國家隊,球技沒變,但比較的對手和標準整個換了,可能只排得上替補
第 3 段 → 投錯職級:你的 senior 不是他們的 senior
狀態差的日子做最小單位也算完成,像健身那天做 5 下伏地挺身也算數
新 習慣要成立靠固定觸發點與極低啟動門檻,不是靠決心;斷掉的是連續性不是進度  ⇄  像 健身習慣:狀態不好那天做 5 下伏地挺身也算完成,重點是沒有斷掉,不是今天做了多少下
第 6 段 → 每天排定時間,把準備變成習慣

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

CSS 把元素視為內容/padding/border/margin 四層,決定它實際佔多大空間
Box model
第 2 段 → 基本功:先能運球再談戰術 · Box model
大公司把工程師分成明確層級,每級對應不同職責範圍與面試標準
leveling
第 3 段 → 投錯職級:你的 senior 不是他們的 senior · leveling
透過過去實際發生的經歷,預測未來在類似情境下會怎麼做的面試題型
behavioral interview
第 4 段 → 技術過了、行為題掛了:拿不出 impact · behavioral interview
工作對產品、使用者或團隊造成的可衡量改變,不是做了哪些任務
impact
第 4 段 → 技術過了、行為題掛了:拿不出 impact · impact
用情境、任務、行動、結果四段組織行為題答案的框架
STAR method
第 4 段 → 技術過了、行為題掛了:拿不出 impact · STAR method
面試中設計一個能承受特定規模的系統,考的是取捨判斷不是標準答案
system design
第 5 段 → 別過度投資 system design · system design
找人扮演面試官,限時、邊寫邊講地完整跑一次面試流程的練習
mock interview
第 6 段 → 每天排定時間,把準備變成習慣 · mock interview
面試沒過後必須等待一段時間才能再次申請同一家公司的規定期間
cooldown period
第 7 段 → 面試是數字遊戲:失敗、複盤、再投 · cooldown period

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

職級不是一對一對應:衡量基準從獨立完成度換成影響半徑
mid-level 衡量把明確定義的任務做完,senior 要自己界定問題並帶動跨團隊,staff 對整個組織技術方向負責;十年二十年經驗投 junior 沒道理,三四年掛 senior 該投 junior 或 mid-level
證明 leveling
演練 同一個人搬到大廠職級可能下降,這證明了什麼沒有變、什麼變了?你會怎麼用這個判準決定自己該投哪一級?
第 3 段 → 投錯職級:你的 senior 不是他們的 senior
可量化的結果才算 impact:建置時間從 40 分鐘壓到 9 分鐘
把建置時間從 40 分鐘壓到 9 分鐘、每天省下團隊約兩小時等待,這才是作者說的 statistics;「後來順利上線了」不算
證明 impact
演練 這兩種說法在評分表上被記成什麼不同的東西?你手上有沒有一段經歷可以換算成類似的具體數字?
第 4 段 → 技術過了、行為題掛了:拿不出 impact
mid-level 折衷做法:1-2 小時詞彙加一套講題順序,不必刷完整本教材
投 mid-level 保留一小塊時間建立詞彙(快取、佇列、分片、讀寫分離)與講題順序(釐清需求→估流量→定介面→畫資料流→講瓶頸與取捨)
證明 system design
演練 為什麼投 junior 的人不該把時間花在 system design?投 mid-level 的人又該花多少、花在哪?
第 5 段 → 別過度投資 system design
作者自曝紀錄:Google 好幾次、Meta on-site 也沒過,但現在在 Amazon
投過 Google 好幾次只拿到一次 on-site 且沒過、Meta 面過幾次其中一次到 on-site 也沒過、一次 phone screen 就掛掉;多數大廠 6 個月到 1 年可重新申請
證明 cooldown period
演練 作者把「被刷掉」重新定義成什麼(能力判決 vs 流程中的常態)?你會怎麼用這個心態對待自己上一次被拒的面試?
第 7 段 → 面試是數字遊戲:失敗、複盤、再投

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

FANG 與 hiring freeze 的定義
FANG 指什麼?hiring freeze 是完全不招人嗎?
FANG 泛指 Facebook/Amazon/Netflix/Google 等一線科技公司;hiring freeze 是暫停或大幅減少錄取,通常不等於完全不招人
第 1 段 → 面試官視角:同樣的錯誤一再出現
面試關卡結構:各關分開評分
大廠面試為什麼「強項很強」補不了「弱項太弱」?
面試是一串分開評分的關卡(履歷、線上測驗、多位面試官各自評分後彙整),任何一關低分就可能整體不過
第 1 段 → 面試官視角:同樣的錯誤一再出現
responsive design/event listener/REST/endpoint/HTTP verb 定義
這五個基本功詞彙分別是什麼?
responsive design 是版面隨螢幕寬度調整;event listener 是註冊在元素上、事件發生時被呼叫的函式;REST 是用 URL 定位資源、HTTP 動詞表達操作的 API 風格;endpoint 是 API 對外暴露的具體網址;HTTP verb 是 GET/POST/PUT/PATCH/DELETE 等方法
第 2 段 → 基本功:先能運球再談戰術
staff engineer 與 bootcamp 的定義
staff engineer 和 bootcamp 分別是什麼?
staff engineer 是 senior 再上一級、負責跨團隊影響整個組織技術方向的個人貢獻者;bootcamp 是數週到數月的密集職訓課程
第 3 段 → 投錯職級:你的 senior 不是他們的 senior
architect 的定義
作者提到的 architect 指什麼角色?
負責決定系統整體結構與技術選型、而非只實作單一功能的角色
第 5 段 → 別過度投資 system design
data structures and algorithms 與 Atomic Habits
data structures and algorithms 涵蓋什麼?Atomic Habits 主張靠什麼建立習慣?
陣列、雜湊表、樹、圖等資料組織方式及其時間空間複雜度分析;Atomic Habits 主張靠設計環境、縮小行動門檻建立習慣,而不是靠意志力
第 6 段 → 每天排定時間,把準備變成習慣
on-site interview 與 phone screen 的定義
on-site interview 和 phone screen 分別是什麼?
on-site 是通過前面關卡後一天內連續多場、由不同面試官評分的最終輪;phone screen 是最前面的篩選關卡,通常一小時一到兩題程式題
第 7 段 → 面試是數字遊戲:失敗、複盤、再投

What Is Apache Pulsar? The Cloud-Native Messaging & Streaming Platform

The Data and AI Guy 訊息與串流系統 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

評估自己系統卡在哪個需求,值得列入 Pulsar 候選清單
  1. 列出現在同時維運幾套 queue/streaming 系統
  2. 檢查有沒有需要多租戶隔離或跨區域複製的需求
  3. 檢查有沒有掉一則訊息就不可接受的場景
  4. 檢查 topic 數量會不會成長到需要每使用者/裝置一個專屬 topic
今天就做 寫一句判斷:你現在的系統卡在哪個需求(多租戶/跨區/零遺失/百萬 topic),值得列入 Pulsar 候選清單
第 8 段 → 誰在用、用來做什麼
用 producer/consumer/topic 重新描述自己維運的訊息系統
  1. 找出你系統裡誰是 producer、誰是 consumer
  2. 找出資料現在存在哪一層,是否跟處理邏輯綁在同一台機器
  3. 想一下擴充處理能力時資料需不需要跟著搬
  4. 對照 broker/bookie 分離畫一張你系統的分層圖
今天就做 用 producer/consumer/topic 三個詞,把你現在維運的一個訊息系統重新描述一次
第 4 段 → 基本詞彙與核心想法:計算與儲存分離

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

中央管線像廣播電台,producer 與 consumer 互不認識
新 位置更新只發布一次到中央管線,誰需要誰訂閱,producer 跟 consumer 不需要互相知道  ⇄  像 廣播電台播送一次,誰想聽誰調頻,電台不用知道現在有誰在聽
第 2 段 → 中間層:broker、queue vs streaming
compute/storage 分離像餐廳把現場烹飪跟食材倉庫分開
新 Pulsar 把「處理訊息收發」跟「持久保存資料」放在不同機器層  ⇄  像 餐廳把現場烹飪跟食材倉庫分開:要多接客人就加廚師,不用把整個倉庫搬家
第 4 段 → 基本詞彙與核心想法:計算與儲存分離
百萬 topic 像每個使用者一個專屬信箱,不用貼標籤過濾
新 單叢集可達百萬 topic,解鎖「每個使用者/裝置一個專屬 topic」的設計  ⇄  像 傳統合用信箱要幫每封信貼標籤分類;每人一個專屬信箱,收到的自然就是自己的
第 7 段 → 架構帶來的五個優勢

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

開源的分散式 messaging 與 streaming 平台,解決 Kafka 在規模成長後的痛點
Apache Pulsar
第 1 段 → 問題:即時、可靠、大規模搬資料 · Apache Pulsar
把處理訊息收發與持久保存訊息資料放在不同機器層,讓兩者能獨立擴展
Compute/Storage 分離
第 4 段 → 基本詞彙與核心想法:計算與儲存分離 · Compute/Storage 分離
Pulsar 的訊息層節點:只做收發與追蹤 ack,不在本機保存訊息資料
Stateless broker
第 5 段 → 兩層架構:無狀態 broker + BookKeeper · Stateless broker
Pulsar 的儲存層,獨立的 Apache 分散式日誌儲存專案,負責耐久寫入與跨節點複製
Apache BookKeeper
第 5 段 → 兩層架構:無狀態 broker + BookKeeper · Apache BookKeeper
一個 topic 的歷史由一串 ledger 組成,各自分散存在不同 bookie 上
Ledger(segment)
第 6 段 → 協調層、ledger 分段與 tiered storage · Ledger(segment)
依冷熱把資料放不同成本儲存層:舊 ledger 自動卸載到 S3,近期資料留在 bookie
Tiered storage
第 6 段 → 協調層、ledger 分段與 tiered storage · Tiered storage
同一套實體系統安全服務多個彼此隔離的團隊/產品,各有存取控制與資源配額
Multi-tenant(多租戶)
第 7 段 → 架構帶來的五個優勢 · Multi-tenant(多租戶)
把 topic 資料自動複製到其他地理區域,且能自動 client failover
Geo-replication
第 7 段 → 架構帶來的五個優勢 · Geo-replication
訊息先複製到多個 bookie 並寫入磁碟,系統才向 producer 回 ack
Durable write(落盤後 ack)
第 8 段 → 誰在用、用來做什麼 · Durable write(落盤後 ack)

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

Yahoo 2013 年起源:100+ 產品跨全球、絕不掉訊息
2013 年 Yahoo 需要一套服務 100+ 產品(Mail、Finance、Ads…)、跨全球、零遺失的系統,市面上沒有於是自建,2015 上線、2016 開源
證明 Apache Pulsar
演練 Yahoo 當年的三個需求分別對回 Pulsar 後面的哪三項能力?這種「需求決定設計」的邏輯,你會怎麼用來判斷自己要不要導入 Pulsar?
第 3 段 → Pulsar 的定位與 Yahoo 起源
Kafka rebalancing:新增 broker 要實體複製既有資料,大叢集數小時
Kafka 每個 broker 同時處理訊息進出與本機磁碟儲存,新加 broker 是空的,必須把既有資料實體複製過去,大叢集上可能耗時數小時且效能受損
證明 Compute/Storage 分離
演練 Kafka 的 rebalancing 為什麼慢?這件事證明了「處理能力」和「資料位置」綁在一起會有什麼代價?
第 4 段 → 基本詞彙與核心想法:計算與儲存分離
加 bookie 後新開的 ledger 自然落到新節點,不用搬舊資料
一個 topic = 一串 ledger,每個 ledger 各自選一組 bookie 存放;加新 bookie 後新開的 ledger 自然分配到新節點,舊資料留在原處不必重排
證明 Ledger(segment)
演練 這件事怎麼解釋上一段「加 bookie 不用 reshuffle」?為什麼這對訊息系統來說已經足夠(寫入壓力永遠在新資料上)?
第 6 段 → 協調層、ledger 分段與 tiered storage
金融級保證:ack 那一刻資料已在多台機器磁碟上
Pulsar 保證訊息複製到多個 bookie(預設三份)並寫入磁碟後才回 ack 給 producer,機器斷電資料也在;代價是寫入延遲略高於 Kafka 預設
證明 Durable write(落盤後 ack)
演練 「複製到多個 bookie 且落盤後才 ack」跟 Kafka 預設比強在哪?這個保證值得用在你系統裡的哪個場景?
第 8 段 → 誰在用、用來做什麼

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

reliably / at scale / real time 分別對回什麼
開頭那句「可靠、大規模、即時」三個詞各對回後面哪個主題?
可靠對應資料不遺失、大規模對應擴展與百萬 topic、即時對應 streaming 而非批次
第 1 段 → 問題:即時、可靠、大規模搬資料
message queuing 與 streaming 的本質差異
queue 模型和 streaming 模型最根本的差別是什麼?
訊息被消費後還在不在:queue 被 ack 後就消失,適合分派工作;streaming 留在 log 上,consumer 只是移動讀取位置,適合多方各自讀歷史
第 2 段 → 中間層:broker、queue vs streaming
Cloud-native 的定義
文中 Cloud-native 指什麼?
元件可以獨立、快速地增減與替換,通常靠無狀態服務與分離的儲存達成
第 3 段 → Pulsar 的定位與 Yahoo 起源
Producer / Consumer / Topic 的定義
producer、consumer、topic 各自是什麼?
producer 是寫入訊息的一方、consumer 是讀取訊息的一方,兩者透過 topic 這個具名 channel 互動、不直接認識對方
第 4 段 → 基本詞彙與核心想法:計算與儲存分離
Source of truth 與 metadata store
ZooKeeper/metadata store 在 Pulsar 裡的角色是什麼?
保存 topic 歸屬、ledger 位置等 metadata 的高可用小叢集,是所有 broker 與 bookie 共享的權威來源
第 6 段 → 協調層、ledger 分段與 tiered storage
Subscription type 的兩種 ack 模式
Pulsar 的訂閱型態有哪兩種?各適合什麼?
逐筆 ack(individual,queue 風格,適合任務佇列)與累積 ack(cumulative,offset 風格,適合有序串流),同一 topic 可支援兩種
第 7 段 → 架構帶來的五個優勢
Pulsar Functions 的定義
Pulsar Functions 是什麼?
內建於 Pulsar 的輕量 serverless 計算,用 Java/Python/Go 寫小函式,在訊息流經時做過濾、路由、轉換、豐富化
第 7 段 → 架構帶來的五個優勢
Shared subscription 與 RPC over topics
shared subscription 和 RPC over topics 各是什麼?
shared subscription 是多個 consumer 共用同一訂閱、訊息輪詢分派,適合工作佇列;RPC over topics 是服務改用 topic 傳遞請求回應,需要系統能便宜支撐大量 topic
第 8 段 → 誰在用、用來做什麼

WebSockets Aren’t as Reliable as You Think.. Here's Why

Software Developer Diaries 網路與 API 基礎 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

替現有 WebSocket 專案加上 client 端 heartbeat
  1. client 每隔 5 秒送一個 ping 訊息
  2. server 收到訊息就回 pong
  3. client 記錄 lastPongTime,每次收到 pong 更新
  4. 超過 10 秒沒收到 pong 就關閉 socket 並重連
今天就做 在你手上一個用 WebSocket 的專案裡加上 client 端 heartbeat:每 5 秒 send('ping'),10 秒沒收到回應就重連
第 2 段 → 問題一:server 崩潰時 client 不知道
替即時功能設計一個 SSE fallback 的熔斷門檻
  1. server 開一個 SSE 端點(GET 持續送 event-stream)
  2. client heartbeat 連續失敗達門檻時改用 EventSource 連 SSE 端點
  3. 把畫面狀態標成「走 fallback」
  4. 定期嘗試切回 WebSocket
今天就做 替你的即時功能選一個熔斷門檻(例如連續 3 次 heartbeat 失敗),並寫下失敗後要切到哪個 fallback
第 5 段 → 問題二:WebSocket 掛了改用 SSE 備援
寫一份 nginx 多台 WebSocket server 的負載均衡設定
  1. upstream 區塊加 least_conn
  2. 每台 server 設 max_fails 與 fail_timeout
  3. location /ws 用 proxy_pass 並加 Upgrade / Connection 標頭
  4. 需要 sticky session 時把 least_conn 換成 ip_hash
今天就做 寫一份 nginx upstream 設定:兩台 WebSocket server 用 least_conn 分流,各帶 max_fails=3 fail_timeout=10s
第 7 段 → 問題三:多台 server 前面放 load balancer

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

WebSocket 升級像先打總機轉接,接通後就是直線熱線
新 一開始經過 HTTP 交握,成功後同一條 TCP 連線就變成不用每次重來的雙向通道  ⇄  像 先打電話給總機請人轉接,接通後就是一條直線熱線,不用每次重撥、重講一次
第 1 段 → WebSocket 是什麼、兩種典型用法
least connection 像看哪個窗口排隊人少就去哪,不是固定輪流排
新 WebSocket 適合用 least connection:看現在誰身上開著的連線最少  ⇄  像 排隊時看哪個窗口人少就去哪個,而不是機械式輪流固定排某一個窗口
第 7 段 → 問題三:多台 server 前面放 load balancer
message broker 像快遞轉運中心,不是每站互相寄送
新 每台 server 只認得 broker 一個地方,新增 server 只要連上 broker 就自動加入廣播(N×1 而非 N×N)  ⇄  像 快遞不是每個站點互相寄送給彼此,而是都送到轉運中心再分送出去
第 9 段 → Broadcasting:用 RabbitMQ 跨 server 廣播

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

瀏覽器與 server 之間建立一次、之後雙方都能隨時互傳訊息的長連線
WebSocket
第 1 段 → WebSocket 是什麼、兩種典型用法 · WebSocket
client 定期送小訊息要求 server 回覆,用有沒有準時回判斷連線是否還活著
heartbeat
第 2 段 → 問題一:server 崩潰時 client 不知道 · heartbeat
server 透過一條持續開著的 HTTP 回應,單向不斷把事件推給瀏覽器
Server-Sent Events (SSE)
第 5 段 → 問題二:WebSocket 掛了改用 SSE 備援 · Server-Sent Events (SSE)
主要方法失敗到某個程度後,自動改用的次要方法
fallback
第 5 段 → 問題二:WebSocket 掛了改用 SSE 備援 · fallback
站在多台後端 server 前面,接收所有 client 請求並依策略分配給某一台
load balancer
第 7 段 → 問題三:多台 server 前面放 load balancer · load balancer
把新連線分給目前開著連線數最少的那台 server,適合長連線的負載特性
least connection
第 7 段 → 問題三:多台 server 前面放 load balancer · least connection
load balancer 記住某個 client 曾被分到哪台後端,之後請求盡量送回同一台
sticky session
第 8 段 → Sticky session:重連要回到同一台 · sticky session
一則訊息要送給所有連線中的 client,不只是某一個
broadcasting
第 9 段 → Broadcasting:用 RabbitMQ 跨 server 廣播 · broadcasting
站在多個服務之間,收下訊息並轉交給訂閱者的獨立元件
message broker
第 9 段 → Broadcasting:用 RabbitMQ 跨 server 廣播 · message broker

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

demo:把 server 的 pong 改成 pongo,client 誤判死亡觸發重連循環
client 只在收到字串 "pong" 時更新 lastPongTime,server 改回 "pongo" 後 10 秒沒等到真正的 pong,client 判定死亡並重連,但 server 一直回 pongo,於是進入連上→10秒→斷→重連的循環
證明 heartbeat
演練 這個 demo 證明 heartbeat 偵測的其實是什麼(不只是連線層斷線)?重連能不能解決 server 邏輯壞掉這種情況?
第 4 段 → Demo:把 pong 改成 Pongo 觸發重連
fallback demo:Failed Attempts 數到 3 後印出 Switching to SSE fallback
console 依序印 WebSocket Connected → Received: pongo → No pong received in 10s → Failed Attempts 數到 3 → Switching to SSE fallback,畫面變成橘點 Connected via SSE
證明 fallback
演練 這個 demo 裡 SSE 為什麼還連得上(server 整體活著只是 WebSocket 回應壞掉)?如果 server 整台死掉,SSE 還會連得上嗎?
第 6 段 → SSE 程式碼與 fallback demo
nginx 設定:least_conn 加 max_fails=3 fail_timeout=10s
upstream websocket_servers { least_conn; server ... max_fails=3 fail_timeout=10s; ... } location /ws 用 proxy_pass 轉發,並設 Upgrade / Connection 標頭
證明 load balancer
演練 max_fails=3 fail_timeout=10s 的實際語意是什麼?為什麼這個設定要特別加 Upgrade / Connection 標頭?
第 7 段 → 問題三:多台 server 前面放 load balancer
程式碼陷阱:兩台 server 共用同一個具名 queue,其實是工作佇列不是廣播
截圖裡兩台 server 都 consume 同一個 broadcast_queue,在 RabbitMQ 裡這是工作佇列語意——每則訊息只會被其中一個 consumer 拿走,不是每台都收到;真正要廣播需要 fanout exchange,每台各自宣告專屬 queue 綁上去
證明 message broker
演練 為什麼這份範例程式碼其實做不到真正的廣播?要改成 fanout exchange 需要改動哪兩個地方?
第 9 段 → Broadcasting:用 RabbitMQ 跨 server 廣播

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

WebSocket 怎麼從 HTTP 升級來的
WebSocket 連線一開始是什麼?升級成雙向通道的關鍵是什麼?
一開始是普通 HTTP request(帶 Upgrade 標頭),server 同意後同一條 TCP 連線就升級成雙向通道,之後不再有 request/response 邊界
第 1 段 → WebSocket 是什麼、兩種典型用法
half-open 連線與 TCP keepalive
server 崩潰時 client 的 socket 為什麼還看起來「開著」?
正常關閉要交換 Close 幀、TCP 送 FIN,崩潰時都不會發生;TCP 內建的 keepalive 預設常要兩小時才起作用,這種狀態叫 half-open
第 2 段 → 問題一:server 崩潰時 client 不知道
health check endpoint 的兩步流程
client 重連前為什麼先打 health check?
先用普通 HTTP fetch /health-check 確認 server 活著,成功才真的開 WebSocket,比盲目重開 socket 省資源
第 3 段 → Heartbeat 程式碼:server 與 client
fault tolerant 定義與 2:1 比例
fault tolerant 是什麼?ping 5 秒一次、pong 門檻 10 秒這個比例的用意?
某部分出錯時整體仍能繼續運作或自行恢復;連續錯過兩次 pong 才重連,避免單次網路抖動被誤判
第 4 段 → Demo:把 pong 改成 Pongo 觸發重連
EventSource 內建自動重連
EventSource 相較 WebSocket 多了什麼內建功能?
斷線後預設約 3 秒自動重試,還會帶 Last-Event-ID 讓 server 補送漏掉的事件,這些 WebSocket 都要自己手寫
第 5 段 → 問題二:WebSocket 掛了改用 SSE 備援
為什麼要 proxy_http_version 1.1 加 Upgrade/Connection 標頭
nginx 轉發 WebSocket 為什麼需要 proxy_http_version 1.1 與 Upgrade/Connection 標頭?
WebSocket 交握需要 HTTP/1.1 的 Upgrade 機制;nginx 預設用 1.0 轉發且不轉這兩個 hop-by-hop 標頭,少了它們連不上
第 7 段 → 問題三:多台 server 前面放 load balancer
nginx ip_hash 實際機制(修正 cookie 的誤解)
nginx 的 ip_hash 是靠 cookie 記住 client 嗎?
不是,ip_hash 直接拿 client IP(IPv4 取前三個 octet)算 hash 決定後端,client 不需要記任何東西;弱點是同一 NAT 後的使用者會被分到同一台
第 8 段 → Sticky session:重連要回到同一台
AMQP 的定義
AMQP 是什麼?
RabbitMQ 使用的開放式訊息協定,定義了 connection、channel、exchange、queue 等概念與線上格式
第 9 段 → Broadcasting:用 RabbitMQ 跨 server 廣播

Vapor Mode is the Future of Vue

LearnVue 歷史參考(已過時) 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

用 playground 對自己專案的熱點元件試切 Vapor
  1. 找出 app 裡真正的效能熱點(大型清單、頻繁更新的表格、動畫密集介面)
  2. 確認該元件沒有依賴第三方 virtual DOM 元件庫
  3. 打開 Vue Vapor playground,貼上該元件切 VAPOR ON/OFF 比較輸出
  4. 先只在單一元件 opt-in,不影響其他部分
今天就做 打開 Vue Vapor playground,把你專案裡最常更新的一個元件貼進去,切 VAPOR ON/OFF 比較兩邊輸出
第 7 段 → 整合計畫、限制與結語
手繪一次自己寫過的元件的 track/trigger 渲染迴圈
  1. 挑一個你寫過的 Vue 元件
  2. 找出它讀了哪些 reactive data(track 的對象)
  3. 想一下資料變動時哪條邊會 trigger 重跑
  4. 對照編譯輸出,看哪部分被 static hoisting 提出去
今天就做 挑一個你寫過的 Vue 元件,手繪一次 track/trigger 的渲染循環圖
第 2 段 → Vue 現在怎麼運作:vdom 全迴圈

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

Vue 一直知道該重畫哪個元件,只是不知道元件裡哪一塊要重畫
新 reactive data 透過 track/trigger 通知的單位是整個元件的 render function  ⇄  像 知道信件要送到哪一棟大樓,但不知道要送到哪一戶
第 2 段 → Vue 現在怎麼運作:vdom 全迴圈
訂閱粒度從整份元件縮小到單一 DOM 節點
新 Vapor 只是把 reactivity 的訂閱單位從元件換成單一節點,機制沒變  ⇄  像 從訂閱整份報紙縮小到只訂閱其中一個版面
第 5 段 → 做得到的原因:reactivity system
以元件為單位逐步啟用,不是像 Vue 2→3 整包換過去
新 opt-in、以檔案為單位啟用、可自由混用 Vapor 與非 Vapor 元件  ⇄  像 逐間房間裝修,不是把整棟房子搬空重建
第 7 段 → 整合計畫、限制與結語

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

Vue 的實驗性編譯策略:同一份 template 編譯成不經 virtual DOM 的程式碼
Vapor Mode
第 1 段 → Vapor Mode 的承諾 · Vapor Mode
template 編譯成 render function,資料變動觸發重跑、新舊樹比對後套用到真實 DOM
virtual DOM
第 2 段 → Vue 現在怎麼運作:vdom 全迴圈 · virtual DOM
template 編譯後得到的函式,執行它會產生當下該有的 virtual DOM tree
render function
第 2 段 → Vue 現在怎麼運作:vdom 全迴圈 · render function
編譯器把模板中永遠不變的片段提到 render function 外面,只建立一次
static hoisting
第 3 段 → 編譯期最佳化:static hoisting · static hoisting
為了維持 virtual DOM 這層中介必須付出、無法被編譯期最佳化消除的固定成本
vdom overhead
第 4 段 → vdom 的代價,與拿掉它的念頭 · vdom overhead
資料變動時只更新真正受影響的那一個 DOM 節點,不重算也不比對其他部分
fine-grained update
第 5 段 → 做得到的原因:reactivity system · fine-grained update
Vue 追蹤誰讀了資料、資料變動時通知它們的整套機制
reactivity system
第 5 段 → 做得到的原因:reactivity system · reactivity system
預設不啟用、以元件為單位主動選擇才生效,不影響既有程式碼
opt-in
第 7 段 → 整合計畫、限制與結語 · opt-in

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

白板圖:五方塊直線加 Reactive Data 用 track/trigger 接回形成迴圈
Template →compile→ Render Function →returns→ Virtual Dom Tree →mount/patch→ Actual DOM,Reactive Data 在下方用 trigger 與 track 兩條邊接回 Render Function
證明 virtual DOM
演練 這張圖裡哪一步是「重跑」、哪一步是「比對」、哪一步是「套用」?拿掉 virtual DOM 之後這張圖會少哪一塊?
第 2 段 → Vue 現在怎麼運作:vdom 全迴圈
節點樹範例:動態 {{val}} 標綠色,靜態 "hi" 整支框起來只建一次
根節點 div 分兩支:左邊 p→span→{{val}}(綠色代表動態),右邊 div→"hi"(被框起來代表 hoist 出去)
證明 static hoisting
演練 這棵樹在編譯完成那一刻就被塗上顏色了,這證明編譯器比 runtime 多知道什麼?你的專案裡哪些片段屬於「死的」那一半?
第 3 段 → 編譯期最佳化:static hoisting
before/after 兩張圖:Virtual Dom Tree 方塊整個從圖上消失
before 圖:Virtual Dom Tree 方塊頭上多了時鐘(走訪時間)與磁碟(記憶體)圖示;after 圖:該方塊消失,只剩 Render Function→mount→Actual DOM 與 Reactive Data→patch(虛線)→Actual DOM
證明 vdom overhead
演練 mount 這一步為什麼在 after 圖裡還在?被拿掉的具體是「之後每一次更新」的哪個環節?
第 4 段 → vdom 的代價,與拿掉它的念頭
Playground 對照:關閉 Vapor 時 setup 回傳 render function 與 createElementVNode
同一份 App.vue 切 VAPOR OFF/ON:關閉時輸出 setup(__props){...} return (_ctx,_cache)=>{...} 與 _createElementVNode(...),涵蓋 v-if、v-model、事件、動態文字四種動態
證明 Vapor Mode
演練 關閉與打開 Vapor 的輸出差在哪一層(render function 產生的東西 vs 直接操作 DOM)?這四種動態各自對應 Vapor 輸出裡的哪個機制?
第 6 段 → Playground 對照:關掉與打開 Vapor

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

compilation strategy 與 Solid JS
為什麼作者說 Vapor 是 compilation strategy 而不是新 API?Solid JS 跟它的關係?
改的是同一份 template 被編譯成什麼 JS,原始碼不變、輸出換一套;Solid JS 是第一天就不用 virtual DOM 的先例,靠編譯直接產生 DOM 操作
第 1 段 → Vapor Mode 的承諾
track 與 trigger 分別做什麼
白板圖上 track 與 trigger 兩條邊分別做什麼?
track:render function 執行時記錄讀到了哪些 reactive data;trigger:那些被讀過的資料變動時通知重跑
第 2 段 → Vue 現在怎麼運作:vdom 全迴圈
static hoisting 省的是哪一側的成本
static hoisting 省掉的是真實 DOM 元素還是別的?
省的是 JavaScript 那邊的 virtual node「分身」重建與比對的成本,真實 DOM 元素一樣存在、一樣佔記憶體
第 3 段 → 編譯期最佳化:static hoisting
DOM command 的定義
DOM command 指什麼?
瀏覽器提供的、直接修改頁面的原生操作,例如設定文字內容、新增或移除元素
第 4 段 → vdom 的代價,與拿掉它的念頭
ref 與 effect 的定義
ref 和 effect 各自是什麼?
ref 是最基本的響應式容器,用 .value 讀寫並被追蹤;effect 是會被自動記錄依賴的函式,依賴改變就重新執行
第 5 段 → 做得到的原因:reactivity system
patch flag 的定義
patch flag 是什麼?
編譯器寫在 v-node 上的數字標記,告訴 runtime 這個節點只有哪一類內容會變
第 6 段 → Playground 對照:關掉與打開 Vapor
renderEffect/event delegation/template cloning 各做什麼
Vapor 輸出裡 renderEffect、event delegation、template cloning 各自解決什麼?
renderEffect 把一個 DOM 更新直接綁到 reactive data;event delegation 把監聽器綁在共同祖先而非每個元素;template cloning 用 clone 一份現成模板取代逐個建立元素
第 6 段 → Playground 對照:關掉與打開 Vapor
Composition API/Options API/script setup 的差別
Composition API、Options API、script setup 各自是什麼寫法?
Options API 用 data/methods/computed 選項配 this;Composition API 在 setup 裡用 ref/computed 組邏輯;script setup 是編譯期語法糖,讓 script 區塊本身就是 setup
第 7 段 → 整合計畫、限制與結語

The top 1% Think on Paper. Here’s How To Do It.

Justin Sung 學習方法 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

把筆記濃縮成只有自己看得懂的關鍵字
  1. 不寫完整句子,只留關鍵字
  2. 濃縮到只有自己看得懂就好,不求美觀
  3. 把查閱用的完整資訊交給原始材料或 AI
今天就做 把手上一份筆記的其中一段,改寫成只剩 3-5 個關鍵字,5 分鐘後測試自己還記不記得原意
第 8 段 → 縮短筆記的具體做法
讀到覺得亂或有錯時,另拿白紙整個重畫而不是局部塗改
  1. 讀15-20分鐘後停下來看整張圖
  2. 檢查有沒有連結或分組跟現在的理解對不上
  3. 開一張新白紙重畫整個地圖,不要在舊圖上局部塗改
  4. 之後越讀越亂就再重做一次
今天就做 下次讀書覺得筆記亂或有錯時,另拿一張白紙把整個地圖重畫一次,不要局部塗改
第 10 段 → 重整:regroup、rearrange、reconnect

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

把想法全寫下來像跑到一半累了跳上車開到終點
新 逐字全寫下來會繞過大腦的組織處理,資訊變不成知識  ⇄  像 為了健康去跑步,跑累了跳上車開到終點,不累了但失去意義
第 2 段 → 不在紙上思考會遇到的兩個問題
20片拼圖先攤開看圖案,不是一片一片試著拼
新 讀新資訊先把關鍵字放到紙上,此時還不知道怎麼連  ⇄  像 拼拼圖時先把碎片倒出來攤在桌上看圖案,而不是一片一片硬拼
第 4 段 → 原則一:Make it wrong
筆記寫完會不會回頭重整,決定它是工作區還是垃圾桶
新 寫完就不再動它的筆記,跟寫完會回來重整的筆記,效果完全不同  ⇄  像 東西丟進垃圾桶就不會再打開來看
第 10 段 → 重整:regroup、rearrange、reconnect

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

把腦中正在處理的想法即時放到紙上,用紙面當工作區
think on paper
第 1 段 → 為什麼要在紙上思考 · think on paper
在紙上思考時不追求正確,先把想法放上紙再說
make it wrong
第 4 段 → 原則一:Make it wrong · make it wrong
因為想把每個決定都做對,反而卡在起點什麼都做不了
analysis paralysis
第 4 段 → 原則一:Make it wrong · analysis paralysis
先對某件事做過猜測,之後真正遇到時大腦處理得更快
priming
第 5 段 → 示範:先撒關鍵字,再亂猜連結 · priming
筆記只剩「幫大腦找模式」的功能,查閱功能外包給原始材料和 AI
make it shorter
第 7 段 → 原則二:Make it shorter 的理由 · make it shorter
看到就想起當時整個想法的極簡記號,本身不承載完整資訊
memory anchor
第 8 段 → 縮短筆記的具體做法 · memory anchor
把一次猜測與修正的結果固定成穩定記憶與理解的過程
consolidate
第 9 段 → 原則三:Make it again——變亂與發現錯誤 · consolidate
太亂或有錯時把想法整個重整:重新分組、排列、連線
reorganize
第 10 段 → 重整:regroup、rearrange、reconnect · reorganize
把腦中的東西倒到外部以減輕負荷,但之後不再處理它
mental offload
第 10 段 → 重整:regroup、rearrange、reconnect · mental offload

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

作者親身經歷:學習時間至少減半,商業金融AI都適用
醫學院中途發現此原則,之後用在商業、行銷、金融、AI/ML,過去要花幾天的問題現在一個下午能解決
證明 think on paper
演練 這段經歷證明了 think on paper 的什麼效果?你會用它挑戰你目前哪一種讀書方式?
第 1 段 → 為什麼要在紙上思考
示範:382秒黑板九個詞散亂無線,約讀了一頁半的量
encoding、retrieval、thalamus、cognitive load、memory、brain、explicit/working/long-term memory 九個詞散在黑板上,沒有任何線
證明 make it wrong
演練 這格畫面證明了 make it wrong 的哪個特徵(排版、正確性、完整度)?你會怎麼在自己的筆記上重現這一步?
第 5 段 → 示範:先撒關鍵字,再亂猜連結
示範:把三句 takeaway 縮成 FAST / EXAMINE / GUESS-CONNECT
716秒黑板寫「∅ full sentences ↳ key(words)」;787秒把三句完整 takeaway 用黃筆縮成三個詞
證明 memory anchor
演練 FAST / EXAMINE / GUESS-CONNECT 對別人來說沒資訊量,為什麼對寫的人足以喚回整句?
第 8 段 → 縮短筆記的具體做法
發現舊連結錯了:三種記憶不該被歸成互不相干的三類
把 explicit/working/long-term memory 歸成三個獨立 types 其實不合理,因為分類軸不同:working/long-term 按保存時間分,explicit 按能否被意識到分,可以重疊
證明 consolidate
演練 這個錯誤是怎麼被發現的?發現「筆記跟腦中理解對不上」這件事,證明了先前哪一步(猜測)的價值?
第 9 段 → 原則三:Make it again——變亂與發現錯誤
重整後:working memory 成中心,explicit memory 修回 long-term 底下
935秒的亂圖到1103秒重整版:working memory 圈成中心、encoding/retrieval 連到 long-term memory,explicit 移到 long-term memory 底下而非並列
證明 reorganize
演練 重整版跟原本亂圖比,哪裡的錯誤被修正了?作者說「沒學新東西,好處卻主要來自這裡」在講什麼機制?
第 10 段 → 重整:regroup、rearrange、reconnect

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

overwhelm 的定義
overwhelm 在文中指什麼?
資訊量超過腦袋能同時抓住的程度時產生的混亂與無力感
第 2 段 → 不在紙上思考會遇到的兩個問題
cognitive processes 的定義
cognitive processes 指什麼?
大腦把新資訊組織、處理,使之留下記憶並形成理解的心智運作
第 2 段 → 不在紙上思考會遇到的兩個問題
三個口訣分別對到哪個壞習慣
make it wrong / shorter / again 各自反對什麼本能寫法?
wrong 對「求正確」、shorter 對「求完整」、again 對「寫完就存」
第 3 段 → 三個原則總覽
keyword 的定義
文中 keyword 指什麼?
用一兩個詞代表正在吸收的一個想法,作為紙上的一個點
第 5 段 → 示範:先撒關鍵字,再亂猜連結
perfectionism 與 awareness 的定義
perfectionism 與 awareness 在原則一裡各指什麼?
perfectionism 是因為想做到正確完美而不敢動筆的傾向;awareness 是即時注意到自己正被這種傾向牽著走的能力
第 6 段 → 原則一的 takeaway 與完美主義警覺
reference notes 的定義
reference notes 指哪種筆記?
為了日後查閱而寫的筆記,功能是儲存資訊而不是幫助當下理解
第 7 段 → 原則二:Make it shorter 的理由
筆記字數與理解的關係,更精確的說法
「筆記字數越多理解越差」這句話精確的版本是什麼?
逐字抄寫式的長筆記才跟理解較差有關;關鍵變因是有沒有經過自己改寫整理,不是單純字數多寡
第 8 段 → 縮短筆記的具體做法
explicit memory 的定義
explicit memory 指什麼?
能被有意識回想、用語言說出來的記憶(事實、事件),相對於內隱記憶
第 9 段 → 原則三:Make it again——變亂與發現錯誤
三個適用情境對回「學得快」的三個定義
讀書、開會、獨自思考三種情境各對回第1段哪個定義?
密集讀書對應留得住、忙碌會議對應做決策、獨自思考對應解問題
第 11 段 → 適用範圍與收尾

The 3 Skills That Separate Architects From Senior Developers

The Serious CTO 工程職涯與架構角色 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

把技術提案翻成成本/風險/價值/工程現實四句話
  1. 寫一句成本:這要花多少、何時花
  2. 寫一句風險:最壞會發生什麼、機率多大
  3. 寫一句價值:使用者或營收會怎麼變
  4. 寫一句工程現實:現有人力與系統撐不撐得住
今天就做 把你最近一個技術提案,用成本、風險、價值、工程現實四個角度各寫一句話
第 5 段 → 把技術主張換算成商業帳
幫現有系統寫一句最小可行的技術願景
  1. 寫一句現在在哪:現況架構與痛點
  2. 寫一句兩三年後在哪:目標狀態
  3. 列出可以先做、可獨立驗證的第一小步
  4. 確認每一步系統都能持續運作(strangler fig 的做法)
今天就做 為你正在維護的系統寫一句技術願景:現在在哪、兩三年後要在哪
第 7 段 → 技術願景要附一條路:Netflix 花了七年
三條「在……之前,先……」規則,把技能變成立刻能做的動作
  1. 提任何解法之前,先寫下 trade-off
  2. 跟別的團隊爭論之前,先把論點翻成成本、風險、價值
  3. 第五次修同一個耦合之前,先問是什麼結構一直在重新製造它
今天就做 本週提技術方案前,先寫一段 trade-off 記錄:選了什麼、放棄什麼、代價是什麼
第 8 段 → 實務規則:改變你的產出

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

「決定少但槓桿高」像立法者訂規則,不像法官逐案判決
新 架構師的一次決定會被重複套用幾百次,錯誤也會被放大  ⇄  像 立法者訂一條規則影響全體人民;法官逐案判決一次只影響一案
第 3 段 → 「比較好」要先問:對什麼比較好?
改造成耦合的條件,像治水改河道而不是逐次清淤
新 structural design 改的是邊界、所有權與溝通方式,讓亂改別人程式不再是最省事的選項  ⇄  像 治水改河道從根本解決淹水;逐次清淤只解決這次淹水,下次還會淹
第 6 段 → 技能三:結構設計是改掉產生耦合的條件
寫程式回饋以分鐘計,架構決定回饋以季年計
新 架構決定的回饋週期長,中間一大段時間沒有訊號能確認做對了  ⇄  像 種樹要等季節才看得到成果,跟按鈕按下去馬上有反應不同
第 9 段 → 身分的轉換:不以程式為中心

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

架構師的產出是限制與決策,不是解法本身
architect
第 1 段 → 架構師是另一種工作,不是升級版資深工程師 · architect
架構是「帶著後果的 trade-off 記帳」:每個好處都要標上代價
trade-off accounting
第 2 段 → 技能一:系統性思考與微服務陷阱 · trade-off accounting
先問「對什麼比較好」,再從系統屬性排出現在最缺哪一個
quality attribute
第 3 段 → 「比較好」要先問:對什麼比較好? · quality attribute
把同一個技術方向用成本、風險、價值、工程現實各講一次
organizational influence
第 4 段 → 技能二:組織影響力不是當翻譯機 · organizational influence
把技術主張換算成財務主管讀得懂的成本/效益加減式
business case
第 5 段 → 把技術主張換算成商業帳 · business case
結構設計改的是製造耦合的條件,不是逐一修掉耦合本身
structural design
第 6 段 → 技能三:結構設計是改掉產生耦合的條件 · structural design
領域驅動設計的邊界單位:邊界內同一個詞只有一種意思
bounded context
第 6 段 → 技能三:結構設計是改掉產生耦合的條件 · bounded context
技術願景要回答現在在哪、兩三年後在哪、怎麼不停業走過去
technical vision
第 7 段 → 技術願景要附一條路:Netflix 花了七年 · technical vision
從現況走到目標架構的分段步驟,每步都能在系統運作中完成
migration path
第 7 段 → 技術願景要附一條路:Netflix 花了七年 · migration path

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

微服務代價三數字:MTTR 15→90分、40%跨服務呼叫、18個月工時
故障復原從 15 分鐘變 90 分鐘;40% 使用者操作變跨服務呼叫;投入 18 個月工程時間
證明 trade-off accounting
演練 這三個數字各屬於哪種代價(營運/設計/機會成本)?你會怎麼用它們說服團隊先算帳再拆服務?
第 2 段 → 技能一:系統性思考與微服務陷阱
案例算完其實淨值 -20萬/年,數字比作者呈現的更值得懷疑
現有基礎建設 300萬美金/8FTE;新架構 420萬美金/5.5FTE,省下2.5FTE價值約100萬,但成本多120萬,淨值 -20萬
證明 business case
演練 這筆帳的淨值其實是負的,證明了商業論證要包含什麼(時間軸/敏感度)?你會怎麼補救這個提案?
第 5 段 → 把技術主張換算成商業帳
10人長到80人、沒架構指引,變成 big ball of mud
公司從 10 個工程師成長到 80 個、中間沒有架構指引,最後每個團隊都在動別人的程式
證明 structural design
演練 人數從10長到80為何會自動製造耦合?你的團隊現在有沒有類似的「直接改別人程式最省事」的地方?
第 6 段 → 技能三:結構設計是改掉產生耦合的條件
Netflix 微服務遷移花了七年,且先備妥三個前置元件
Netflix 遷移花 7 年,願景包含前置條件:circuit breaker、service discovery、API gateway
證明 migration path
演練 Netflix 花七年證明了遷移路徑的什麼特性?那三個前置元件各解決「新舊並存」的哪個問題?
第 7 段 → 技術願景要附一條路:Netflix 花了七年

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

leverage 在本片指什麼
leverage 在文中指什麼?
一次決定能影響到的人數或情境數,決定少但槓桿高
第 1 段 → 架構師是另一種工作,不是升級版資深工程師
MTTR 的定義
MTTR 是什麼縮寫?代表什麼?
平均修復時間,衡量故障發生到恢復服務要多久
第 2 段 → 技能一:系統性思考與微服務陷阱
on-call 為何是架構圖上看不到的成本
為什麼 on-call 團隊的負擔不會出現在架構圖上?
圖上畫得越乾淨的設計,往往把複雜度推給了值班的人
第 3 段 → 「比較好」要先問:對什麼比較好?
stakeholder 在本片指誰
影片裡 stakeholder 包含哪些角色?
會被技術決定影響或有否決權的人:工程、產品、營運、財務、高層
第 4 段 → 技能二:組織影響力不是當翻譯機
FTE 的定義
FTE 是什麼縮寫?
Full-Time Equivalent,換算成相當於幾個全職人力的計量單位
第 5 段 → 把技術主張換算成商業帳
cloud-native 的定義
cloud-native 架構的特徵是什麼?
以雲端環境為前提設計,倚賴容器、託管服務與彈性伸縮,而非自建機房
第 5 段 → 把技術主張換算成商業帳
Conway's law 的定義
Conway's law 說的是什麼?
系統的結構會複製設計它的組織的溝通結構
第 6 段 → 技能三:結構設計是改掉產生耦合的條件
三個前置元件各做什麼
circuit breaker、service discovery、API gateway 在遷移期間各做什麼?
circuit breaker 擋故障擴散;service discovery 讓服務找到彼此;API gateway 統一入口決定流量走向
第 7 段 → 技術願景要附一條路:Netflix 花了七年
Architecture Decision Record 是什麼
ADR 記錄什麼?通常放在哪?
一個架構決定的背景、選項、決定與後果,通常隨程式碼一起版控
第 8 段 → 實務規則:改變你的產出
enabling team 與 ambiguity 的定義
enabling team 是什麼?架構工作的 ambiguity 指什麼?
enabling team 不直接交付產品、靠提升其他團隊能力衡量價值;ambiguity 是問題沒有明確定義、也沒有唯一正確答案
第 9 段 → 身分的轉換:不以程式為中心

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

回想這週用 AI coding agent 時切換終端機/分支的次數與耗時
  1. 回想這週用過幾次不同的 AI coding agent
  2. 估算每次為了比較結果而切換終端機或 Git 分支花了多少時間
  3. 加總這週花在『切換與比對』上的總時間
  4. 判斷這個時間值不值得換一套編排工具
今天就做 回想這週切換 AI 終端機/分支的次數,估算總共花費的時間(約 10 分鐘)
第 1 段 → 開場:AI 工具越多越混亂
自己動手用 git worktree 跑兩個方案,體會 Orca 自動化的是什麼
  1. 在一個小專案裡用 git worktree add 開第二個工作目錄
  2. 在原目錄跑一個改法,在新 worktree 跑另一個改法
  3. 用 git diff 比較兩邊的改動
  4. 決定要合併哪一個
今天就做 在自己一個小專案手動開兩個 git worktree,各跑一種改法,比較 diff(約 25 分鐘)
第 4 段 → 技術核心:git worktree 隔離

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

git worktree 像大樓裡每個代理各有一張獨立辦公桌,共用同一棟樓
新 git worktree:同一個倉庫可以同時擁有多個工作目錄,共用同一個 .git 物件庫  ⇄  像 同一棟大樓裡每個人有自己獨立的辦公桌,但共用整棟樓的水電基礎設施
第 4 段 → 技術核心:git worktree 隔離
node-pty 讓代理誤以為自己在真的終端機裡,像飛行模擬器騙過機師的反應
新 node-pty 提供偽終端,代理讀到的所有行為都跟手動在 Terminal 開它一模一樣  ⇄  像 飛行模擬器:讓機師的操作與反應跟真的開飛機時一模一樣,才練得出真實技能
第 5 段 → node-pty 虛擬終端與 WebGL
Orca 不跟模型公司拼 AI 本身,改做競技場與裁判系統,像賽事主辦方
新 Orca 的定位:不參與模型競賽,只提供讓各家代理比賽與被評判的場地  ⇄  像 運動賽事的主辦方:自己不參賽,只提供場地、規則與裁判,讓選手一較高下
第 9 段 → 總結:管理 AI 複雜性的平台

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

ADE:整合多個會自己動手的代理,重心是派工、隔離、觀察與比較,不是編輯程式碼
Agentic Development Environment (ADE)
第 2 段 → Orca 的定位:ADE · Agentic Development Environment (ADE)
Orchestration:不自己造 AI,只做編排,任何新代理都能接進來但受制於外部 CLI
orchestration
第 3 段 → 痛點:多工具讓工作流支離破碎 · orchestration
Git worktree:同一倉庫同時有多個工作目錄,各自 checkout 不同分支不用切換
git worktree
第 4 段 → 技術核心:git worktree 隔離 · git worktree
Node-pty:給每個代理一個偽終端,代理誤以為自己在真的命令列裡執行
node-pty
第 5 段 → node-pty 虛擬終端與 WebGL · node-pty
Design Mode:點選頁面元素連同白話指令一起塞進 prompt,代理才知道要改哪裡
Design Mode
第 6 段 → 適合誰:三個使用場景 · Design Mode
Over-engineering:只用一個 AI 助手時,編排層沒有意義,是多花的成本
over-engineering
第 7 段 → 社群評價與質疑 · over-engineering
Computer Use:讓代理看螢幕、動滑鼠鍵盤操作任意桌面程式,碰的是整台電腦
Computer Use
第 8 段 → 角色轉變與潛在風險 · Computer Use
Sandbox:把 Computer Use 的操作限制在隔離環境,否則代理誤判會刪錯東西
sandbox
第 8 段 → 角色轉變與潛在風險 · sandbox
Multi-agent workflow:多代理協同會不會成為主流,決定 Orca 是典範還是過渡工具
multi-agent workflow
第 9 段 → 總結:管理 AI 複雜性的平台 · multi-agent workflow
Git branch:傳統一次只能 checkout 一條分支,換代理就得 stash、切分支重跑
Git branch
第 1 段 → 開場:AI 工具越多越混亂 · Git branch

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

GitHub About 欄:用你自己的訂閱跑任何代理,支援桌面、手機、VPS,42.8k 星
“Run any coding agent with your own subscription. Available on desktop, mobile and VPS.”——Orca 不賣模型,接的是使用者已有的 Claude Code、Codex 等帳號
證明 Agentic Development Environment (ADE)
演練 這句官方自述補了作者沒提的哪兩個重點?如果 Orca 要賣自己的模型,還算 ADE 嗎?
第 2 段 → Orca 的定位:ADE
架構圖:Git Worktree Manager 直接對接 File System,與 System Operations 並排
主行程的 Agent Orchestrator 對接 Git Worktree Manager、System Operations (SSH/Files)、Communication Service,前端是 xterm.js + WebGL 的 Terminal Panel
證明 git worktree
演練 從這張架構圖看,worktree 的管理是誰在做決定?為什麼要跟 SSH/Files 並排?
第 4 段 → 技術核心:git worktree 隔離
CLI 代理是互動式程式:偵測 stdout 是不是 TTY,才決定要不要開彩色輸出與進度動畫
只用管線接 stdin/stdout(child_process)很多代理會退化成非互動模式甚至直接拒絕跑,這是必須用虛擬終端機的技術前提
證明 node-pty
演練 為什麼不能直接用 child_process 把代理跑起來?TTY 這個判斷點決定了代理的什麼行為?
第 5 段 → node-pty 虛擬終端與 WebGL
Design Mode 把選取元素的標籤、class、樣式連同白話指令一起塞進 prompt
解決的是 AI 改 UI 最常見的失敗原因——描述不清楚要改哪裡;跟 Chrome DevTools『檢查元素』抓到的資訊相同,只是自動送給代理
證明 Design Mode
演練 這個機制解決的是 AI 改 UI 最常見的哪種失敗?跟你自己用文字描述『把按鈕改藍色』有什麼差別?
第 6 段 → 適合誰:三個使用場景
社群反面意見:只用一個助手的人覺得 Orca 是過度工程
編排層的價值來自『執行者很多』,只有一個代理時編排本身就沒有意義;這不是 Orca 的缺點,而是它的前提條件
證明 over-engineering
演練 判斷自己需不需要編排工具,只要問哪一個問題?你自己現在的情況符合嗎?
第 7 段 → 社群評價與質疑
GitHub 提交紀錄:fix(claude): make managed hook paths portable
這類提交是 Orca 持續追著上游 CLI 變動修的痕跡;這些外部 CLI 沒有對 Orca 承諾任何穩定 API,改個提示文字就可能讓整合壞掉
證明 orchestration
演練 這條提交記錄證明了 Orca 的哪個弱點?如果你要評估要不要導入類似工具,這算不算一個持續成本?
第 8 段 → 角色轉變與潛在風險

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

多代理切換成本的根因
為什麼多個 AI 代理同時工作會逼你在 Git 分支間切換?
每個代理都直接改動工作目錄的檔案,兩個代理同時改同一份會互相覆蓋,只能一次一個代理在一個分支上工作
第 1 段 → 開場:AI 工具越多越混亂 · AI coding agent
git diff 能告訴你什麼、不能告訴你什麼
git diff 比較的是什麼?它會不會告訴你哪個方案比較好?
只比較檔案改了什麼;好壞仍要人跑測試、讀程式碼判斷,diff 只是讓比較『看得清楚』不是『自動評分』
第 4 段 → 技術核心:git worktree 隔離 · git diff Code Review
WebGL 渲染器為什麼比較不容易卡
多個代理同時狂噴 log 時,為什麼用 WebGL 畫終端機字元比較不會卡?
預設用 DOM/canvas 畫字元,每行都是 DOM 操作;WebGL 把字元當紋理交給 GPU 畫,CPU 只管資料不管畫圖
第 5 段 → node-pty 虛擬終端與 WebGL · xterm.js WebGL
SSH 模式下本機 Orca 只是介面
SSH 模式下 worktree 和代理行程實際跑在哪裡?手機 App 怎麼連回去?
都在遠端伺服器上,本機只是介面;手機 App 透過 Communication Service 連回桌面端,桌面電腦得開著才能用
第 6 段 → 適合誰:三個使用場景 · SSH Mobile Companion App
Electron 為什麼吃資源
同時跑多個代理時,Electron 這個技術選型的資源代價來自哪裡?
每個 App 內含一份 Chromium 與 Node.js 的固定成本;再加上每個代理各一個 node-pty 行程與 WebGL 面板,N 個代理就是 N 倍
第 7 段 → 社群評價與質疑 · Electron
Computer Use 跟 worktree 隔離完全不同層次
worktree 隔離的是什麼?Computer Use 能碰到的又是什麼?
worktree 只隔離『檔案』;Computer Use 能碰到『整台電腦』,包括瀏覽器裡登入中的帳號、密碼管理員、其他 App
第 8 段 → 角色轉變與潛在風險 · breaking change

Frontend System Design at Scale (The Senior Dev Playbook)

Monsterlessons Academy 前端底層與系統設計 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

把自己專案裡最大的一個路由改成動態 import
  1. 找出打包分析裡體積最大的一個路由或頁面元件
  2. 把它的 import 改成 React.lazy(() => import(...))
  3. 重新 build,確認它被切成獨立的 chunk
今天就做 挑自己專案最大的一個路由元件,改成動態 import 並確認生出獨立 chunk(約 15 分鐘)
第 3 段 → Bundling:把大檔切成 chunk
檢查自己專案有沒有把 API 資料錯塞進全域 store
  1. 列出目前放在 Redux/Zustand 裡的資料
  2. 逐項判斷:真相在伺服器上還是只存在這個分頁裡
  3. 屬於 server state 的部分規劃改用 React Query/SWR 管理
今天就做 檢查自己專案的全域 store,列出哪些其實是 server state(約 15 分鐘)
第 9 段 → State management:三種 state
替專案裡一個容易出錯的區塊加上 ErrorBoundary
  1. 挑一個獨立、可能出錯的區塊(例如第三方元件或圖表)
  2. 包上 ErrorBoundary,寫一句清楚的 fallback 文案
  3. 在 componentDidCatch 裡把錯誤連同 userId、route 送出
今天就做 挑自己專案一個獨立區塊包上 ErrorBoundary 並加上錯誤上報(約 20 分鐘)
第 12 段 → 可靠性:不要整頁白畫面

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

module federation 像不打包的相依:host 只記路徑,執行期才去抓
新 module federation:建置時不打包 cart 的程式碼,只記下去哪個 URL 拿  ⇄  像 一般的 npm 相依:build time 就把套件內容整個打包進最終檔案
第 6 段 → Module federation:各自部署
server state 就像快取:本地存的是可能過期的副本,真相在遠端
新 server state:手上這份 API 資料隨時可能跟伺服器不同步  ⇄  像 快取(cache):本地存的是可能過期的副本,真相永遠在遠端那一份
第 9 段 → State management:三種 state
每個 micro-frontend 包一層 ErrorBoundary,像電路的保險絲
新 各片段各包一層 ErrorBoundary:一塊壞了,其他片段照常運作  ⇄  像 電路的保險絲:局部過載時自己斷開,不影響其他電路
第 12 段 → 可靠性:不要整頁白畫面

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

Code splitting:用動態 import 把大 bundle 切成獨立 chunk,每頁只載要用的那塊
code splitting
第 3 段 → Bundling:把大檔切成 chunk · code splitting
Micro-frontend:把前端拆給不同團隊各自開發,是組織問題的技術解法
micro-frontend
第 4 段 → Micro-frontend:切團隊也切複雜度 · micro-frontend
Module federation:host 只記路徑,執行期才去別的團隊抓最新版模組
module federation
第 6 段 → Module federation:各自部署 · module federation
Cache invalidation:帶內容雜湊的檔案長快取,指路的入口檔要短快取
cache invalidation
第 7 段 → CDN 與快取:距離就是延遲 · cache invalidation
Tree shaking:砍掉沒用到的函式,但 import 寫錯就整包被拉進來、無警告
tree shaking
第 8 段 → Bundle 最佳化與瀏覽器載入順序 · tree shaking
Server state:手上永遠只是伺服器資料的一份副本,可能過期
server state
第 9 段 → State management:三種 state · server state
Event replay:斷線重連補回漏掉的事件,適合每個事件本身有意義的場景
event replay
第 10 段 → 即時同步與斷線後怎麼辦 · event replay
Feature flag:讓部署與發布脫鉤,程式碼先合併但對使用者關著
feature flag
第 11 段 → CI、feature flag 與 A/B 測試 · feature flag
Error boundary:攔截渲染錯誤切到 fallback,讓一塊壞掉不拖垮整頁
error boundary
第 12 段 → 可靠性:不要整頁白畫面 · error boundary
Constraints:先問規模、現況與真正要解的問題,才有依據做每個技術選擇
constraints
第 13 段 → 先問限制,再提方案 · constraints

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

React.lazy(() => import('./Dashboard')) 讓 Dashboard 被單獨切成一個檔案
動態 import 在建置階段就是給打包工具的切割記號,Vite/webpack 看到就單獨輸出;真正走到那一行才發請求下載
證明 code splitting
演練 切割發生在建置期還是執行期?決定要不要載又發生在哪一期?
第 3 段 → Bundling:把大檔切成 chunk
webpack remotes 設定:cart: 'cart@https://cdn.team-b.com/remoteEntry.js'
host 建置時不打包 cart 程式碼;B 團隊重新部署、覆蓋掉這個 URL,host 下次載入就自動拿到新版,不必重 build
證明 module federation
演練 如果 team-b 的 remoteEntry.js 版本跟 host 期待的不相容,會發生什麼?
第 6 段 → Module federation:各自部署
chunk 檔名帶內容雜湊可設超長快取,index.html 與 remoteEntry.js 要設極短快取
內容一變檔名就變,所以帶雜湊的檔案能安全長快取;指路的入口檔案版本不變但內容常變,長快取會讓使用者拿舊地圖找新檔案
證明 cache invalidation
演練 為什麼這兩類檔案要用完全相反的快取策略?你的專案有沒有把入口檔也設了長快取?
第 7 段 → CDN 與快取:距離就是延遲
同一個 debounce,三種 import 寫法差 70KB+、2KB、可完全搖掉
import _ from 'lodash' 整包拉進來;import debounce from 'lodash/debounce' 只有 2KB;import { debounce } from 'lodash-es' 因為是 ES module 能被搖掉
證明 tree shaking
演練 為什麼 CommonJS 的 lodash 做不到具名 import 也能搖?你的專案有沒有用錯寫法的地方?
第 8 段 → Bundle 最佳化與瀏覽器載入順序
早年常見錯誤:把 API 資料塞進 Redux,自己寫 loading/error/重新抓取
這些全是 server state 特有的問題,因為資料真正的來源在別人的伺服器上;React Query 把『副本管理』標準化
證明 server state
演練 如果把 API 資料錯塞進 useState 而不是 React Query,你要自己多寫哪些邏輯?
第 9 段 → State management:三種 state
getDerivedStateFromError 切 fallback,componentDidCatch 把錯誤連 userId/route 送去監控
ErrorBoundary 只攔得到 React 渲染期間的錯誤;非同步、事件處理器裡的錯誤要靠 unhandledrejection 全域監聽補上
證明 error boundary
演練 為什麼錯誤報告要附上 userId 和 route?只有一行 stack trace 為什麼通常修不了?
第 12 段 → 可靠性:不要整頁白畫面

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

資深與中階面試回答的差別
面試官評 system design 時,資深與中階的回答差在哪?
中階講出來像背誦技術清單;資深講出來像決策紀錄,會主動指出方案的代價與取捨
第 1 段 → 面試不是背答案,是給思路 · senior developer
Monorepo 與 multi-repo 的核心取捨
multi-repo 的 versioning hell 具體長什麼樣?
app-a 要修 shared-ui 的 bug,得開 PR、等發版、升版號,要花好幾天;monorepo 一個 PR 就解決,但要共用 CI 與增量建置工具
第 5 段 → Monorepo vs multi-repo · monorepo multi-repo Nx
CDN 為什麼能把延遲從等比降到很低
沒有 CDN 打到美國 origin 要約 200ms,命中 edge node 快取只要約 20ms,差距的主因是什麼?
光速加上 TCP/TLS 來回次數;距離愈遠每次握手都要多一趟往返,收益是成倍不是線性的
第 7 段 → CDN 與快取:距離就是延遲 · CDN edge node origin server
defer/async 只對外部 script 有效
defer 和 async 屬性寫在內嵌 script 上會生效嗎?
不會,只對外部 script(有 src 屬性的)有效,這是實務上常見的誤用
第 8 段 → Bundle 最佳化與瀏覽器載入順序 · defer render blocking
重連時要用指數退避
WebSocket 斷線重連為什麼要用指數退避(exponential backoff)?
否則伺服器一掛,成千上萬客戶端同時重連,會把它再打掛一次
第 10 段 → 即時同步與斷線後怎麼辦 · WebSocket Socket.IO
feature flag 的隱藏代價
feature flag 用久了會累積什麼問題?該怎麼處理?
每個 flag 都是一條分支,清不掉的舊 flag 會讓程式碼難以推理;實務上要給 flag 設定到期日
第 11 段 → CI、feature flag 與 A/B 測試 · feature flag A/B testing CI
ErrorBoundary 攔不到的錯誤類型
ErrorBoundary 攔不到哪三種錯誤?靠什麼補上?
事件處理器裡、非同步回呼裡、Promise rejection 的錯誤;靠全域的 unhandledrejection 監聽補上
第 12 段 → 可靠性:不要整頁白畫面 · stack trace Core Web Vitals

Building a Harness with Jev

LangChain AI 模型與推論 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

在 LangChain 裡呼叫 Jev 的最小流程
  1. `uv pip install langchain-typesafe`
  2. 到 console.typesafe.ai 拿 key,設環境變數 `TYPESAFE_API_KEY`
  3. `from langchain_typesafe import Noul, TypeSafeClassifier`
  4. `classifier.invoke({"state": <原始文字>, "questions": {"is_urgent": Noul(instructions="...")}})`
  5. 取值:`response.nouls["is_urgent"].noul` → 0–1 機率
  6. 在程式端自訂門檻(例如 > 0.9 才動作)
今天就做 拿你手邊任何一段真實文字(一封客訴、一則 issue),用 TypeSafeClassifier 問它一題 Noul(例如「是否緊急」),印出機率並跟你自己的判斷比一次。
第 6 段 → 在 LangChain 裡用 Jev · langchain-typesafe TypeSafeClassifier
用 Jev 建一個 rubric judge
  1. 挑一個 agent 的 input + 回答(有參考答案更好)
  2. 把「好回答」拆成 3–5 條可獨立判斷的 rubric(正確/有依據/完整…)
  3. 每條寫成一題 Noul 或 score 的 instructions
  4. state = 問題 + 回答 (+ 參考答案),一次 invoke 平行回答
  5. 取各項分數,決定總分怎麼算(平均或加權)
  6. 抽 10 筆跟自己人工評分比對一致率
今天就做 拿你 agent 最近 5 筆對話,各寫 3 條 rubric(正確、有依據、完整),用 Jev 打分,再自己打一次,看哪一條最常不一致。
第 8 段 → 用例:Jev 當 online evals 的 judge · rubric online evals
找出自己 agent 裡該交給 Jev 的判斷點
  1. 畫出自己 agent 的 loop:入口、每條 action 邊、出口
  2. 列出「每一輪都要做、答案在有限選項內」的判斷
  3. 每個判斷對到 choice / score / noul 之一
  4. 先從 auto mode 下手(有現成 middleware,只換分類器)
  5. 其次 routing;judge 最後(需先有 evals 資料集)
今天就做 打開你正在用的 agent(或 Claude Code 的設定),寫下 loop 裡 3 個「每輪都要判、答案有限」的判斷,各標 choice / score / noul。
第 9 段 → 開始動手:安裝與資源 · agent loop classification task

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

agent loop 像一個 while 迴圈:每輪 LLM 決定「再叫工具」或「結束」
新 agent loop  ⇄  像 程式裡的 while 迴圈
第 1 段 → 回顧大家熟悉的 agent loop
Jev vs LLM 像 Kahneman 的 System 1 直覺 vs System 2 推理
新 Jev 與 LLM 的分工  ⇄  像 《快思慢想》的 System 1 / System 2
第 3 段 → Jev 是什麼:System 1 model · System 1 model System 2
Jev 的判斷標準寫在 instructions 裡,像改設定檔;傳統分類器像重訓模型
新 用自然語言 instructions 定義 routing / risky 的標準  ⇄  像 傳統小型分類器(標資料→訓練→部署)
第 7 段 → 用例:model routing 與 auto mode

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

agent loop:request → LLM → tools → 回 LLM,直到它決定結束回 result
agent loop
第 1 段 → 回顧大家熟悉的 agent loop · agent loop harness
structured outputs:把輸出型別綁到 LLM,讓結果符合 JSON schema 而非純文字
structured outputs
第 2 段 → tool calling 與 structured outputs · structured outputs JSON schema
System 1 model:專做快速結構化決策、讓軟體直接使用輸出的模型類別
System 1 model
第 3 段 → Jev 是什麼:System 1 model · System 1 model System 2
Jev:吃 state + questions,回 typed answers 與機率,根本不生成文字
Jev
第 3 段 → Jev 是什麼:System 1 model · Jev state question typed answer
confidence 是「這題判得準不準」的自我評估,跟各選項機率是兩回事
confidence
第 5 段 → Jev 回答的三種問題類型 · confidence choice
一個 state 可帶多題,Jev 平行回答;LLM 則要順序寫出
parallel questions
第 5 段 → Jev 回答的三種問題類型 · parallel questions
model routing:依請求複雜度先決定送快便宜或強昂貴模型
model routing
第 7 段 → 用例:model routing 與 auto mode · model routing
auto mode:middleware 讓 Jev 判斷每個 tool call 是否 risky,有就在 runtime 擋掉
auto mode
第 7 段 → 用例:model routing 與 auto mode · auto mode middleware hook
online evals:對真實流量每條 trace 打分;Jev 沿 rubric 每項給分,取代昂貴的 LLM-as-a-judge
online evals
第 8 段 → 用例:Jev 當 online evals 的 judge · online evals rubric LLM-as-a-judge

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

Jev 在分類型任務上比 LLM 快 20–200 倍、便宜 40–400 倍
TypeSafe AI 文件:分類型任務快 20–200x、便宜 40–400x;原因是沒有逐 token 自迴歸生成,只對 state 編碼一次、每題輸出一個值
證明 System 1 model
演練 這組數字證明了 System 1 model 的什麼性質?為什麼「不生成文字」能差一到兩個數量級?你會拿它說服誰、換掉哪個判斷?
第 3 段 → Jev 是什麼:System 1 model
PII demo:LLM 加 structured output 約 5 秒,Jev 0.1 秒回 has_pii 0.98
同一問題「這段文字有 PII 嗎?」LLM 先生成文字再 structured output 共約 5s;Jev 直接回機率 0.98,標「0.1s for a probability」,差 50 倍
證明 Jev
演練 這個 demo 證明了什麼、又刻意沒證明什麼(準確度)?0.98 是機率而非 yes/no,你的程式端要怎麼用它?
第 4 段 → Demo:LLM vs Jev 回答 PII 問題
Stripe 客訴:billing 0.84 / technical 0.159 / sales 0.001,但 confidence 只有 0.596
choice 的分佔加總約 1,答案很集中在 billing,但整體 confidence 僅 0.596——訊息同時像 billing 又像 technical,模型知道自己拿不準
證明 confidence
演練 為什麼分佈集中但 confidence 中等?這證明兩個數字量的是不同東西——你的程式在什麼情況下應該看 confidence 而不是最高機率?
第 5 段 → Jev 回答的三種問題類型
作者因分類太慢關掉 auto mode,Jev 夠快後才開回來
Sydney 親身經歷:判斷 tool call 危不危險那一步太慢,coding agent 用起來沒生產力→關掉 auto mode;Jev 讓判斷幾乎即時→重新開啟
證明 auto mode
演練 這件事證明了 latency 對安全機制意味著什麼?(太慢=等於不存在)你自己的工具裡有哪個「因為慢所以被關掉」的檢查?
第 7 段 → 用例:model routing 與 auto mode
退款範例:correct 0.98、grounded 0.92、complete 0.11,總分 0.67
問「退款期限多久」答「30 天內可退」;rubric 三條,complete 只 0.11 因為漏了條件;總分是三項平均。這是 code 式 evaluator 抓不到、又不值得動用 LLM 的判斷
證明 online evals
演練 這個例子證明 rubric 拆項的價值在哪?為什麼 complete 這種判斷 code-style evaluator 做不到?你的 agent 回答會漏什麼條件?
第 8 段 → 用例:Jev 當 online evals 的 judge
LangChain 部落格:Jev 當 judge 不只便宜快,跨 evals 更可靠一致
Jev 的分數是模型直接輸出而非生成的 token,同輸入同輸出;LLM-as-a-judge 靠取樣生成數字,同一回答可能 7 分或 8 分。比較基準(哪個 LLM、跟人工一致率)影片未說
證明 online evals
演練 為什麼「不生成」會帶來一致性?這證明了 LLM 打的分數與 Jev 打的分數在本質上有何不同?去讀部落格前你要先確認哪個比較基準?
第 8 段 → 用例:Jev 當 online evals 的 judge

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

tool calling 與 structured outputs 各管 agent loop 的哪條邊
tool calling 與 structured outputs 分別管 agent loop 上的哪條邊?
tool calling 管 LLM → tools(action 邊);structured outputs 管 LLM → result(出口邊)
第 2 段 → tool calling 與 structured outputs
System 1 命名來源
「System 1 model」的名字借自哪本書、哪位作者?
Daniel Kahneman《Thinking, Fast and Slow》(快思慢想)
第 3 段 → Jev 是什麼:System 1 model
Jev 的三種 question 型別
Jev 能回答哪三種問題型別?各回什麼?
choice(多選一:各選項機率分佈)、score(連續量尺:一個實數)、noul(是否:為真的機率 0–1);都附 confidence
第 5 段 → Jev 回答的三種問題類型
是否型別在 Jev 裡的正式名稱
Jev 的是/否問題型別叫什麼?Python 裡怎麼宣告一題?
noul;`Noul(instructions="...")`,回應在 `response.nouls["題名"].noul`
第 5 段 → Jev 回答的三種問題類型
Jev 可以掛在 harness 的哪三個位置
langchain-typesafe 說 Jev 可以掛在 agent 的哪三種位置?
a node、a tool、a middleware hook——對應 agent loop 的流程節點、tools 邊、LLM 呼叫前後的攔截點
第 6 段 → 在 LangChain 裡用 Jev
動手資源清單
開始用 Jev 要裝什麼、去哪拿 key、讀哪篇文?
`uv pip install langchain-typesafe`;console.typesafe.ai 拿 API key;langchain.com/blog/building-a-harness-with-jev
第 9 段 → 開始動手:安裝與資源

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

把一個 yes / no 判斷寫成 Jev 式呼叫:state(原始 context)+ question(型別、instructions、criteria)。
  1. 找一個目前用 LLM 回 true / false 的判斷(例如審核、分類、路由)
  2. 把要判斷的原始資料原封不動放進 state(不必先整理成結構)
  3. 寫 question:欄位名、型別 noul、一句 instructions
  4. criteria 裡分別寫 true 與 false 各代表什麼
  5. 拿回兩個選項的機率,而不是一個字
今天就做 在你自己的專案裡挑一個現在用 LLM 回 true / false 的判斷,用 state / instructions / criteria 三欄把它改寫成一份 JSON(不用真的呼叫 Jev),看有哪些判斷其實寫不進 criteria。
第 3 段 → System 1 模型與便宜到能即時用的場景 · Noul content moderation
用信心門檻接自動化:拿機率 → 設門檻 → 過就自動、沒過就退回人工或推理模型。
  1. 對每個決策先取回選項機率(不是單一標籤)
  2. 依錯誤代價定門檻:誤封成本高就門檻高
  3. 機率 ≥ 門檻 → 自動執行
  4. 機率 < 門檻 → 排進人工佇列,或改呼叫 System 2 推理模型
  5. 定期抽樣過門檻的案例,驗證命中率是否真的接近信心值
今天就做 拿你手上任何一個會回機率的分類器(或 LLM 加上「附一個 0–100 的信心」),對 20 筆真實資料跑一次,畫出「信心 vs 實際對錯」,看它有沒有校準。
第 4 段 → 型別安全 ≠ 正確:校準過的信心值 · confidence threshold
自己驗證 zero-shot 分類:開 OpenJev 的 WebGPU demo,用同一題跑多次看機率。
  1. 在瀏覽器開 OpenJev 的 WebGPU demo
  2. 把 Horse Tinder 的 state / question / criteria 貼進去
  3. 同一題連跑三次,記下 true / false 機率
  4. 換一個需要算術的問題再跑,看機率是否有意義
今天就做 今天就開 OpenJev WebGPU demo,把「is this a horse」跑三次記下機率,再丟一題「17 × 23 = 391?」看它給的信心值——親眼看 System 1 模型的邊界在哪。
第 5 段 → 黑箱與質疑:這不就是 zero-shot 分類器? · zero-shot classification WebGPU

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

Jev 的「型別安全問題」被比成 TypeScript:你宣告回傳型別,型別錯誤就不可能發生。
新 Jev:問題帶型別(noul / choice / score),答案只能是那個形狀  ⇄  像 TypeScript:函式宣告回傳型別,編譯期擋掉型別錯誤
第 2 段 → Typesafe AI:強型別的問題與三種回傳形狀
Jev 的 schema 保證 vs LLM 的 structured output / JSON mode:層次不同。
新 Jev:只能在給定選項集合裡選,沒有「格式不對」這種輸出  ⇄  像 LLM structured output / JSON mode:自由生成後靠 grammar 約束、parser 檢查或重試
第 2 段 → Typesafe AI:強型別的問題與三種回傳形狀
Typesafe 借 Kahneman 的 System 1 / System 2 比喻模型:快而直覺 vs 慢而推理。
新 Jev = System 1 模型,GPT-6 / Claude Fable = System 2 模型  ⇄  像 Kahneman《快思慢想》:人類的直覺思考與深思熟慮兩種模式
第 3 段 → System 1 模型與便宜到能即時用的場景
RLCD vs RLHF:把獎勵從「人類喜不喜歡」換成「信心值與實際對錯是否一致」。
新 RLCD:獎勵 = 信心值與命中率的一致程度  ⇄  像 RLHF:獎勵 = 人類評分者的偏好,人類偏好篤定的回答
第 4 段 → 型別安全 ≠ 正確:校準過的信心值

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

「刪掉語言」= 模型不再生成文字,只從固定選項裡選一個,所以沒有輸出 token、也沒有格式外的輸出。
classifier
第 1 段 → LLM 的致命缺陷:不會閉嘴 · classifier output tokens hallucination
強型別的問題:輸入跟 LLM 一樣(問題+非結構化 context),輸出必須是三種形狀之一,schema 匹配是保證的。
type-safe
第 2 段 → Typesafe AI:強型別的問題與三種回傳形狀 · type-safe schema matching
Noul 不只是布林:它是「評估某件事有多真」,回的是 true / false 各自的機率。
Noul
第 2 段 → Typesafe AI:強型別的問題與三種回傳形狀 · Noul
System 1 模型 = 不生成中間思考、直接給選項機率;System 2 = 先吐長串推理再作答的推理模型。
System 1 / System 2
第 3 段 → System 1 模型與便宜到能即時用的場景 · System 1 / System 2 reasoning model
校準:模型說 70% 的那批答案裡,實際約 70% 是對的——信心值等於命中率,才能直接當機率用。
calibrated confidence
第 4 段 → 型別安全 ≠ 正確:校準過的信心值 · calibrated confidence RLCD
實務用法不是取最高機率就用,而是設信心門檻:過了自動處理,沒過退回人工或 System 2 模型。
confidence threshold
第 4 段 → 型別安全 ≠ 正確:校準過的信心值 · confidence threshold
Zero-shot 分類:把問題與選項寫成 prompt 給現成 LLM,不生成,只讀它對每個選項的 logit,softmax 就是機率。
zero-shot classification
第 5 段 → 黑箱與質疑:這不就是 zero-shot 分類器? · zero-shot classification logits
單次 forward pass 同時解釋「輸出 token 免費」「零幻覺」「200 倍快」:沒有解碼迴圈、輸出空間鎖死。
forward pass
第 5 段 → 黑箱與質疑:這不就是 zero-shot 分類器? · forward pass logits

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

TypeSafe 自家對照表:LLM 端到端 3–329 秒、$0.20–10/MTok;Jev 70–500ms、$0.042/MTok。
畫面上寫的是「40x–200x faster」,作者口頭取區間上限 200 倍;價格差算起來約 5–240 倍,「400 倍」要把免費輸出 token 一起算。全是廠商自報。
證明 classifier
演練 這張表證明了「不解碼」帶來什麼量級的差異?你拿到廠商自報的倍數時,第一步會怎麼查?
第 1 段 → LLM 的致命缺陷:不會閉嘴
Horse Tinder 範例:state 描述一頭驢,問 isHorse,回 17% true / 83% false 兩個機率。
Playground 畫面:Questions 是 `isHorse` 型別 noul、instructions「Is this animal a horse?」、criteria { true: yes it is horse, false: no not a horse };Response 是兩個選項的機率,不是單一標籤。
證明 Noul
演練 這個例子證明 noul 的回傳其實是什麼?程式拿到 17% / 83% 之後還要做哪一步才能封鎖帳號?
第 3 段 → System 1 模型與便宜到能即時用的場景
作者的示意圖「Confidence levels and thresholds… 12 / 40 PASS」:40 個回答只有 12 個過門檻。
3:34 的復古風畫面;意思是自動化只吃過門檻的那 12 個,其餘 28 個不能直接用。
證明 confidence threshold
演練 12 / 40 這個數字說明了什麼?如果門檻降低,你會換到什麼風險?
第 4 段 → 型別安全 ≠ 正確:校準過的信心值
OpenJev:凍結 Qwen 4B,單次 forward pass 讀選項 logits → 機率,無需訓練,一張 3090 可跑。
流程圖:Unstructured state + Runtime criteria + Typed options → 4B model → native option logits → Probabilities;另有 WebGPU 瀏覽器 demo。沒重現的是 RLCD 校準。
證明 zero-shot classification
演練 OpenJev 重現了 Jev 的哪一半、沒重現哪一半?這對「Jev 是不是新東西」的判斷有什麼影響?
第 5 段 → 黑箱與質疑:這不就是 zero-shot 分類器?

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

Jev 宣稱的「零幻覺」實際只代表不會產生選項以外的輸出。
Jev 的「zero hallucinations」是什麼意思?
輸出空間被鎖死,不會吐出選項以外的東西;選錯選項仍然是錯。
第 1 段 → LLM 的致命缺陷:不會閉嘴
Jev 不是確定性的:同一問題、同一 context 送兩次可能得到不同結果。
Jev 的輸出是確定性的嗎?
不是;跟一般 LLM 一樣可能不同(logits 受批次與數值精度影響)。
第 4 段 → 型別安全 ≠ 正確:校準過的信心值
RLCD 全名 reinforcement learning for calibrated decisions。
RLCD 是什麼的縮寫?
Reinforcement Learning for Calibrated Decisions——獎勵信心值與對錯一致。
第 4 段 → 型別安全 ≠ 正確:校準過的信心值

Embrace Reactive Programming in React and Vue with RxJS - Fahad Ibnay Heylaal

JavaScript Conferences by GitNation 歷史參考(已過時) 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

用三個問題檢查自己的元件是不是真的需要 RxJS
  1. 自問:這個畫面需要合併多個非同步來源嗎?
  2. 自問:需要在事件之間取消先前未完成的工作嗎?
  3. 自問:有 debounce、retry、重播這類時間相關需求嗎?
  4. 三題都答不出來就用 useState + fetch 就夠了
今天就做 挑自己專案一個非同步邏輯,用這三題判斷該不該換成 RxJS,寫下結論(約 15 分鐘)
第 13 段 → 結語:先有 use case 再用 Rx
檢查自己專案有沒有訂閱了永不結束的 stream 卻忘記取消
  1. 搜尋專案裡用到 interval、WebSocket 或長期訂閱的地方
  2. 確認元件卸載或頁面離開時有沒有對應的 unsubscribe
  3. 沒有的話補上,避免記憶體洩漏
今天就做 搜尋自己一個專案的 subscribe 呼叫,確認每個都有對應的 unsubscribe(約 15 分鐘)
第 10 段 → interval 範例,與今天手接的痛

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

Observable 在 subscribe 才啟動,就像函式定義了不會自己跑,要呼叫才執行
新 Observable:建立時不執行,subscribe 那一刻才真正開始跑  ⇄  像 函式定義與函式呼叫:定義只是描述,呼叫才觸發執行
第 3 段 → Observable:和 Promise 差在哪
RxJS operator 像陣列的 map/filter,但要等 subscribe 才真的執行
新 stream 的 operator:宣告『以後進來的值都這樣處理』,真正執行等 subscribe  ⇄  像 陣列的 map/filter/reduce:呼叫當下就把整個陣列算完
第 5 段 → RxJS 是什麼:Rx 的三類 API
把 props 當 stream 之後,元件變得像純函式:同樣輸入永遠同樣輸出
新 元件只是 stream 的最後訂閱者,狀態全在 RxJS 那側,元件不再自己記狀態  ⇄  像 純函式:同樣輸入永遠產生同樣輸出,不受外部狀態影響
第 9 段 → 把 props 當成 stream

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

Observable:push-based、lazy、可發 0 到多值、可取消,subscribe 才啟動
Observable
第 3 段 → Observable:和 Promise 差在哪 · Observable
Marble diagram:字母是值、X 是錯誤、豎線是結束,官方文件的通用語言
Marble diagram
第 2 段 → data stream 與 marble diagram · Marble diagram
Subject:同時是 Observable 也是 Observer,事件世界送值進 stream 的入口
Subject
第 8 段 → Subject:讓外部把值送進 stream · Subject
BehaviorSubject:記得最後一個值,新訂閱者一接上就先拿到當前狀態
BehaviorSubject
第 8 段 → Subject:讓外部把值送進 stream · BehaviorSubject
Operator:宣告以後進來的值怎麼處理,跟陣列方法同組但等 subscribe 才執行
Operator
第 5 段 → RxJS 是什麼:Rx 的三類 API · Operator
Pipe:把要用的 operator 一個個 import 串起來,換來打包時能 tree shaking
pipe
第 7 段 → operator 實例與 pipe:順便瘦身 bundle · pipe
Stateless component:狀態不住在元件裡,元件只是 stream 的最後一個訂閱者
Stateless component
第 9 段 → 把 props 當成 stream · Stateless component
Subscription:訂閱 Observable 產生的物件,忘了取消永不結束的 stream 就洩漏
Subscription
第 10 段 → interval 範例,與今天手接的痛 · Subscription
Higher-order component:把 props$ 跟 stateless 元件接起來的中間層,寫成一行
Higher-order component
第 11 段 → 理想的接法、三個好處,與既有方案的限制 · Higher-order component
withStream:stream in、stream out,把上游 props 也當 stream 接住再送出新的
withStream
第 12 段 → proppy:attach、withObservable、withStream · withStream

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

現場人數 100 人,朋友離場變 99,回來又變 100——就是一條 stream
任何能對著時間軸畫出來的變化都是 stream,這個直覺之後被壓縮成 marble diagram 的記號
證明 Marble diagram
演練 這個例子證明了『stream』的範圍有多廣?你的專案裡還有哪些東西其實也是 stream?
第 2 段 → data stream 與 marble diagram
numbers$ 經 filter(奇數) 得 1,3,5,再 map(×10) 得 10,30,50
三條時間軸上下疊著看,operator 就是 stream 進、stream 出;用 pipe 只 import 用到的 operator,未用到的可被 tree shaking
證明 pipe
演練 改成舊寫法(掛在 prototype 上)為什麼會讓打包體積變大?
第 7 段 → operator 實例與 pipe:順便瘦身 bundle
先 new Subject() 再 subscribe,之後才 next(1)(2)(3),依序印出 1、2、3
Subject 不保存歷史,訂閱之前發出的值永遠錯過;沒有 complete 就一直開著等下一個值
證明 Subject
演練 如果把 subscribe 放在三次 next 之後才呼叫,會印出什麼?為什麼這正是 BehaviorSubject 存在的理由?
第 8 段 → Subject:讓外部把值送進 stream
React 與 Vue 並排的 interval 元件樣板,語法不同但形狀一模一樣、都整整一屏
宣告 state、在生命週期掛載時 subscribe、值進來 setState、卸載時 unsubscribe;忘了 unsubscribe 就洩漏,因為 interval 永不結束
證明 Subscription
演練 這張並排投影片證明了什麼可以被抽出來共用?unsubscribe 為什麼是必要動作不是最佳實踐建議?
第 10 段 → interval 範例,與今天手接的痛
proppy-react 換成 proppy-vue,程式碼唯一差異只有 import 的套件名
驗證了作者前面的主張:呈現層只是接口,邏輯全部留在 RxJS 那一側
證明 Higher-order component
演練 這個對照直接證明了『邏輯與呈現分離』的什麼好處?換一套前端框架時你要改的只有什麼?
第 12 段 → proppy:attach、withObservable、withStream

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

$ 結尾命名慣例
RxJS 社群的變數命名慣例是什麼?
變數名結尾加 $,提醒自己這是一條 stream,例如 numbers$、props$
第 4 段 → Observable 的程式長相與 subscribe · $ suffix convention
push-based 與 pull-based 的分別
Observable 是 push-based,對照的 pull-based 例子是什麼?
iterator、async iterator——值由你主動去要,而不是來源主動推給你
第 3 段 → Observable:和 Promise 差在哪 · TC39
Cancellation 與 Retry 這兩種真實場景的共通點
即時訊息、取消請求、斷線重試這三個場景有什麼共同點?
都不是『拿到一個值』,而是一段延續中的關係,Promise 天生只描述前者
第 6 段 → 前端為什麼需要它 · Cancellation Retry
受控輸入的完整迴路
受控輸入的三條箭頭怎麼繞一圈?
inputValue 下去 → handleChange 上來送進 Subject → inputValue 再下去,中間可插入 debounce、驗證
第 9 段 → 把 props 當成 stream · Controlled input
redux-observable 為什麼不符合作者的需求
作者用過 redux-observable 等方案後放棄的兩個原因是什麼?
會把自己綁在全應用的 Redux store 上;且大多只能往下產生新 observable,接不住上游傳進來的 props stream
第 11 段 → 理想的接法、三個好處,與既有方案的限制 · redux-observable Epic
全面 stream 化之後 SSR 為什麼變麻煩
把元件全部換成接 stream 之後,SSR 會遇到什麼問題?
HTML 必須在某個時間點定格輸出,但 stream 沒有天然的定格點,需要專門機制先把 static props 算完
第 12 段 → proppy:attach、withObservable、withStream · Server-side rendering (SSR)

Jev + Treg is a crazy combo for automation...

AI Jason AI 模型與推論 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

輸入超過 32K 時用 map-reduce 拆問
  1. 把長輸入(長對話、整頁 DOM)切成每塊 < 32K 的區塊
  2. 對每塊各問一次同樣的 Jev question(map)
  3. 把各塊的機率/答案整理成一個短摘要
  4. 再對摘要問一次 Jev 得到最終答案(reduce)
今天就做 拿你手上一份超過 32K token 的文件(長 log 或會議紀錄),寫下你會怎麼切塊、每塊問什麼、reduce 那一步的 question 怎麼寫。
第 2 段 → Jev 是什麼:只做決策、不生成文字 · map-reduce context window
把一個商業判斷寫成 Jev question
  1. 挑一個目前靠人或靠寫死規則做的判斷(路由、守門、排序)
  2. 把要判斷的原始資料整理成 state(一張單、一則貼文+數據)
  3. 選型別:多個具名選項 → choice;是/否 → true/false;有序刻度 → score
  4. 寫 instructions:這個判斷的焦點是什麼(例如「路由給能修根因的人」)
  5. 寫 criteria:每個選項的名稱與描述(score 型要排序,附 signals)
  6. 看回傳的機率分佈,確認最高機率是否合理,不合理就改 instructions 或 criteria
今天就做 拿你產品裡一個現在靠 if/else 或人眼判斷的分類(客服單分派、通知要不要發),用 JSON 寫出它的 state、一個 question 的 type / instructions / criteria,30 分鐘內寫完貼給同事看。
第 4 段 → 怎麼呼叫:state +三種問題 · state question (Jev)
建信心分數決策樹並用自己的資料校門檻
  1. 列出這個判斷的三種處理者:自動執行、人工審查、放行
  2. 先用 0.70 / 0.35 當起始門檻寫成 if/else
  3. 拿一批已知正確標籤的歷史案例跑過 Jev
  4. 看每個門檻區間的實際錯誤率,錯誤率高的區間往人工審查移
  5. 依人工審查的每日容量調整灰色地帶的寬度
  6. 寫成規則檔而不是散在程式裡,之後只改門檻
今天就做 找 30 筆你已知結果的歷史資料(垃圾信、退款申請),先猜門檻,再算每個區間有幾筆會被判錯,寫下你會把門檻調成多少。
第 5 段 → 用信心分數寫決策樹、用描述引導答案 · decision tree on confidence calibration
把每個選項寫成 what / not_for / examples
  1. 每個選項寫 what:它涵蓋哪些情況
  2. 寫 not_for:容易被誤歸到這裡、但其實屬於別的選項的邊界案例
  3. 寫 examples:2–3 句典型的原話
  4. 找一張曾經分錯的真實案例,確認 not_for 有描述到它
今天就做 拿第 17 筆你寫的 question,替其中兩個最容易混淆的選項各補上 not_for 與一句 example,然後用一張曾分錯的案例檢查會不會翻轉。
第 5 段 → 用信心分數寫決策樹、用描述引導答案 · rubric (option description)
設計一條 filter → qualify → reach 管線
  1. 列出管線每一步的單價(搜尋、抓人、enrich、找 email、Jev 呼叫)
  2. 把最貴的一步移到最後、只對前段做
  3. 在每個花錢的步驤前插一個便宜的 Jev 判斷(true/false 濾題、score 排序)
  4. 每個 Jev 步驟寫門檻(例如 on-topic ≥ 0.70、fit ≥ 1.5)
  5. 先跑 20 筆看漏斗每層剩多少與花多少,再放量
今天就做 選一個你想抓的訊號(某主題 LinkedIn 貼文的留言者、某類 GitHub issue 的作者),在紙上畫出五步漏斗、每步的資料來源與單價、Jev 在哪兩步介入與門檻。
第 7 段 → 案例二:買家意圖的 lead 篩選 · filter → qualify → reach

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

RLHF 學到「講得像很確定」就像人類偏好篤定的回答
新 RLHF 訓出的模型過度自信  ⇄  像 人在對話裡偏好聽到肯定、流暢、有把握的回答
第 1 段 → 問題:強模型為何做不了商業自動化
呼叫 Jev 很像大模型的 structured output
新 Jev 的 state + questions + criteria  ⇄  像 大模型的 structured output(給 JSON schema 回欄位)
第 4 段 → 怎麼呼叫:state +三種問題
score 題是把有序選項當刻度做機率加權平均
新 Jev score question 的最終分數  ⇄  像 問卷的 Likert 量表(1–5 分)取平均
第 4 段 → 怎麼呼叫:state +三種問題
寫死規則抓特徵、Jev 看整體樣貌,所以換 domain 騙不過 Jev
新 用 Jev 依註冊資訊+使用行為判斷詐騙  ⇄  像 程式寫死的規則(黑名單 domain、email 格式)
第 6 段 → 案例一:詐騙偵測到註冊分析(引入 Treg)

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

商業流程要的不只是答對率,而是模型答錯時自己知道不確定(校準)
calibration
第 1 段 → 問題:強模型為何做不了商業自動化 · calibration overconfidence
Jev 是只在給定選項中做決策、不生成文字、一次回所有選項機率的模型
Jev
第 2 段 → Jev 是什麼:只做決策、不生成文字 · Jev reinforcement learning for calibrated decisions
每個答案附機率,你才能圍著它寫業務邏輯、讓模型全自動運作
confidence score
第 3 段 → 機率分佈解鎖的新用途 · confidence score probability distribution
把「決定」跟「生成」拆開:Jev 選、語言模型寫
decision vs generation split
第 3 段 → 機率分佈解鎖的新用途 · Browser Use DOM (Document Object Model)
呼叫 Jev = 一份 state + 一組 question;每題有 type、instructions、criteria
question (Jev)
第 4 段 → 怎麼呼叫:state +三種問題 · state question (Jev) choice question true/false question score question
分層門檻:機器很確定 → 自動;不確定 → 交人;確定沒事 → 放行
decision tree on confidence
第 5 段 → 用信心分數寫決策樹、用描述引導答案 · decision tree on confidence human-in-the-loop
選項當 rubric:what/not_for/examples,把分派 SOP 寫進選項而不是長 prompt
rubric (option description)
第 5 段 → 用信心分數寫決策樹、用描述引導答案 · rubric (option description) few-shot prompting
單位經濟:判斷已便宜到不是瓶頸,資料的價格才決定管線划不划算
unit economics
第 6 段 → 案例一:詐騙偵測到註冊分析(引入 Treg) · unit economics usage-based pricing Treg
filter → qualify → reach:便宜的判斷放前面、貴的資料抓取放後面
filter → qualify → reach
第 7 段 → 案例二:買家意圖的 lead 篩選 · filter → qualify → reach buyer intent lead qualification

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

Jev 比 GPT-5.6 Luna 快 5.9–7.0 倍;成本在 2k 輸入只便宜 1.7 倍,8k 以上才 5.7 倍
Jev 每次呼叫 0.39–0.51 s vs GPT-5.6 Luna 2.35–2.74 s;成本倍數 2k 輸入 1.7×、8k 以上 5.7–5.8×
證明 Jev
演練 這組數字證明了 Jev 的優勢主要來自哪裡?在什麼情況下「便宜 5 倍」會高估?你會怎麼決定某個任務值不值得換成 Jev?
第 2 段 → Jev 是什麼:只做決策、不生成文字
Browser Use 讓 Jev 只決定 click/type 與目標元素,文字交給小模型
輸入:任務+互動歷史+DOM 元素清單;Jev 輸出動作類型與目標元素的機率;type 的文字由小型語言模型生成。避免大模型「生成」錯誤 selector 或吃光 context
證明 decision vs generation split
演練 為什麼把 DOM 元素當選項比讓大模型生成 selector 更可靠?你自己的 agent 有哪一步其實是「選擇」而不是「生成」?
第 3 段 → 機率分佈解鎖的新用途
內部連結對映:Jev 每次呼叫 302 tokens、Opus 5 4,483 tokens;500 頁 < 50 秒
同時起跑:Jev 已讀 76 頁、放 1,201 個連結、每次 ~302 tokens;Opus 5 讀 6 頁、每次 4,483 tokens。作者稱 Opus 5 要幾小時(畫面是 simulated run)
證明 decision vs generation split
演練 差距的來源是模型速度還是「不必寫出推理過程」?這個例子怎麼說明「便宜」解鎖的是哪一類任務?
第 3 段 → 機率分佈解鎖的新用途
門檻 0.70/0.35 來自 TypeSafe cookbook 的 strict policy(jev-1.12),不是通用常數
任一危害 ≥ 0.70 → 該危害自己的動作(block/review/support),severity ≥ 2 把 review 升級成 block;≥ 0.35 → review;否則 pass。一次呼叫同時回 jailbreak、harmful、medical、self_harm + severity
證明 decision tree on confidence
演練 你會怎麼用自己的資料重新校驗 0.70 / 0.35?灰色地帶的寬度該由什麼決定?
第 5 段 → 用信心分數寫決策樹、用描述引導答案
同一張重複扣款單:指引「哪個團隊處理」→ billing;「路由給能修根因的人」→ technical
客服單「匯出按鈕重複扣款、發票錯誤」。not_for 寫「billing 不處理 bug 造成的錯誤扣款」是讓答案翻轉的關鍵
證明 rubric (option description)
演練 為什麼 not_for 比 what 更有力?你公司有哪個分派規則其實是「邊界案例」該寫進 not_for?
第 5 段 → 用信心分數寫決策樹、用描述引導答案
註冊分析管線每步單價:verify $0.0015、enrich $0.0026–0.03、Jev $0.00008
email → Treg verify → Treg person enrich → Jev → {segment, is_fraud, upsell_value};一天約 $1 掃數百筆註冊
證明 unit economics
演練 這條管線的錢花在哪一步?這證明為什麼作者強調「Jev 加 Treg」而不只是 Jev?
第 6 段 → 案例一:詐騙偵測到註冊分析(引入 Treg)
Treg vs Clay:290 人 $1.49 vs $10.3(便宜 85%);precision 91.3% vs 93.6% 略輸
找到 289 vs 280 人、地址精確比對 90.4% vs 89.7% Treg 略勝;precision Clay 略勝;每列 $0.0051 vs $0.0354;命中中位延遲 0.44 s vs 11.2 s
證明 unit economics
演練 作者說「準確度相近甚至更好」,對照表哪個指標支持、哪個不支持?真正拉開差距的是什麼?
第 6 段 → 案例一:詐騙偵測到註冊分析(引入 Treg)
Lead 漏斗:20 篇貼文 → 3 篇 on-topic → 80 人 → 56 決策者/24 使用者 → 前 16 找 email 命中 13
Treg search ×4;Jev on-topic ≥ 0.70 留 3 篇;Jev rank ×80:fit ≥ 1.5 decision maker、≥ 0.8 user;只對前 16 做 email find(每次命中 ~$0.02),81% 命中。368 人排名 42.8 s、Jev $0.013、Treg $0.87
證明 filter → qualify → reach
演練 Jev 在這條管線被用了兩次,各是什麼題型?為什麼 email find 放最後?
第 7 段 → 案例二:買家意圖的 lead 篩選
Viral 篩選用互動率當 state:paid 例子 likes 0.15%、generic 回覆 11%、reposts 0
168 篇 24h 貼文、162 個作者檔案、每篇 20 則回覆樣本 → Jev judge ×60 每篇 5 題,169 s,Treg $0.105、Jev $0.017;結果 organic 18/paid 5/irrelevant 41。organic 例子 likes 0.31%、generic 0%
證明 question (Jev)
演練 為什麼要先抓回覆樣本再判斷?「irrelevant 41」這個桶說明了同一次呼叫可以做什麼?這套啟發式什麼時候會誤判?
第 8 段 → 案例三:篩掉付費灌流量的爆紅貼文

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

大模型做不了商業自動化的兩個原因:過度自信、成本與速度
作者說最新大模型做不了商業自動化的兩個原因是?
① 過度自信、沒校準的信心;② 大量流程下成本與速度不划算
第 1 段 → 問題:強模型為何做不了商業自動化
Jev 的四個「不能」
Jev 不能做的四件事?
不能自創選項、不能輸出文字、不能寫程式、不能逐步推理
第 2 段 → Jev 是什麼:只做決策、不生成文字
Jev 的 context window 是 32K
Jev 的 context window 多大?超過怎麼辦?
32K tokens;太長用 map-reduce 切塊各問再合併
第 2 段 → Jev 是什麼:只做決策、不生成文字
三種問題型別與典型用途
Jev 的三種 question 型別各對應什麼用途?
choice → 路由;true/false → 守門(jailbreak 判斷);score → 排序/給分
第 4 段 → 怎麼呼叫:state +三種問題
Treg:3,000 多個資料端點、只收使用量、0% 加價
Treg 是什麼?計費方式?
作者團隊的資料與工具 open router,接 3,000+ 端點;usage-based、0% markup
第 6 段 → 案例一:詐騙偵測到註冊分析(引入 Treg)
詐騙偵測每 15–30 分鐘跑一批
作者的詐騙偵測管線多久跑一次?超過門檻怎麼處理?
每 15–30 分鐘抓一批註冊+使用量給 Jev;超門檻自動封鎖
第 6 段 → 案例一:詐騙偵測到註冊分析(引入 Treg)
資源:treg.to/jev 有 recipe 與可貼進 Claude Code/Codex 的 prompt
作者把團隊的 Jev 自動化 recipe 放在哪?
treg.to/jev;每條附可貼進 Claude Code/Codex 的 prompt;還有免費的 Twitter 貼文 organic/paid 判斷工具
第 8 段 → 案例三:篩掉付費灌流量的爆紅貼文
作者是 Treg 的開發者,對 Treg 的評價有利益關係;Jev 用法不依賴 Treg
看這支影片時要注意作者的什麼立場?
他是 Treg 開發者;Jev 的模式可接任何便宜資料源。數字是 2026-09 當下狀態
第 9 段 → 總結:改變自動化的單位經濟

Cross-Site Request Forgery (CSRF) Explained

PwnFunction 歷史參考(已過時) 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

檢查專案的寫入型 API 有沒有嚴格檢查 Content-Type,拒收非預期型別
  1. 挑一個會改動狀態的 API endpoint
  2. 確認伺服器端有沒有檢查 Content-Type 必須是 application/json
  3. 測試改用 text/plain 或 form 送同樣的內容,確認會被拒絕
今天就做 挑自己專案一個寫入型 API,確認它會拒絕非 application/json 的 Content-Type(約 15 分鐘)
第 11 段 → Content-Type 這道關卡與 CORS
檢查自己專案的 session cookie 有沒有設 SameSite
  1. 打開瀏覽器 DevTools 檢查登入後的 session cookie
  2. 確認有沒有 SameSite 屬性,值是 Lax 或 Strict
  3. 跨網域嵌入需求存在時才考慮 SameSite=None,並確認同時有 Secure
今天就做 檢查自己專案 session cookie 的 SameSite 設定,沒設的話加上 Lax(約 10 分鐘)
第 5 段 → 拆解:兩個請求長得一模一樣
檢查自己專案的 CSRF token 是不是隨機、且綁定使用者 session
  1. 找出產生 CSRF token 的程式碼
  2. 確認它是密碼學安全的隨機值,不是時間戳或遞增序號
  3. 確認 token 綁定使用者的 session,不是全站共用一個固定值
今天就做 檢查自己專案 CSRF token 的產生方式是否隨機且綁定 session(約 15 分鐘)
第 6 段 → 防禦 anti-CSRF token 與「沒破壞任何規則」

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

anti-CSRF token 的安全性等於密碼強度:能被猜到就等於沒設防
新 token 必須隨機且不可預測,可預測的 token(時間戳、遞增序號)形同沒有防護  ⇄  像 密碼強度要求:容易被猜到的密碼等於沒有保護
第 6 段 → 防禦 anti-CSRF token 與「沒破壞任何規則」
把等號關進字串值裡讓 JSON 合法,跟 SQL injection 的引號技巧同一招
新 讓 name 停在未閉合的引號上,由 value 補完,瀏覽器插入的等號就落在字串值裡面  ⇄  像 SQL injection 常見手法:把不受控的字元關進字串字面值裡,繞過語法檢查
第 9 段 → 用表單拼出一段合法 JSON
Flash 能設標頭沒 cookie,307 帶 cookie 沒標頭,兩個殘缺工具拼成完整攻擊
新 把 Flash 的輸出接到 307 轉址的輸入,組合出單一工具做不到的完整請求  ⇄  像 供應鏈攻擊常見思路:串連兩個各自無害的系統,拼出一條破壞力更大的鏈
第 12 段 → Flash 加 307 轉址繞過標頭限制

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

CSRF:一個網域偽造請求,讓另一個網域的伺服器以為是使用者本人想做的事
Cross-Site Request Forgery (CSRF)
第 4 段 → CSRF 現場:光是點進去帳號就沒了 · Cross-Site Request Forgery (CSRF)
Cookie:瀏覽器只認目的地網域,不管請求是誰發起的,就自動附上
cookie
第 2 段 → Cookie 會自動跟著送 · cookie
Same-origin policy:擋的是讀回應,不是擋發請求,請求照樣送得出去
same-origin policy
第 3 段 → 同源政策:能送不能讀 · same-origin policy
Anti-CSRF token:後端隨機產生、綁定 session,攻擊者讀不到也猜不到
anti-CSRF token
第 6 段 → 防禦 anti-CSRF token 與「沒破壞任何規則」 · anti-CSRF token
SameSite cookie:跨站請求時是否附帶 cookie 的旗標,2019 年起預設開啟
SameSite cookie
第 5 段 → 拆解:兩個請求長得一模一樣 · SameSite cookie
CORS:讓不同來源合法溝通的機制,決定權在伺服器要不要開門
CORS
第 11 段 → Content-Type 這道關卡與 CORS · CORS
Preflight request:不簡單的跨來源請求先送 OPTIONS 問許可,沒回應就整個不送
preflight request
第 11 段 → Content-Type 這道關卡與 CORS · preflight request
Junk parameter:把瀏覽器插入的等號吞進字串值裡,拼出合法 JSON
junk parameter
第 9 段 → 用表單拼出一段合法 JSON · junk parameter
307 redirect:轉址時原封不動保留方法與 body,當中繼站接上瀏覽器自動附的 cookie
307 redirect
第 12 段 → Flash 加 307 轉址繞過標頭限制 · 307 redirect
State-changing request:CSRF 只鎖定會改動狀態的動作,讀取資料的請求沒有收穫
state-changing request
第 4 段 → CSRF 現場:光是點進去帳號就沒了 · state-changing request

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

DevTools 顯示攻擊請求標頭裡其實有 Origin: cat.com,只是伺服器沒去驗
Cookie 標頭裡的 SessionID 是自動附帶的實體;Content-Type 是 application/x-www-form-urlencoded,Form Data 是 delete:1
證明 cookie
演練 這張截圖推翻了作者的哪句話?它指出了除了 token 之外的哪一條防線?
第 5 段 → 拆解:兩個請求長得一模一樣
整個攻擊只是一個 hidden input 表單加一行 submit(),沒有任何漏洞利用碼
action 指向 vulnerable.com/delete_my_account,method POST,唯一的 input 是 hidden 型別,載入頁面瞬間自動送出
證明 Cross-Site Request Forgery (CSRF)
演練 這段程式碼證明了『偽造』偽造的到底是內容還是意圖?你會怎麼跟人解釋這不需要任何駭客技巧?
第 7 段 → 攻擊頁的程式碼長什麼樣
name='{"itemName":"apple","junk":"'、value='test"}' 拼出合法 JSON
瀏覽器插入的等號落在 junk 的字串值中間,伺服器只挑出它要的 itemName,junk 被無視
證明 junk parameter
演練 為什麼 JSON 一定要寫在 name 而不是 value?少了 enctype=text/plain 這招會怎樣?
第 9 段 → 用表單拼出一段合法 JSON
真實漏洞常長成:伺服器把請求的 Origin 原封反射回 ACAO,還加 Allow-Credentials: true
等於對任何網站敞開大門且允許帶身分;正確設定時 ACAO 不能是萬用字元 `*`,必須明確寫出來源
證明 CORS
演練 為什麼『反射 Origin+允許帶憑證』這個組合特別危險?你會怎麼檢查自己專案的 CORS 設定?
第 11 段 → Content-Type 這道關卡與 CORS
作者上週靠一個 CSRF 串出遠端程式碼執行(RCE)
CSRF 本身只能『讓動作發生』,但那個動作可能是上傳檔案、改設定、觸發部署
證明 state-changing request
演練 為什麼一個只能觸發動作的漏洞值得跟嚴重漏洞一樣重視?你的專案有哪個端點被串接後果會很嚴重?
第 13 段 → 串接其他漏洞與現況

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

同源判定的三個條件
瀏覽器判斷兩個網址是不是同源,要比對哪三個欄位?
協定(scheme)、網域(host)、連接埠(port),三者全等才算同源
第 3 段 → 同源政策:能送不能讀 · origin
hidden input 的用途
攻擊表單裡的 type="hidden" 是用來隱藏什麼?
不是隱藏機密,只是讓必要的 POST 參數不顯示在畫面上,受害者不會發現有表單存在
第 7 段 → 攻擊頁的程式碼長什麼樣 · hidden input
enctype 必須設成 text/plain 的原因
junk parameter 這招為什麼一定要把表單 enctype 設成 text/plain?
預設的 urlencoded 會把大括號、引號百分號編碼,送到伺服器就不是合法 JSON 了
第 9 段 → 用表單拼出一段合法 JSON · text/plain enctype
fetch 跨來源預設不帶 cookie
用 fetch 發跨來源請求,cookie 會自動附上嗎?
不會,預設 credentials 是 same-origin;要明寫 credentials: 'include' 才會附上 cookie
第 10 段 → 更簡單的做法:fetch · fetch
帶 cookie 的跨來源請求還需要 Allow-Credentials
跨來源請求要帶 cookie,除了 ACAO 還需要哪個標頭?有什麼限制?
Access-Control-Allow-Credentials: true;此時 ACAO 不能是萬用字元 *,必須明確寫出來源
第 11 段 → Content-Type 這道關卡與 CORS · Access-Control-Allow-Origin
為什麼一定要用 307 而不是 301/302
Flash+redirect 這招為什麼指定要 307,不能用常見的 301/302?
301/302 常把後續請求降級成 GET 並丟掉 body;307(含 308)規範要求維持原本方法與 body
第 12 段 → Flash 加 307 轉址繞過標頭限制 · 307 redirect
現代 CSRF 為什麼變少了
2019 年之後 CSRF 為什麼比以前少見?
主流框架(Rails、Django、Laravel、Spring)預設內建 CSRF 防護,加上瀏覽器 SameSite cookie 預設開啟
第 13 段 → 串接其他漏洞與現況

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

檢查自己專案裡的 map operator 選用是否合理
  1. 找你專案裡任一個用 mergeMap/switchMap/concatMap 的地方
  2. 確認來源是不是只吐一次值就完成(如單次 HTTP 請求)
  3. 若是單發來源,換成別的 map operator 測試結果是否一樣
  4. 若來源連續吐值,確認選用的策略符合需求(取消/排隊/忽略)
今天就做 檢查你專案裡一個用了 switchMap 或 mergeMap 的地方,確認來源是否連續吐值,策略是否符合實際需求
第 8 段 → 真實例子:為何 concatMap 和 switchMap 結果一樣

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

mergeMap 接在高頻事件上會像不設限的服務窗口越開越多
新 mergeMap 不限制同時活著的內層數量,來源吐值快時內層會無限增生  ⇄  像 一個櫃檯來一個客人就開一個新窗口服務,不限制窗口數量,客人一多整個大廳都是窗口
第 4 段 → mergeMap / flatMap:全部同時開,誰也不取消 · mergeMap concurrent

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

Observable:描述會隨時間吐出零到多個值的物件,要被 subscribe 才會真的開始跑
Observable
第 2 段 → map:純轉換,不建新的 observable · Observable
map:把每個值換成另一個值,callback 回傳普通值不是 Observable,沒有等待/取消問題
map
第 2 段 → map:純轉換,不建新的 observable · map
高階串流:值本身又是 Observable,四個攤平 operator 都是 map 產生它再用某種策略攤平
higher-order Observable
第 4 段 → mergeMap / flatMap:全部同時開,誰也不取消 · higher-order Observable
mergeMap:每個值一進來就立刻建內層、全部並行,不等也不取消,順序不保證
mergeMap
第 4 段 → mergeMap / flatMap:全部同時開,誰也不取消 · mergeMap
concatMap:同一時間只跑一個內層,排隊等前一個完成,一個都不丟但可能無限排隊
concatMap
第 5 段 → concatMap:排隊,等前一個完成 · concatMap
完成通知:Observable 宣告不再吐值的結束訊號;被取消不會觸發它,是兩種不同結束
complete
第 5 段 → concatMap:排隊,等前一個完成 · complete
switchMap:新值一到就取消前一個還沒完成的內層,只保留最新的那一個
switchMap
第 6 段 → switchMap:新的來了就砍掉舊的 · switchMap
取消訂閱:中止進行中的訂閱;被取消不等於完成,清理要寫在 finalize
unsubscribe
第 6 段 → switchMap:新的來了就砍掉舊的 · unsubscribe
exhaustMap:內層還沒完成時忽略所有新值,不建新內層也不排隊,是 switchMap 的鏡像
exhaustMap
第 7 段 → exhaustMap:忙的時候,後來的一律忽略 · exhaustMap
冷串流:每次被訂閱都重新從頭執行一次,多訂閱一次就多打一次 API
cold Observable
第 8 段 → 真實例子:為何 concatMap 和 switchMap 結果一樣 · cold Observable

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

同一骨架下四種輸出:全部、逐一、只剩最新、只剩最早
mergeMap 全部一起湧出(0~4)、concatMap 逐一出現(0~4,間隔明顯)、switchMap 只剩 4、exhaustMap 只剩 0
證明 higher-order Observable
演練 這四種輸出證明了什麼共同的比較軸?如果把 delay 改成隨機值,mergeMap 的輸出順序還會是 0~4 嗎?
第 7 段 → exhaustMap:忙的時候,後來的一律忽略

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

六個 operator 其實是一比五
六個 map 系列 operator 真正該比較的是幾個?為什麼?
其實是一比五:map 的 callback 回傳普通值,其他五個回傳新的 Observable,才會遇到「多工作同時存在」的問題
第 1 段 → 六個 map 為什麼分不清 · RxJS map
pipe 裡的 operator 何時才真的執行
pipe 裡寫了 operator,什麼時候才會真的執行?
要等 subscribe 才會執行(cold 行為);pipe 本身只是描述,把 operator 由左到右串起來
第 2 段 → map:純轉換,不建新的 observable · pipe subscribe from
of 跟 from 吐出的東西不一樣
of([1,2,3]) 跟 from([1,2,3]) 差在哪?
of([1,2,3]) 吐出一個陣列;from([1,2,3]) 依序吐出三個數字
第 3 段 → 共用實驗骨架:0 到 4 加 delay 500 · of from
delay(500) 為什麼是實驗的關鍵
實驗骨架裡的 delay(500) 為什麼很重要?拿掉會怎樣?
延遲讓每個內層工作都要花時間才結束,逼出「新值來時舊工作還沒完」的局面;拿掉延遲四個 operator 幾乎無法分辨
第 3 段 → 共用實驗骨架:0 到 4 加 delay 500 · delay inner Observable
flatMap 只是 mergeMap 的舊別名
flatMap 跟 mergeMap 是什麼關係?concurrent 參數做什麼?
flatMap 是 mergeMap 的舊別名(已 deprecated);concurrent 限制同時活著的內層數,設成 1 就等同 concatMap
第 4 段 → mergeMap / flatMap:全部同時開,誰也不取消 · flatMap concurrent
concatMap 排隊策略的隱藏風險
concatMap 排隊策略有什麼隱藏風險?
佇列沒有上限,若內層永遠不完成或來源長期快過處理速度,佇列會無限增長、延遲越來越久
第 5 段 → concatMap:排隊,等前一個完成 · concatMap
switchMap 是唯一能真正省流量的策略
switchMap 為什麼是唯一能真正省網路流量的策略?什麼情境不該用它?
取消訂閱會讓瀏覽器中止底層 HTTP 連線;不能用在每一筆都不能掉的動作,如送出訂單、逐筆儲存
第 6 段 → switchMap:新的來了就砍掉舊的 · switchMap
exhaustMap 最典型的用途
exhaustMap 最典型的用途是什麼?
防止重複觸發:送出按鈕連點只送一次、token 續期多次要求只實際續一次、下拉更新不重複發
第 7 段 → exhaustMap:忙的時候,後來的一律忽略 · exhaustMap
為什麼換 operator 結果一樣
為什麼把 concatMap 換成 switchMap,getUser 串接的結果完全一樣?
外層只吐一個值就完成,從頭到尾只有一個內層被建立,四個策略都無事可做;來源連續吐值時才會分出差異
第 8 段 → 真實例子:為何 concatMap 和 switchMap 結果一樣 · cold Observable

JavaScript Visualized - Execution Contexts

Lydia Hallie JavaScript 執行模型 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

自己標出 creation phase 結束時每個 binding 的狀態
  1. 挑一段你寫過的、有巢狀函式或區塊的程式碼
  2. 標出每個宣告是 const/let/var/function
  3. 推測 creation phase 結束時每個 binding 手上有什麼
  4. 在 DevTools 中斷點驗證
今天就做 挑一段自己寫的程式碼,逐行標出 creation phase 結束時每個宣告手上有什麼值,用 DevTools 中斷點驗證
第 9 段 → Execution phase 與函式呼叫建立新 EC

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

global object 上的 Array 只是名牌,實體住在 intrinsics 裡
新 名字掛在 global object 上,實際的內建物件實體住在 intrinsics 裡,兩者是分開的  ⇄  像 職員名牌上印的職稱可以隨便改,但那個人實際的技能和職責不會因此改變
第 4 段 → Realm:隔離環境與 intrinsics · intrinsics global object

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

Execution context:引擎執行一段程式碼時建立的內部結構,規格層級、程式碼碰不到
execution context
第 1 段 → 問題起點:什麼是 execution context · execution context
Creation phase:為當前這層的宣告配置記憶體但不執行程式碼
creation phase
第 3 段 → 兩個階段與 global EC 的三個元件 · creation phase
Realm:彼此隔離的執行環境,各自有自己的 intrinsics、global object 與 global environment record
realm
第 4 段 → Realm:隔離環境與 intrinsics · realm
Global object:承載全域名字的物件,屬性分規格定義、宿主定義、使用者定義三種來源
global object
第 5 段 → Global object 的三種屬性來源 · global object
Global environment record:內含 object record(收 var)與 declarative record(收其餘)
global environment record
第 6 段 → Global environment record 的內部 · global environment record
Outer env:指向外一層 environment record 的屬性,來源是宣告位置,全域時是 null
outer env
第 6 段 → Global environment record 的內部 · outer env
Scope chain:沿 outer env 一層層往外查找 binding,是靜態的,不是往下翻 call stack
scope chain
第 10 段 → 沿 scope chain 找變數,再逐一出堆疊 · scope chain
Hoisting:宣告在 creation phase 就被登記,依種類分三種待遇——空、undefined、或完整函式物件
hoisting
第 11 段 → Hoisting 的三種待遇與 TDZ · hoisting
TDZ:從區塊開始到 const/let 宣告那一行為止,存取該 binding 會得到 ReferenceError
temporal dead zone
第 11 段 → Hoisting 的三種待遇與 TDZ · temporal dead zone
Closure:內層函式透過 function object 的 environment 屬性,保留外層 environment record 的參照
closure
第 12 段 → Scope chain 與 closure 的同一條線 · closure

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

九行範例:三種宣告在 creation phase 結束時各拿到什麼
const firstName、let lastName 進 declarative record 保持 uninitialized;函式宣告 greet 進 object record,creation phase 就完成初始化生成完整 function object
證明 creation phase
演練 這個例子證明了三種宣告在 creation phase 結束時各自拿到什麼?為什麼函式宣告可以在宣告之前呼叫?
第 8 段 → Creation phase 走一遍 script

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

call stack 上堆的其實是 execution context
call stack 上堆的到底是什麼?
不是函式本身,是 execution context;call stack 只是 execution context 的堆疊
第 1 段 → 問題起點:什麼是 execution context · call stack execution context
execution context 自己不存變數
execution context 自己存變數嗎?
不存,它只持有指標;真正存放「名字→值」的是 environment record
第 2 段 → 定義:執行環境與 environment record · environment record identifier binding
execution context 何時真正誕生
execution phase 跟 creation phase 差在哪?execution context 何時真正誕生?
execution phase 是程式碼逐行真正執行、變數拿到值的階段;execution context 在 creation phase 就已存在,只是還沒開始跑
第 3 段 → 兩個階段與 global EC 的三個元件 · execution phase global execution context
重新賦值 Array 為什麼不會壞掉
為什麼把全域 Array 重新賦值,陣列字面量還是能正常運作?
名字掛在 global object 上,實體住在 intrinsics 裡;引擎內部建立陣列時直接走 intrinsics,不經過全域名字
第 4 段 → Realm:隔離環境與 intrinsics · intrinsics
為什麼瀏覽器跟 Node 能力不同
為什麼同一段語法在瀏覽器跟 Node 能力不同?
fetch、setTimeout、document 這類 host-defined 屬性由宿主環境注入 global object,不是 JS 規格保證的
第 5 段 → Global object 的三種屬性來源 · host-defined property
const 宣告的名字在 globalThis 上找不到
宣告 const x 後 globalThis.x 拿得到嗎?宣告 var y 呢?
const x 存進 declarative record,globalThis.x 是 undefined;var y 存進 object record,globalThis.y 拿得到值
第 6 段 → Global environment record 的內部 · object record declarative record globalThis
lexical 跟 variable environment 為何全域看不出差別
lexical environment 跟 variable environment 差在哪?為什麼全域看不出差別?
lexical environment 指向 var 以外的 binding、隨區塊更換;variable environment 指向 var 的 binding、整個函式期間不變;全域沒有更大區塊,兩者剛好同指一處
第 7 段 → Lexical 與 variable environment 的分工與簡化 · lexical environment variable environment
uninitialized 不是 undefined
uninitialized 跟 undefined 有什麼不同?
uninitialized 是引擎內部狀態,讀取會丟錯誤;undefined 是一個真正的值,可以被讀取和比較
第 8 段 → Creation phase 走一遍 script · uninitialized
function object 的 environment 屬性建立後就固定
function object 的 environment 屬性在什麼時候被固定?之後會不會改變?
在函式被建立(宣告)的那一刻就固定指向宣告處的環境,之後不管被傳到哪、被誰呼叫都不會變
第 9 段 → Execution phase 與函式呼叫建立新 EC · function object function execution context
參數跟函式內 const 在 creation phase 待遇不同
同一個 creation phase 裡,函式參數跟函式內的 const 拿到的待遇為什麼不同?
參數的值來自呼叫端,建立 context 時就已備妥;const 的值要靠執行到那一行運算才算得出,只能先留空
第 9 段 → Execution phase 與函式呼叫建立新 EC · function environment record
var 的 undefined 比 TDZ 更危險
var 的 hoisting 比 const/let 的 TDZ 更危險在哪?
var 提前使用不會報錯,只會靜默拿到 undefined,錯誤要到很後面才浮現;TDZ 是刻意讓錯誤提早爆炸
第 11 段 → Hoisting 的三種待遇與 TDZ · hoisting temporal dead zone
closure 保留的是參照不是複製
closure「記住」外層變數,是複製了一份值嗎?
不是,它保留的是對整個 environment record 的參照;外層變數之後被修改,closure 看到的也是新值
第 12 段 → Scope chain 與 closure 的同一條線 · closure

Core Web Vitals Explained: LCP, INP & CLS

I Code It 網頁效能 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

替阻塞 LCP 的腳本加上 defer,並把最大元素的圖片加 preload + fetchpriority
  1. 找出 header 或 body 前段會拖慢解析的 <script>,加上 defer
  2. 確認 hero image 的 <img src> 寫在原始 HTML 裡(不是 data-src)
  3. 在該圖片加 <link rel=preload> 與 fetchpriority="high"
  4. 用 DevTools Performance 面板比較改前改後的 LCP
今天就做 挑自己專案一個被 header script 拖慢的頁面,加上 defer + preload,量一次 LCP 前後差異(約 20 分鐘)
第 6 段 → 修法:defer、preload、fetchpriority
替所有內容圖片補回 width 與 height,讓瀏覽器提前預留空間
  1. 搜尋專案裡沒有寫 width/height 的 <img>
  2. 補上原圖的實際寬高比例
  3. 無法預知長寬比的內容改用 CSS aspect-ratio 或固定高度骨架佔位
今天就做 檢查自己專案的圖片標籤,把缺 width/height 的補齊,重新量一次 CLS(約 15 分鐘)
第 9 段 → after:寫上寬高,CLS 歸零
把一個會卡住畫面的重運算切成小塊,讓主執行緒有空檔繪製
  1. 找出一個會一次跑完、耗時較久的同步迴圈或運算
  2. 按下觸發時先同步更新 UI 顯示處理中狀態
  3. 把迴圈拆成每塊約 500 筆,用 setTimeout(fn, 0) 排下一塊
  4. 跑起來確認處理過程中頁面仍能互動
今天就做 挑自己專案一個會卡住畫面的重運算,改成 500 筆一塊、setTimeout 分批執行(約 25 分鐘)
第 12 段 → 修法:切成小塊並交還控制權

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

defer 保證解析完才依序執行,async 是下載完立刻插隊、順序不保證
新 defer:延到 HTML 解析完成後才執行,且多支之間維持書寫順序  ⇄  像 async:下載完就立刻插隊執行,時機不可預測,適合獨立分析腳本
第 6 段 → 修法:defer、preload、fetchpriority
HTML 的 width/height 其實會被換算成 CSS 熟悉的 aspect-ratio
新 img 標籤上寫死的 width/height 屬性,瀏覽器換算成 aspect-ratio 套進預設樣式  ⇄  像 CSS 的 aspect-ratio 屬性:常用來做響應式版面的長寬比控制
第 9 段 → after:寫上寬高,CLS 歸零
效能除錯的 before/after 對照,就是科學實驗的控制變因法
新 效能優化:固定其他變因、只改一處,看同一個數字怎麼動  ⇄  像 科學實驗的控制變因法:只改一個自變數,其餘條件保持不變
第 5 段 → 實測 before:LCP 5.4 秒

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

LCP:主要內容多快出現在畫面上,量的是繪製完成的時間點
Largest Contentful Paint
第 2 段 → 三個指標各自管什麼 · Largest Contentful Paint
CLS:整個頁面生命週期累加的版面位移分數,好的分數低於 0.1
Cumulative Layout Shift
第 2 段 → 三個指標各自管什麼 · Cumulative Layout Shift
INP:使用者互動到畫面下一次更新的時間,理想低於 200 毫秒
Interaction to Next Paint
第 2 段 → 三個指標各自管什麼 · Interaction to Next Paint
LCP candidate:可見面積最大的元素會不斷換人,直到使用者第一次操作才停止更新
LCP candidate
第 3 段 → LCP:最大元素畫完的那一刻 · LCP candidate
Render-blocking script:腳本可能改掉頁面結構,瀏覽器得先讓它跑完才能安全繼續解析
render-blocking script
第 4 段 → 阻塞腳本為什麼拖慢 LCP · render-blocking script
Preload scanner:parser 被腳本卡住時,另外掃一遍原始 HTML 提前發出資源請求
preload scanner
第 4 段 → 阻塞腳本為什麼拖慢 LCP · preload scanner
Intrinsic size:圖片缺少寫死的寬高,瀏覽器首次繪製時不知道要留多少空間
intrinsic size
第 8 段 → before:圖片沒寫寬高就會擠壓版面 · intrinsic size
Layout shift:非預期的版面位移,互動後 500ms 內的移動視為預期而豁免
layout shift
第 7 段 → CLS:版面為什麼會亂跳 · layout shift
Long task:佔住單執行緒主執行緒的重運算,讓瀏覽器無法更新 UI 或回應互動
long task
第 10 段 → INP:主執行緒被長工作卡住 · long task
Task chunking:把重運算切成小塊,塊與塊之間讓出主執行緒給瀏覽器繪製
task chunking
第 12 段 → 修法:切成小塊並交還控制權 · task chunking

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

DevTools 實測:腳本擋住解析的 before 頁面,LCP 是 5.41 秒,標成 poor
Chrome DevTools Performance 面板的 Local metrics 直接吐出 5.41s;同時 CLS 是 0、INP 是一橫槓(還沒人互動過)
證明 render-blocking script
演練 這個數字證明了哪一段因果鏈的結果?它是不是可以直接拿來宣稱線上合格?
第 5 段 → 實測 before:LCP 5.4 秒
加 defer+preload+fetchpriority 後,LCP 從 5.41 秒掉到 0.10 秒
JavaScript 一樣慢,但 hero image 立刻出現;三個屬性同時用在同一份檔案(第 9 行 preload、第 11 行 defer、第 17 行 fetchpriority)
證明 Largest Contentful Paint
演練 為什麼這裡要同時改三個地方而不是只改一個?只做 defer 不做 preload 會怎樣?
第 6 段 → 修法:defer、preload、fetchpriority
demo 的 CLS 只有 0.04(在合格線內),但真實頁面同樣錯誤會輕易破 0.1
demo 只有三張圖、一個視窗高度;真實頁面配上廣告位、動態橫幅、換字型(FOUT)就會嚴重得多
證明 layout shift
演練 為什麼 demo 數字好看不代表你的專案沒事?你會怎麼在真實頁面上重現這個問題?
第 8 段 → before:圖片沒寫寬高就會擠壓版面
把網路節流到更慢,補了 width/height 的頁面 CLS 依然是 0
圖片載得更慢,但版面沒有再位移——證明版面穩定與載入快慢是兩件獨立的事
證明 intrinsic size
演練 這個反證推翻了什麼直覺?你會怎麼跟同事解釋『慢不會造成位移,資訊不足才會』?
第 9 段 → after:寫上寬高,CLS 歸零
CPU 降速 20 倍後,before 頁面按下處理鍵 INP 飆到 1,888 毫秒
門檻是 200 毫秒,1,888 毫秒接近十倍;after 頁面同樣降速 20 倍,處理進行中按鈕仍按得動
證明 long task
演練 這個實測證明了 INP 記錄的是整次造訪最差的一次還是平均值?為什麼要刻意降速測試?
第 11 段 → 20 倍降速實測:卡住 vs 有反應
task chunking 程式碼:每塊只跑 500 筆,用 setTimeout(…, 0) 排到隊尾
先把按鈕文字改成「Processing…」,再用 setTimeout 包住第一塊運算,讓瀏覽器有機會先畫出文字
證明 task chunking
演練 為什麼一定要先設文字再包 setTimeout,順序反過來會怎樣?setTimeout(...,0) 的 0 毫秒代表什麼?
第 12 段 → 修法:切成小塊並交還控制權

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

Core Web Vitals 的來歷與指標替換史
Core Web Vitals 最早的三個指標是什麼?2024 年 3 月發生了什麼替換?
最早是 LCP、FID、CLS;2024 年 3 月 INP 正式取代 FID,因為 FID 量不到互動後畫面多久更新
第 1 段 → 先問:效能到底該量什麼 · Core Web Vitals
瀏覽器在 script 處暫停解析的歷史原因
瀏覽器為什麼必須在 <script> 處暫停解析 DOM?
因為舊時代的 document.write 可能在解析當下改變接下來要解析的內容,parser 不能先跑過去
第 4 段 → 阻塞腳本為什麼拖慢 LCP · DOM
本機數字 vs Chrome UX Report 真實使用者資料
本機 DevTools 量到的 LCP 能不能拿來宣稱線上合格?
不能;Google 排名採用 Chrome UX Report 的真實使用者分佈(75 百分位),本機數字只適合相對比較
第 5 段 → 實測 before:LCP 5.4 秒 · Chrome UX Report
CLS 分數怎麼算、怎麼分段累加
CLS 單次位移的分數怎麼算?2020 年 6 月後為什麼改成 session window?
分數=受影響區域佔視窗比例 × 移動距離佔視窗比例;改用 session window 是避免長時間停留的頁面被無止境累加
第 7 段 → CLS:版面為什麼會亂跳
一次互動延遲的三段組成
INP 單次互動延遲由哪三段組成?主執行緒被長工作佔住主要放大哪幾段?
input delay、processing time、presentation delay;長工作主要放大第一段與第三段,不是事件處理器本身變慢
第 10 段 → INP:主執行緒被長工作卡住 · input delay
什麼情況該用 Web Worker 而不是切塊
task chunking 跟 Web Worker 該怎麼選?
運算完全不碰 DOM 時丟給 Web Worker 最徹底;需要在主執行緒上、或中途要更新畫面時才用分塊
第 12 段 → 修法:切成小塊並交還控制權 · Web Worker

JavaScript Visualized - Event Loop, Web APIs, (Micro)task Queue

Lydia Hallie JavaScript 執行模型 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

自己動手排列組合 setTimeout 與 queueMicrotask
  1. 寫一支只有 console.log 和 setTimeout(fn,0) 的腳本,確認同步永遠贏
  2. 加一個 Promise.resolve().then,確認 microtask 贏過 task
  3. 把 .then 換成巢狀 queueMicrotask,重現 4 插在 2 前面
  4. 每次先寫下預測順序再執行,對答案
今天就做 寫一支混用 setTimeout、Promise.resolve().then、巢狀 queueMicrotask 的腳本,先手寫預測順序再執行驗證
第 12 段 → 自己動手試才會真的懂

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

非同步 Web API 呼叫像轉一下門把,登記完立刻放手
新 呼叫非同步 web API 只是轉一下門把,登記完立刻放手,門後的瀏覽器繼續做事  ⇄  像 櫃檯人員收下你的申請單就讓你先離開,不用站在櫃檯前等辦完
第 4 段 → Web APIs:把長任務交給瀏覽器 · offloading

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

Call stack:唯一決定執行順序的堆疊,先進後出,函式呼叫推入、執行完彈出
call stack
第 2 段 → 單一 call stack 怎麼跑完一支程式 · call stack
阻塞:某段程式長時間占住 call stack,導致後面所有工作(含算繪、點擊)都無法進行
blocking
第 3 段 → 單執行緒的代價:長任務會凍結畫面 · blocking
Web API:瀏覽器提供給 JS 用的介面,不屬於 JS 語言本身,不全是非同步的
Web API
第 4 段 → Web APIs:把長任務交給瀏覽器 · Web API
卸載:發起呼叫仍上 call stack 但只做登記交接,真正工作在瀏覽器背景跑,不占堆疊
offloading
第 4 段 → Web APIs:把長任務交給瀏覽器 · offloading
Task queue:存放 web API callback 與事件處理函式的先進先出佇列
task queue
第 6 段 → Task queue 與 event loop 的分工 · task queue
Event loop:不執行程式碼,只不斷檢查 call stack 是否為空,空了就搬一個任務上去
event loop
第 6 段 → Task queue 與 event loop 的分工 · event loop
setTimeout:延遲是到「進入 task queue」為止,不是到「執行」為止
setTimeout
第 7 段 → setTimeout:延遲是到佇列,不是到執行 · setTimeout
Microtask queue:優先權高於 task queue,call stack 一空就要整個清空才碰 task queue
microtask queue
第 8 段 → Microtask queue 有優先權 · microtask queue
微任務餓死:microtask 不斷排入新的 microtask,佇列永遠清不空,task queue 永遠輪不到
microtask starvation
第 8 段 → Microtask queue 有優先權 · microtask starvation
微任務檢查點:event loop 在每個 task 前後把 microtask queue 徹底清空才繼續
microtask checkpoint
第 10 段 → 小測驗:為什麼答案是 51342 · microtask checkpoint

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

getCurrentPosition:登記完立刻彈出,等待完全不占 call stack
getCurrentPosition 呼叫被推上 call stack,但只是登記 success/error callback、啟動背景任務就立刻彈出;使用者可能十秒後才按授權視窗,這段等待完全不佔 call stack
證明 offloading
演練 這個例子證明了「發起呼叫」跟「真正等待」是兩件事嗎?使用者按下允許前 call stack 是什麼狀態?
第 5 段 → Callback 型 API:為什麼不能直接放回堆疊
51342 測驗:整個清空跟每輪取一個的差別
同步 console.log(5) 先印;Promise.resolve().then 的 1、3 在第一輪 checkpoint 清空;巢狀 queueMicrotask 排的 4 是清空過程中新增的仍算這輪;setTimeout 的 2 要等 checkpoint 徹底清空才輪到
證明 microtask checkpoint
演練 這題證明了「整個清空」跟「每輪取一個」的差別在哪?如果改成每輪只取一個,4 會排到第幾?
第 10 段 → 小測驗:為什麼答案是 51342

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

engine 跟 runtime 不是同一件事
JavaScript engine 跟 runtime 差在哪?setTimeout 屬於哪一個?
engine(如 V8)只管解析執行 JS;runtime 是 engine 加上宿主提供的 web APIs、佇列、event loop。setTimeout 屬於後者
第 1 段 → Event Loop 只是 runtime 的一個小零件 · JavaScript runtime JavaScript engine
memory heap 跟 call stack 分工
memory heap 跟 call stack 分工差在哪?
heap 放資料(物件、變數),stack 記執行順序;垃圾回收處理 heap,這支影片全程談 stack 的推入彈出
第 1 段 → Event Loop 只是 runtime 的一個小零件 · memory heap
被推上堆疊的是呼叫不是函式本身
被推上 call stack 的是函式本身還是別的東西?
是 execution context(這一次呼叫),不是函式本身;同一個函式呼叫兩次就有兩個 execution context
第 2 段 → 單一 call stack 怎麼跑完一支程式 · execution context single-threaded
長任務其實有兩種
「長任務」其實有兩種完全不同的東西,是什麼?
一種是 CPU 真的在算(只能用 Web Worker 搬走);一種是在等外部結果,這種才是 web API 能解決的
第 3 段 → 單執行緒的代價:長任務會凍結畫面 · long-running task
callback 為什麼不能直接插隊
為什麼 callback 資料回來後不能直接插回 call stack?
JavaScript 保證 run-to-completion;讓 callback 插隊會破壞這個保證,變數可能被改到一半
第 5 段 → Callback 型 API:為什麼不能直接放回堆疊 · callback callback-based API
卡住時狂點按鈕為什麼沒反應
頁面卡住時狂點按鈕為什麼沒反應,長任務結束後又突然全部觸發?
事件處理函式跟 web API callback 走同一條路,都排進 task queue 等 call stack 空;長任務結束後一次被搬上去
第 6 段 → Task queue 與 event loop 的分工 · event handler task
setTimeout(fn,0) 不是立刻執行
setTimeout(fn, 0) 會立刻執行嗎?
不會,仍要走完整條回程路(登記→到期→排進 task queue→等堆疊空),同步 console.log 永遠先贏
第 7 段 → setTimeout:延遲是到佇列,不是到執行 · setTimeout timers API
fetch 呼叫時 call stack 上發生什麼
fetch 呼叫時 call stack 上實際發生什麼?
只做兩件事:建立 pending 的 promise、發動背景請求,就立刻彈出;回應回來才把 handler 排進 microtask queue
第 8 段 → Microtask queue 有優先權 · promise-based API fetch
.then 本身是同步的
.then 本身是同步還是非同步執行的?它做了什麼?
.then 本身同步執行,做的是把 handler 登記進 promise 的 reaction 清單;落定後 handler 才排進 microtask queue
第 8 段 → Microtask queue 有優先權 · promise reaction record
queueMicrotask 跟 MutationObserver 走哪條佇列
queueMicrotask 跟 MutationObserver 的 callback 走哪條佇列?為什麼?
都走 microtask queue;這一輪的收尾必須早於使用者下一次互動或計時器
第 8 段 → Microtask queue 有優先權 · queueMicrotask MutationObserver
promisify 沒有改變底層機制
把 callback 型 API 包成 promise(promisify)後,底層機制變了嗎?
沒變,只是多繞一層:原本直接排進 task queue 的 callback,現在是 resolve 動作先排進去,落定後才排 microtask
第 9 段 → 把 callback 型 API 包成 promise · promisify Promise constructor
non-blocking 不等於多執行緒
non-blocking 是不是代表多執行緒?
不是,JavaScript 仍只有一條線,只是不在那條線上原地等待;靠的是把等待卸載出去,不是同時執行多段程式碼
第 3 段 → 單執行緒的代價:長任務會凍結畫面 · non-blocking single-threaded

JavaScript Visualized - Closures

Lydia Hallie JavaScript 執行模型 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

驗證自己的 factory function 各自獨立
  1. 找一個你寫過的、回傳內層函式的 factory function
  2. 畫出每次呼叫會產生幾份獨立的 environment record
  3. 呼叫兩次並預測各自的輸出
  4. 實際執行驗證
今天就做 拿你專案裡任一個回傳函式的 factory function,呼叫兩次並畫出各自的 closure record,驗證彼此互不干擾
第 9 段 → 小測驗:兩個 counter

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

互相參考的兩者,沒人從外面連進來還是一起沉
新 inner 和 outer record 互相參考成一個環,但只要沒人從外面指進來,整個環一起被清掉  ⇄  像 兩人互相拉著手在水裡,但沒人抓住岸邊的繩子,兩個一起沉
第 5 段 → 光有引用還不夠 · reachability

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

Closure:function object 加上本該被回收卻因被參考而保留的 environment record
closure
第 1 段 → 開場:closure 到底是什麼 · closure
Environment record:實際存放變數、參數、函式宣告的記憶體結構,context 只是參考它
environment record
第 2 段 → 複習:執行環境與環境記錄 · environment record
[[Environment]]:function object 建立時寫死的內部槽,記著它被定義時所在的 environment record
[[Environment]]
第 4 段 → 第一步:巢狀函式帶著 environment 屬性 · [[Environment]]
可達性:從根(global、call stack)出發走得到才留下,走不到就整包回收,即使互相參考
reachability
第 5 段 → 光有引用還不夠 · reachability
被保留的環境記錄:所屬函式已結束、context 已銷毀,卻因仍被某 function object 參考而沒被回收
retained environment record
第 6 段 → 把 inner 回傳出去:closure 成形 · retained environment record
Scope chain:呼叫時新 record 的 [[OuterEnv]] 抄自 [[Environment]],查不到就往外層找
scope chain
第 7 段 → 呼叫 closure:outer env 與 scope chain · scope chain [[OuterEnv]]
封裝:把狀態藏進外部存取不到的 environment record,只透過回傳的函式互動,是真正的存取隔離
encapsulation
第 10 段 → 解答:為什麼是 1、1、2 · encapsulation
Memoization:把計算結果依參數存進 cache,cache 要住在外層 record 才能跨呼叫存活
memoization
第 11 段 → 用途一:memoization 的快取 · memoization
記憶體洩漏:不是東西太大,是不再需要卻還留著一條從根走得到它的參考
memory leak
第 12 段 → 用途二的反面:意外的 closure · memory leak

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

290秒/345秒兩張圖:定義時的關係變成查找時真的被走過的路徑
inner record 的 [[OuterEnv]] 指 outer record、outer 指 global,三站鏈串好;inner 裡找不到 count,沿鏈跳到 outer 才找到
證明 scope chain
演練 這兩張圖證明了 scope chain 怎麼從「定義時的關係」變成「查找時真的被走過的路徑」?
第 7 段 → 呼叫 closure:outer env 與 scope chain
createUserManager:兩個小箭頭函式釘住整面 user 資料牆
createUserManager 抓了幾萬筆 user data,retrieve/update 兩個小箭頭函式各自用 [[Environment]] 把整面資料牆釘住
證明 memory leak
演練 這個例子證明了被保留的顆粒度是什麼?函式再小為什麼還是可能扣住整個作用域?
第 12 段 → 用途二的反面:意外的 closure

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

每呼叫一次函式引擎建立什麼
每呼叫一次函式,引擎建立了什麼兩個東西?誰住在哪裡?
一個 function execution context 加一個 function environment record;變數住在 environment record,context 只是持有它的參考
第 1 段 → 開場:closure 到底是什麼 · execution context environment record
creation phase 跟 TDZ 的關係
creation phase 跟 execution phase 分別做什麼?TDZ 錯誤怎麼來的?
creation phase 先配置記憶體(var 填 undefined,let/const 標 uninitialized);execution phase 才賦值,賦值前存取 let 就拋 TDZ
第 2 段 → 複習:執行環境與環境記錄 · creation phase execution phase
call stack 彈出的是什麼
call stack 彈出的是什麼?不必然是什麼?
彈出的是 execution context,不是函式物件,也不必然是 environment record
第 2 段 → 複習:執行環境與環境記錄 · call stack
垃圾回收的判準是可達性
JS 垃圾回收的判準是什麼?「函式執行完了」算不算理由?
判準是可達性:從根出發走不到才回收;函式執行完本身不是回收理由,走不到才是
第 3 段 → 正常情況:變數為什麼會消失 · garbage collection reference
function object、execution context、environment record 的差別
function object、execution context、environment record 三者的差別?
function object 是函式本身(一個);execution context 是某一次呼叫;environment record 是那次呼叫的變數,呼叫十次就十份
第 4 段 → 第一步:巢狀函式帶著 environment 屬性 · function object nested function
return inner 跟 return inner() 差一個字差很多
return inner 跟 return inner() 差在哪?為什麼這一字之差決定 closure 成不成立?
前者回傳函式物件本身,後者先執行再回傳結果;寫成 inner() 會讓 inner 立刻沒人參考,closure 就不存在
第 6 段 → 把 inner 回傳出去:closure 成形 · function declaration
語彙作用域為什麼不是動態的
為什麼 JavaScript 的作用域是語彙(lexical)的,不是動態的?
查找路徑抄自定義時寫死的 [[Environment]],跟誰在哪裡呼叫它無關;只有 this 的行為比較接近動態
第 7 段 → 呼叫 closure:outer env 與 scope chain · lexical scope
closure 成立需要語言有什麼前提
closure 成立需要語言有什麼前提?自由變數是什麼?
函式必須是一級函式(能被回傳、當值傳遞);自由變數是用到但不屬於自己的變數,closure 保留的是整份環境不只是它
第 8 段 → 總結 closure 的兩個條件 · free variable first-class function
1、1、2 而不是 1、2、3
createCounter 呼叫兩次,counter1()、counter2()、counter1() 依序輸出什麼?為什麼不是 1、2、3?
輸出 1、1、2;每次呼叫 createCounter 都產生一份全新獨立的 environment record,count 不是跟函式綁在一起的共用狀態
第 9 段 → 小測驗:兩個 counter · factory function
什麼函式適合 memoize
什麼樣的函式才適合 memoize?
純函式:同樣輸入永遠同樣輸出、沒有外部副作用;否則會回傳過期結果或吃掉副作用
第 11 段 → 用途一:memoization 的快取 · pure function cache
箭頭函式在 closure 上跟一般函式一樣
箭頭函式在 closure 這件事上跟一般函式有什麼不同?
完全一樣,一樣有 [[Environment]],一樣會扣住定義處的 record;箭頭函式特殊之處只在 this 沿 scope chain 決定
第 12 段 → 用途二的反面:意外的 closure · arrow function
怎麼驗證真的有意外 closure 洩漏
要驗證程式真的有意外 closure 洩漏,該怎麼做?
用 DevTools Memory 面板拍 heap snapshot,看目標物件的 retainer;路徑上出現 context 或 closure 節點就是被扣住了
第 13 段 → 收尾:closure 與效能意識 · heap snapshot retainer

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

檢查自己 API 是不是把授權判斷放在最接近資料的那一層
  1. 列出專案裡會讀寫他人資源的 endpoint
  2. 確認每個 endpoint 是否在存取資源那層檢查 role/permission
  3. 找出只在前端隱藏按鈕、或只在 gateway 檢查一次的地方,標記為待補
今天就做 挑一個會讀寫他人資料的 API endpoint,確認授權檢查是否落在存取資源那一層(約 20 分鐘)
第 6 段 → 每次存取都要再檢查一次
手動測一次自己的 API 有沒有 IDOR 漏洞
  1. 挑一個帶 id 的 API,例如 /orders/:id
  2. 換成別人的 id 重送同一個請求
  3. 確認回應是被拒絕而不是直接吐資料
今天就做 挑自己專案一個帶 id 的 API,把 id 換成別人的試著存取,確認有沒有 IDOR(約 15 分鐘)
第 4 段 → 認證擋不住的那件事

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

access token 是短期可撤銷的替身,密碼是外洩就永久失守的長期機密
新 access token:短期、有到期時間、可撤銷、可限定用途  ⇄  像 密碼:一旦外洩,拿到的人有永久的通行權
第 3 段 → Web 版的認證:換成 token
把權限寫死成 if user.id === 42,就像程式裡到處散落的硬編碼特例
新 依身分寫死判斷式,等於把授權規則刻進程式碼裡  ⇄  像 常見的硬編碼特例(hardcoded special-case):一開始省事,個案一多就沒人敢動
第 4 段 → 認證擋不住的那件事
臉、指紋外洩換不了,密碼外洩可以直接重設
新 生物特徵當作憑證:外洩後無法更換,只能靠裝置端比對降低風險  ⇄  像 密碼外洩可以立刻要求使用者重設一組新的
第 8 段 → 並排總結:一次 vs 每次

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

Authentication:確認你是誰;問句是「你是誰」
authentication
第 1 段 → 兩個最常被混淆的字 · authentication
Authorization:確認已認證的人能做什麼;問句是「你能不能」
authorization
第 1 段 → 兩個最常被混淆的字 · authorization
Access token:短期通行證,編碼了身分與角色,取代每次重送密碼
access token
第 3 段 → Web 版的認證:換成 token · access token
Claim:token 裡編碼的一項宣稱,例如 sub 是誰、exp 何時過期、roles 是角色
claim
第 3 段 → Web 版的認證:換成 token · claim
Role:通行證上編碼的身分分類,給人看的「你是哪一類人」
role
第 2 段 → 辦公大樓比喻:進得了門 · role
Permission:給資源看的「可以做哪一件事」,比 role 更細一層
permission
第 7 段 → 會計軟體:role 包住 permission
IDOR:改網址上的 id 就看到別人的資源,認證通過但授權缺失的典型漏洞
IDOR
第 4 段 → 認證擋不住的那件事 · IDOR
Deny by default:讀卡機沒看到允許依據就拒絕,漏寫規則的後果是打不開門
deny by default
第 5 段 → 拿著通行證仍被擋在門外 · deny by default
RBAC:以 role 為單位做授權判斷,檢查 access token 上的 roles
RBAC
第 6 段 → 每次存取都要再檢查一次 · RBAC
Principle of least privilege:新角色從零權限開始逐項加,不是從管理員複製再刪
principle of least privilege
第 7 段 → 會計軟體:role 包住 permission · principle of least privilege

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

把網址 /invoices/1001 改成 /invoices/1002,直接看到別人的帳單
攻擊者完全合法登入、token 完全有效、每次請求都通過認證,卻拿到不屬於他的資源
證明 IDOR
演練 這個例子證明認證通過不代表什麼?你的專案有沒有帶 id 的 API 沒做這層檢查?
第 4 段 → 認證擋不住的那件事
同一人同一張通行證:刷實驗室門紅燈,刷自己辦公室門綠燈
認證這個變數被釘死不變(同一人已通過認證、人就在大樓裡),差別只出在那扇門要求的角色
證明 authorization
演練 紅燈綠燈的差別證明授權判斷的兩個輸入各來自哪裡?為什麼授權要逐扇門各判各的?
第 5 段 → 拿著通行證仍被擋在門外
付款管理員 role 包含 create payments 與 read payments 兩個 permission
使用者要建立付款時,系統檢查有沒有 payment administrator 這個 role,或有沒有 create payments 這個 permission,兩者滿足其一才放行
證明 role
演練 這個例子怎麼示範 role 與 permission 分兩層?你會怎麼替自己專案的一個功能設計對應的 role?
第 7 段 → 會計軟體:role 包住 permission
只在前端隱藏按鈕、或只在 gateway 檢查一次,都不是真正的授權檢查點
前端沒顯示按鈕但 API 仍照收請求,curl 直接繞過;只在入口檢查一次,內部服務互信,入口一被繞過就全面失守
證明 authorization
演練 這兩種失敗模式證明授權檢查該放在哪一層?你的專案有沒有只在前端做的權限判斷?
第 6 段 → 每次存取都要再檢查一次
401 正式名稱是 Unauthorized,但意思其實是「還沒認證」不是「沒權限」
401 對應認證失敗、403 Forbidden 才對應授權失敗;401 的命名是規格上的歷史誤稱
證明 authentication
演練 看到 401 時該懷疑的是認證還是授權?這個誤稱怎麼影響你讀別人 API 文件的方式?
第 8 段 → 並排總結:一次 vs 每次

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

Multi-factor authentication:識別碼+臉部掃描,兩步缺一步就能被冒充
只靠輸入識別碼、沒有第二步驗證,會有什麼風險?
任何知道識別碼的人都能冒充你,所以要加一步驗證(如臉部掃描)確認宣稱為真
第 2 段 → 辦公大樓比喻:進得了門 · multi-factor authentication
Session 與 access token 的頻率關係
認證與授權各多久做一次?
認證通常一個 session 一次;授權每次存取資源都要做
第 3 段 → Web 版的認證:換成 token · session
Token revocation:只檢查 token 裡的 roles 有個代價
為什麼只靠 token 裡的 roles 做授權檢查有風險?
token 到期前難以即時撤銷,角色中途被拿掉,舊 token 仍可能通過檢查
第 6 段 → 每次存取都要再檢查一次 · token revocation
角色數量失控時該換的模型
role 開始切成「北區付款管理員」「南區付款管理員」這種角色爆炸時,該換什麼模型?
ABAC(屬性存取控制):用屬性(如部門)判斷,而不是再切更多角色
第 7 段 → 會計軟體:role 包住 permission · ABAC
Identity Provider/Auth0:可以外包的是認證的粗活
Auth0 這類 Identity Provider 能代勞的是認證還是授權?為什麼?
認證;密碼儲存、多因素、帳號復原等每個產品需求相近,授權規則來自你自己的業務,沒人能替你決定
第 9 段 → 難的是實作,下一步是 Auth0 · Identity Provider Auth0 OpenID Connect
Anonymous access:認證擋下的是匿名使用者
認證主要擋住的是什麼身分的使用者?
匿名使用者(anonymous access);已通過認證的內部使用者要靠授權才擋得住
第 4 段 → 認證擋不住的那件事 · anonymous access

How to scale WebSockets to millions of connections

Ably Realtime 網路與 API 基礎 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

檢查自己專案的重連邏輯
  1. 找出你的 WebSocket/長連線客戶端目前的重連邏輯
  2. 確認斷線後是不是固定間隔立刻重連
  3. 改成指數退避+隨機 jitter
  4. 模擬多個客戶端同時斷線,確認重連請求有被打散
今天就做 檢查你專案裡任一個會自動重連的用戶端,確認有沒有指數退避+jitter,沒有就加上去
第 7 段 → 水平擴展的挑戰
查自己的 load balancer 用哪種演算法
  1. 列出你目前用的 load balancer
  2. 查它預設的分配演算法是什麼
  3. 評估你的連線是不是長命的(WebSocket/SSE)
  4. 決定要不要從 round robin 換成 least connections
今天就做 查你專案目前用的 load balancer 用哪種演算法分配連線,寫下如果是長連線服務要不要換成 least connections
第 5 段 → 水平擴展與 load balancer

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

沒有 backplane 的兩台伺服器就像互不知道對方的孤島
新 連線被 load balancer 分到不同伺服器後,server 2 完全不知道 server 1 上發生了什麼  ⇄  像 兩座孤島之間沒有橋,各自的居民互不知道對方的事
第 6 段 → 狀態分裂與 backplane · backplane

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

WebSocket 是 stateful 長連線:伺服器要為每條連線常駐 memory/CPU;HTTP 是 stateless
stateful
第 1 段 → 為什麼 WebSocket 難以擴展 · stateful
垂直擴展:給同一台機器加資源,架構不用改,但只有一台,有物理上限
vertical scaling
第 3 段 → 兩種擴展方式與「一台能撐多少」 · vertical scaling
水平擴展:加多台機器+load balancer 分流,容量理論上沒上限,但狀態要重新設計
horizontal scaling
第 3 段 → 兩種擴展方式與「一台能撐多少」 · horizontal scaling
Load balancer:站在多台伺服器前分配連線;WebSocket 只在 handshake 分一次,之後固定黏住
load balancer
第 3 段 → 兩種擴展方式與「一台能撐多少」 · load balancer
單點故障:只要那一台掛了整個服務就停,錢能買更大機器但買不掉「只有一台」這個結構問題
single point of failure
第 4 段 → 垂直擴展的實務問題 · single point of failure
Pub/sub:發送方發布到頻道,所有訂閱的伺服器都收到,讓伺服器數量跟訊息路由解耦
pub/sub
第 6 段 → 狀態分裂與 backplane · pub/sub
Backplane:在多台伺服器背後加共享通道,集中的是訊息與狀態可見性,不是 socket 本身
backplane
第 6 段 → 狀態分裂與 backplane · backplane
Load shedding:伺服器快到上限時先拒絕新連線、再踢掉部分既有連線,讓它們重連到健康節點
load shedding
第 7 段 → 水平擴展的挑戰 · load shedding
驚群效應:大量連線同時重連,瞬間壓垮目標伺服器,可能連鎖再掛一台
thundering herd
第 7 段 → 水平擴展的挑戰 · thundering herd
決策框架:用停機代價換架構複雜度——代價低先單台,不能停機的服務一開始就投資水平擴展
business continuity
第 8 段 → 結論:依需求選擇 · business continuity

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

垂直擴展的四個實務問題
貴(按峰值配置)、撞資源上限(kernel 參數)、單點故障阻礙持續部署、memory leak 或 AZ 故障會讓整台下線
證明 single point of failure
演練 這四個問題證明了什麼共同結論?其中哪些能用錢解決,哪些不能?
第 4 段 → 垂直擴展的實務問題
同一張圖三階段:分散→無連結→加 Redis 後雙向連通
A/B 各自連到不同伺服器、兩台間無連結、加上 Redis 後兩台各有雙向線連到 Redis 形成訊息路徑
證明 backplane
演練 這張圖證明了加 Redis 前後,A 傳訊息給 B 的路徑分別是什麼?
第 6 段 → 狀態分裂與 backplane

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

full-duplex 跟 HTTP 半雙工的差別
full-duplex 是什麼?跟 HTTP 的半雙工差在哪?
連線兩端可同時互相傳送資料;HTTP 是一問一答,伺服器不能主動推
第 1 段 → 為什麼 WebSocket 難以擴展 · full-duplex
benchmark 沒算到的三項成本
原生 WebSocket 沒附的電池裡,最容易被 benchmark 忽略的三項是什麼?
heartbeat(心跳)、buffering(訊息緩衝)、backpressure(背壓)——每項都要為每條連線多佔資源
第 2 段 → 為什麼 WebSocket benchmark 會誤導 · heartbeat buffering backpressure
fallback 為什麼要用不同擴展思路
為什麼要準備 HTTP long polling 當 fallback?它的擴展方式為什麼跟 WebSocket 不同?
防火牆/proxy 可能擋掉 WebSocket;long polling 是一串短命 HTTP 請求,要用 stateless 的擴展思路
第 2 段 → 為什麼 WebSocket benchmark 會誤導 · fallback transport HTTP long polling
按峰值配置的浪費怎麼解
垂直擴展下「按峰值配置」浪費在哪?水平擴展怎麼解?
只能為最高同時連線數買機器,離峰時資源閒置照付錢;水平擴展可動態加減機器(autoscaling)
第 4 段 → 垂直擴展的實務問題 · provisioning autoscaling
垂直擴展最先撞到的資源限制
垂直擴展撞到的資源限制通常先是什麼,要怎麼調?
檔案描述符上限與記憶體,要調 kernel 參數(如 ulimit、fs.file-max);調錯可能讓機器不穩
第 4 段 → 垂直擴展的實務問題 · kernel parameters ephemeral port
單台伺服器是持續部署的反面
單台伺服器為什麼是持續部署的反面?除了程式錯誤還有什麼會讓它整個下線?
每次部署都要重啟、所有連線一起斷,只能挑離峰少做;memory leak 或可用區(AZ)故障也會讓整台下線
第 4 段 → 垂直擴展的實務問題 · continuous deployment memory leak availability zone
round robin 對長連線不一定公平
Round robin 對短命 HTTP 請求跟長命 WebSocket 連線的效果為什麼不一樣?
HTTP 請求短命輪流分配就平均;WebSocket 分配後不會再移動,活躍度不同會讓負載失衡,常需改用 least connections
第 5 段 → 水平擴展與 load balancer · round robin load balancing algorithm
message broker 的角色與 Redis 為何常被選用
Message broker 在這裡扮演什麼角色?為什麼 Redis 常被選來當 backplane?
讓伺服器不用互相認識,交給 broker 後持有目標連線的那台自然收到;Redis 延遲低、部署簡單、內建 pub/sub
第 6 段 → 狀態分裂與 backplane · message broker Redis
presence 與 Redis 自身的複製
水平擴展下要怎麼讓「A 離線」通知到 B?Redis 本身又要怎麼避免變成新的單點?
presence:每台伺服器把連線寫進 Redis 並帶 TTL,斷線自動過期;Redis 要做複製(Sentinel/Cluster)避免變單點
第 7 段 → 水平擴展的挑戰 · presence replication
指數退避怎麼打散驚群
指數退避怎麼解決驚群效應?
每次重連失敗等待時間加倍(1s、2s、4s…)並加隨機 jitter,把同時斷線的客戶端重連時間打散
第 7 段 → 水平擴展的挑戰 · exponential backoff
程式寫得好能不能消除單點故障
單台伺服器「程式寫得好、容錯」能不能消除單點故障?
能降低(例外不崩潰、資源保護),但機器或可用區掛掉時再好的程式也救不了
第 8 段 → 結論:依需求選擇 · fault tolerance
「一台能撐多少」是問錯的問題
「一台伺服器能撐多少 WebSocket 連線」為什麼是個問錯的問題?
沒有兩條連線一樣、活躍度不同,連線數不是好的容量單位;而且只有一台終究是單點故障
第 3 段 → 兩種擴展方式與「一台能撐多少」 · vertical scaling

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

今天讀一份新東西時做三步驟
  1. 感到困惑時不當成失敗,繼續讀下去
  2. 讀得很順、不困惑時,主動問「這對我的工作有什麼影響」
  3. 把困惑翻成「有答案會比較不困惑」的問句,寫成清單
  4. 逐一回答,讓新困惑引出下一輪問題
今天就做 今天讀一份新文件或文章,讀完問自己「這對我正在做的事有什麼影響」,把冒出來的困惑寫成 3 個問句
第 6 段 → 適用任何領域:簡單就有效

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

困惑但活躍的大腦,像偵探盯著線索板找關聯
新 困惑但高度活躍:線索都在,還不知道怎麼連,但腦子在拚命連  ⇄  像 偵探盯著線索板找關聯
第 2 段 → 困惑是學習的症狀 · confusion as a symptom of learning
困惑羅盤:困惑像指北針,指出該學什麼方向
新 困惑本身能被用來指出「該學什麼」的方向  ⇄  像 指北針/羅盤靠磁場指出方向
第 1 段 → 只教一招:Confusion Compass · confusion compass

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

困惑羅盤:把困惑當成指向「該學什麼」的羅盤,而非學不好的失敗訊號
confusion compass
第 1 段 → 只教一招:Confusion Compass · confusion compass
困惑是症狀:大腦正在把新資料歸位、組織時產生的副產物,不是學不好的證據
confusion as a symptom of learning
第 2 段 → 困惑是學習的症狀 · confusion as a symptom of learning
問題清單:困惑時寫下「有答案就會比較不困惑」的問題,是 compass 唯一的產物
question list
第 3 段 → 把困惑變成問題清單 · question list
主動觸發困惑:問「這對我的工作有什麼影響」,把新知識放回你學它的原因裡
context of why you're learning
第 4 段 → 範例:讀新 library 的文件 · context of why you're learning
知識缺口:已有理解跟新資料對不上的地方,寫成問題就是強迫自己看清楚它
knowledge gap
第 5 段 → 兩個好處與學習循環 · knowledge gap
學習循環:困惑→提問→回答→新困惑,反覆轉動;轉得越快學得越快
cycle of learning
第 5 段 → 兩個好處與學習循環 · cycle of learning
Overwhelmed:只加資料不提問,資料點越堆越多、彼此不連,多到不知從哪開始
overwhelmed
第 5 段 → 兩個好處與學習循環 · overwhelmed
投入產出比:學習效率不是工具多精緻,而是用最少操作換到最多學習
bang for buck
第 6 段 → 適用任何領域:簡單就有效 · bang for buck

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

工程師讀新 library 文件:把三團困惑翻成具體問句
從「跟現有 library 很像/看不出好處/不知道怎麼放進 workflow」翻成比較、好處、實作、挑戰、時間、成本、複雜度等問句
證明 context of why you're learning
演練 這個例子證明了「放回脈絡」怎麼把困惑具體化?你下次讀新文件會先問自己哪一句話?
第 4 段 → 範例:讀新 library 的文件
白板圖:紅色 DATA 箭頭只加問號,藍色 CONFUSION→QUESTIONS 才畫出綠線
大腦方框裡問號只有少數被綠線連起來;硬讀(紅箭頭)讓問號變多、綠線不變;轉成問題(藍色路徑)才畫出綠線
證明 cycle of learning
演練 這張圖證明了硬讀更多資料跟把困惑轉成問題,兩條路徑分別造成什麼結果?
第 5 段 → 兩個好處與學習循環

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

學習效率指的是策略不是天分
作者說的學習效率/outperform 99% 指的是什麼,不是什麼?
指策略帶來的效率而非天分;同樣時間學得更快更深,不是懂得比較多
第 1 段 → 只教一招:Confusion Compass · learning efficiency
困惑停在情緒層會浪費幾個月
困惑停在情緒層會怎樣?
只是一種不舒服的感覺,讓人想放棄或硬讀,一停就是幾小時到幾個月
第 2 段 → 困惑是學習的症狀 · emotional confusion
有目標的學習跟平常讀書差在哪
「有目標的學習策略」跟平常讀書差在哪?
學習單位不是頁數而是問題;能回答問題就是進度,問題都答完才算讀完
第 3 段 → 把困惑變成問題清單 · targeted learning strategy
完全不困惑是紅旗
讀新東西完全不困惑、眼神渙散代表什麼?
紅旗:大腦沒在連結資料點,通常是因為新資料沒有要嵌入的目的地
第 4 段 → 範例:讀新 library 的文件 · red flag: no confusion
困惑跟 overwhelmed 是健康與否的差別
困惑(confused)跟被淹沒(overwhelmed)差在哪?
困惑是有幾個點還沒連,是健康訊號;overwhelmed 是點太多線太少,是硬讀造成的
第 5 段 → 兩個好處與學習循環 · overwhelmed
費曼技巧與主動回想的共同結構
費曼技巧、主動回想(active recall)跟 confusion compass 有什麼共同結構?
都是先製造困惑(解釋不出來/想不起來),再把困惑對準缺口變成可行動的問題
第 6 段 → 適用任何領域:簡單就有效 · cycle of learning
判斷一個人能不能被教的三個動作
作者用來判斷一個人能不能被教的三個動作是什麼?
擁抱困惑(不當失敗)、沒困惑時主動觸發、把困惑寫成問題清單
第 6 段 → 適用任何領域:簡單就有效 · embrace / trigger / turn into questions

9 JavaScript Concepts That Got Me To Senior Dev

theSeniorDev JavaScript 執行模型 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

用這支片的驗收標準檢查自己:能不能只憑腦中模型、不查文件解釋一段程式碼的執行順序
  1. 挑一段自己專案裡含 setTimeout / Promise / async 的程式碼
  2. 不執行,先手寫預測 console 輸出順序
  3. 跑起來對答案,錯的地方回頭用 Event Loop 那張圖重推一次
今天就做 挑專案裡一段非同步程式碼,手寫預測輸出順序後執行對答案(約 15 分鐘)
第 1 段 → 問題設定:沒人說清楚的「基礎」
用永生根往外追的方法,檢查自己專案有沒有忘記清掉的監聽器
  1. 在專案裡搜尋所有 addEventListener
  2. 逐一確認元件卸載或分頁離開時有沒有對應的 removeEventListener
  3. 沒有的話判斷閉包裡抓著的變數是否可能造成洩漏
今天就做 搜尋自己一個專案的 addEventListener,列出還沒有對應 removeEventListener 的地方
第 5 段 → Closure 與垃圾回收

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

mark-and-sweep 回收就像資料庫的 soft delete:標記,不是真的立刻抹掉
新 mark-and-sweep:標記所有從根可達的物件,其餘視為垃圾等待回收  ⇄  像 soft delete:先標記刪除狀態,不馬上真的清掉那筆資料
第 5 段 → Closure 與垃圾回收
generator 保存整個執行位置與呼叫狀態,closure 只保存用到的變數
新 Generator Function 暫停時整個 execution context(含跑到哪一行)都被凍結  ⇄  像 Closure 只記住宣告時 lexical scope 裡被用到的變數值
第 8 段 → Generator:async/await 的另一半
async/await 只是長得像同步,底層還是 Promise 排隊,容易被誤當真的同步
新 await 之後的程式碼永遠要等到下一個 micro-task 才跑,不是原地繼續  ⇄  像 一般同步程式碼由上往下、當下就執行完
第 7 段 → async/await 是 Promise 的語法糖

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

Event Loop:call stack 清空後先清光 micro-task queue,再從 macro-task queue 取一個
Event Loop
第 2 段 → Event loop 的整體架構 · Event Loop
Macro-task Queue:裝 DOM event、Timer API 的 callback,每輪 event loop 只取一個出來跑
Macro-task Queue
第 2 段 → Event loop 的整體架構 · Macro-task Queue
Micro-task Queue:裝 Promise 的 callback,本輪內清空為止,途中新加的也會被吃掉
Micro-task Queue
第 2 段 → Event loop 的整體架構 · Micro-task Queue
Execution Context:函式碼、this、arguments 與變數對應表的集合,決定一次函式呼叫能看到什麼
Execution Context
第 4 段 → Execution context 與 scope chain · Execution Context
Scope Chain:內層 scope 是外層的延伸不是複本,只能由內往外找變數
Scope Chain
第 4 段 → Execution context 與 scope chain · Scope Chain
Closure:函式記住它宣告時 lexical scope 裡真正用到的變數,不是整條 scope chain
Closure
第 5 段 → Closure 與垃圾回收 · Closure
Garbage Collection:從永生的根往外追引用鏈,追得到的都保留,追不到的才可回收
Garbage Collection
第 5 段 → Closure 與垃圾回收 · Garbage Collection
Inversion of Control:Promise 把「何時執行、執行幾次」的控制權從對方手上拿回來
Inversion of Control
第 6 段 → 從 callback hell 到 Promise · Inversion of Control
async/await:await 右邊包成 Promise,函式在此暫停,後續本體排進 micro-task queue
async/await
第 7 段 → async/await 是 Promise 的語法糖 · async/await
Generator Function:呼叫只拿 iterator,next() 跑到 yield 回傳並凍結,下次從原位置續跑
Generator Function
第 8 段 → Generator:async/await 的另一半 · Generator Function

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

四行程式碼推演:console 輸出順序是 1、4、2、3,不是照原始碼順序
resolve() 當場同步發生,.then 排進 micro-task;setTimeout 計時器立刻到期但 callback 進 macro-task;就算延遲從 0 改成 1000 結果也不變
證明 Event Loop
演練 這題證明了什麼跟延遲長短無關的規則?換成你自己的一段非同步程式碼,你能照同樣的圖推出順序嗎?
第 3 段 → 用 console.log 順序驗證 event loop
字幕把 macro-task queue 誤聽成 micro-task queue,要對照畫面上紅/橘兩個佇列修正
畫面:紅色 Macro-task Queue 裝 event listener/Timer API 的 callback,橘色 Micro-task Queue 裝 Promise 的 callback;字幕聲音辨識把兩者混成同一個詞
證明 Macro-task Queue
演練 這個字幕錯誤如果沒發現,會讓你對下一段那題的推演得出什麼錯誤答案?
第 2 段 → Event loop 的整體架構
追蹤範例:DOM 上的 onclick 抓著整條函式鏈,只有沒人引用的變數能回收
onclick(永生根)→ calculateNetProfit → 用到 taxRateCorporate、calculateNetSales → calculateNetSales 用到 taxRateVAT;四個都動不了,唯獨沒被引用的 taxRateDividends 可回收
證明 Garbage Collection
演練 這條鏈證明了記憶體洩漏最常見的根因是什麼?你的專案有沒有忘記 removeEventListener 的地方?
第 5 段 → Closure 與垃圾回收
三個各 1 秒的請求依序 await 要 3 秒,改用 Promise.all 只要 1 秒
把彼此無關的 await 一行行排下來等於強迫序列化;包進 Promise.all([...]) 才會同時發出
證明 async/await
演練 這個對比證明了 await 的什麼陷阱?你的專案裡有沒有可以改成 Promise.all 的連續 await?
第 7 段 → async/await 是 Promise 的語法糖

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

Call Stack:記錄目前正在執行的函式呼叫,LIFO
call stack 是什麼結構?裝的是什麼?
LIFO(後進先出)結構,裝目前正在執行、尚未 return 的函式呼叫
第 2 段 → Event loop 的整體架構 · Call Stack
Promise 的三種狀態
Promise 有哪三種狀態?可以逆轉嗎?
pending、fulfilled、rejected;一旦決議(fulfilled/rejected)就不能再變
第 6 段 → 從 callback hell 到 Promise · Promise States
error-first callback 慣例
Node.js 傳統 callback API 的參數順序是什麼?
第一個參數是錯誤(沒有就是 null),第二個參數才是資料
第 6 段 → 從 callback hell 到 Promise · Error-first Callback
yield 與 return 在 generator 裡的差別
generator 裡 yield 跟 return 差在哪?
yield 回傳值並凍結、之後能續跑;return 回傳值並結束,done 變 true
第 8 段 → Generator:async/await 的另一半 · yield
var 與 let/const 的 scope 差異
「大括號就是 scope」這個直覺對 var 成立嗎?
不成立;let/const 是 block scope,var 只認 function scope,會漏到整個函式
第 4 段 → Execution context 與 scope chain · Scope
Stack Frame 裡裝的東西就是 Execution Context
一個 stack frame 對應到什麼?
一次函式呼叫的 execution context:函式碼、this、arguments、變數對應表
第 4 段 → Execution context 與 scope chain · Stack Frame
這支片沒展開、但跟 execution context 同機制的下一塊拼圖
作者說這支片最欠缺、留給下一步的主題是什麼?
this 與原型鏈——跟 execution context 是同一個機制的兩面
第 9 段 → 結語與下一步

How Frontend Engineers can master the Full-stack

theSeniorDev 網路與 API 基礎 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

去看自己專案的快取設定
  1. 打開你專案任一頁面的 DevTools Network
  2. 點一個靜態資源看 Response Headers
  3. 找 Cache-Control 的 max-age 與 Age
  4. 算出還剩多少新鮮期,判斷是 public 還是 private
今天就做 打開你自己專案的 Network tab,檢查一個靜態資源的 Cache-Control/Age,算出還剩多少新鮮期
第 7 段 → HTTP 快取:Cache-Control 與 ETag
檢查自己專案的部署六件事
  1. 列出你的專案 build 產出哪些檔案
  2. 確認這些檔案被放到哪裡、誰在服務它們
  3. 檢查目前的快取設定(CDN/瀏覽器)
  4. 確認環境變數是建置時還是執行時注入
  5. 確認出事時怎麼回滾
今天就做 花 30 分鐘檢查你現在的專案:build 產出什麼、放哪裡、快取怎麼設、環境變數何時注入、怎麼回滾
第 17 段 → 基礎設施層與先找出自己的缺口

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

HTTP 請求與回應其實是同一種純文字格式的兩個方向
新 HTTP 請求/回應都是「起始行 + headers + 空行 + body」的純文字訊息  ⇄  像 像一封 email:信封資訊 + 附註 + 信件內容
第 3 段 → HTTP:REST 底下的請求與回應 · HTTP header
N+1 查詢就像對自己的資料庫發動 DDoS
新 GraphQL resolver 逐筆解析造成 1 次清單查詢加 N 次欄位查詢  ⇄  像 對自己的資料庫發動 DDoS
第 10 段 → 圖的彈性與 N+1 問題 · N+1 problem DataLoader

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

分層架構:前端→資料層→商業邏輯層→持久層→基礎設施層,只跟相鄰層對話
layered architecture
第 2 段 → 分層架構的全貌 · layered architecture
REST:用資源當名詞、HTTP 動詞當動作的一組 API 設計約定,不是協定
REST
第 4 段 → REST 端點設計與怎麼實際上手 · REST
HTTP 快取:靠 Cache-Control 判斷新鮮度,過期後靠 ETag 重新驗證
HTTP caching
第 7 段 → HTTP 快取:Cache-Control 與 ETag · HTTP caching
GraphQL:把資料形狀的決定權從後端移到客戶端,單一 query 拿齊所需欄位
GraphQL
第 8 段 → GraphQL 為何出現:under-fetching · GraphQL
BFF:專門服務某類客戶端的薄後端,把多個下游服務縫成剛好的一份回應
BFF
第 9 段 → BFF:後端為前端 · BFF
商業邏輯層(大腦):接住請求後驗證、套用商業規則,再呼叫資料庫
business logic layer
第 12 段 → 商業邏輯層:後端的大腦 · business logic layer
schema:SQL 在寫入時強制守住資料形狀,NoSQL 把這責任丟給讀取端
schema
第 14 段 → 持久層:NoSQL 與 SQL 怎麼選 · schema
CAP:分區容錯是網路系統的前提而非選項,真正的取捨在分區發生那一刻選 CP 或 AP
CAP theorem
第 15 段 → CAP 定理與讀寫分離 · CAP theorem
分片:依 shard key 把大表切到多個資料庫實例,唯一能同時擴展讀寫的手段
sharding
第 16 段 → 索引與分片 · sharding
能力圈:你真正理解、能判斷對錯的範圍;策略是待在圈裡慢慢往外推
circle of competence
第 18 段 → 從能力圈開始往外擴 · circle of competence

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

作者自己截圖:Cache-Control: public, max-age=86400 配 Age: 10843
已存約三小時、還剩約二十一小時的新鮮期示範,來自作者自己 DevTools 的 Response Headers
證明 HTTP caching
演練 max-age 跟 Age 各代表什麼?合起來能算出什麼?你會怎麼用它判斷一份資源還剩多少新鮮期?
第 7 段 → HTTP 快取:Cache-Control 與 ETag
MediaWiki 真實資料表:user、user_properties、user_groups、ipblocks
每個欄位都標明型別的實際 schema 示範,取自 MediaWiki 的資料表結構
證明 schema
演練 這個例子證明了 SQL schema 的什麼特性?要多加一個欄位,SQL 跟 NoSQL 分別要付什麼代價?
第 14 段 → 持久層:NoSQL 與 SQL 怎麼選

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

拿到 Swagger/OpenAPI 檔可以直接拿來做什麼
拿到一份 Swagger/OpenAPI 檔能直接拿來做什麼?
自動生成文件頁、型別安全的 API client、自動產測試
第 4 段 → REST 端點設計與怎麼實際上手 · OpenAPI Swagger
冪等與安全是兩個不同維度
冪等(idempotent)跟安全(safe)差在哪?DELETE 屬於哪一種?
冪等=重複執行結果一樣;安全=完全不改變狀態。DELETE 冪等但不安全
第 5 段 → 冪等性、網路分層與 TCP/IP · idempotency
no-cache 不是不快取
Cache-Control 的 no-cache 和 no-store 差在哪?
no-cache 可以存但每次要重新驗證;no-store 完全不存
第 7 段 → HTTP 快取:Cache-Control 與 ETag · Cache-Control
under-fetching 最痛的形式是瀑布式請求
under-fetching 最痛的形式是什麼?延遲為什麼會累加?
N+1 型瀑布請求:第二次請求要等第一次回來才知道打誰,延遲是倍數不是總和
第 8 段 → GraphQL 為何出現:under-fetching · under-fetching
DataLoader 是批次不是跨請求快取
DataLoader 怎麼運作?它是快取嗎?
把同一輪事件迴圈內的取值請求攢成一次批次查詢;快取只活在單次請求內
第 10 段 → 圖的彈性與 N+1 問題 · DataLoader
SSE 跟 WebSocket 的選用判準
什麼時候用 SSE、什麼時候該用 WebSocket?
客戶端送的訊息遠少於伺服器送的用 SSE;需要頻繁雙向(協作、遊戲、聊天)才用 WebSocket
第 11 段 → 資料串流:WebSocket 與 SSE · WebSocket Server-Sent Events
JWT 的三個必知陷阱
JWT 有哪三個必知的陷阱?
payload 是編碼非加密(人人可讀);簽發後無法單獨撤銷;alg 若接受 none 有致命漏洞
第 12 段 → 商業邏輯層:後端的大腦 · JWT
DTO 怎麼擋下 mass assignment
DTO 怎麼擋下 mass assignment 漏洞?
只有 DTO 宣告的欄位能通過;使用者偷塞 isAdmin:true 因不在宣告裡被丟棄
第 13 段 → 清洗驗證與 DTO · DTO
read replica 完全不解決寫入壓力
讀寫分離(read replica)解決什麼問題?完全不解決什麼?
解決讀取併發量;完全不減輕寫入壓力,寫入仍全部落在 master
第 15 段 → CAP 定理與讀寫分離 · read replica
索引用寫入速度換讀取速度
索引用什麼換取查詢加速?什麼情況會讓索引失效?
用寫入速度與儲存空間換讀取速度;對欄位運算、前綴萬用字元 LIKE、複合索引跳過前面欄位都會讓索引失效
第 16 段 → 索引與分片 · database index

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

依四層(元件/狀態/載入/bundle)做一次效能檢查
  1. 檢查元件層:有沒有不必要的re-render(用React DevTools Profiler的highlight updates看)
  2. 檢查狀態層:有沒有結構不良或prop drilling造成連鎖re-render
  3. 檢查載入層:重元件改用dynamic import + Suspense
  4. 檢查bundle層:大小、tree shaking、gzip有沒有做
今天就做 挑你專案裡一個覺得慢的畫面,依這四層(元件/狀態/載入/bundle)各檢查一項,寫下發現
第 1 段 → 效能優化:從程式碼內到打包外
判斷邏輯該寫進事件處理器還是useEffect
  1. 問自己:這段邏輯是回應使用者操作而發生的,還是要跟外部系統同步
  2. 是回應操作(如按鈕點擊)就直接寫在事件處理器裡,不要包進useEffect
  3. 只有真的需要跟外部系統同步(訂閱、連線、計時器隨時間自動更新)才用useEffect
  4. 把邏輯抽成有意義命名的函式(如startCounter/stopCounter),不要用字串參數分流
今天就做 檢查你專案裡一個useEffect,問它是不是其實只是在回應使用者操作,是的話把它搬進事件處理器裡
第 15 段 → hook 不能巢狀,與命名的建議
把計時器id存進ref、啟動前檢查、停止後設回null
  1. 判斷這個值改變時畫面需不需要跟著變,不需要就放ref、需要就放state
  2. start前檢查ref.current是否為空,為空才setInterval並把id存進ref.current
  3. stop時clearInterval(ref.current)之後把ref.current設回null
  4. 不要額外造一個hasStarted之類的state,ref有沒有值本身就是答案
今天就做 寫一個用setInterval的元件(如倒數計時器),照這個模式:ref存id、start前檢查、stop後設回null,確認連按start不會加速
第 17 段 → 真正的修法:清掉 ref 並在啟動前檢查

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

closure像搬到外地工作的人依然記得家鄉地址,即使已離開很久
新 closure讓函式記住並存取它出生時的外層變數,即使外層函式早已執行完畢  ⇄  像 搬到外地工作的人依然記得家鄉的地址和習慣,即使已經離開家鄉很久
第 5 段 → Closure:函式記得外層變數
useRef像口袋隨身記事本寫了馬上看得到;useState像公告欄要等下次巡視才更新
新 useRef改了立刻可讀,不觸發re-render;useState的更新是排程的,同一次render裡讀到的還是舊值  ⇄  像 口袋裡的記事本寫了馬上翻開就看得到最新內容;佈告欄貼新公告要等下一次巡視(重新渲染)才會被看到
第 7 段 → useRef 與 useState 的分工
ref有沒有值就是有沒有在跑的答案,像倉庫有沒有貨就代表訂單有沒有在處理
新 ref裡有沒有值本來就已經代表有沒有在跑,不需要額外的hasStarted state  ⇄  像 倉庫有沒有貨就代表訂單有沒有在處理中,不需要額外掛一個『處理中』的牌子,那個牌子遲早會跟倉庫實際狀態對不上
第 17 段 → 真正的修法:清掉 ref 並在啟動前檢查

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

React維護virtual DOM,狀態變動時用diffing演算法比對,只把差異寫回真實DOM
reconciliation
第 2 段 → Reconciliation 與 diffing 演算法 · reconciliation virtual DOM diffing algorithm
解析HTML/CSS建出DOM與CSSOM→算layout→paint→合成後交GPU輸出像素
critical rendering path
第 3 段 → 瀏覽器渲染流程:Critical Rendering Path · critical rendering path DOM CSSOM layout paint
debounce是延遲後只發一次,throttle是每段區間內最多發一次
debounce
第 4 段 → Debounce 與 Throttle 的差別 · debounce throttle
函式記得並能存取它出生時的外層變數,即使外層函式早已執行完畢
closure
第 5 段 → Closure:函式記得外層變數 · closure
this指向呼叫該函式的物件;箭頭函式沒有自己的this,從語彙作用域往外拿
this
第 6 段 → this 的指向與三種調整方式 · this arrow function bind / call / apply
useRef記住跨re-render仍存在的值,改它不會觸發re-render;useState才會
useRef
第 7 段 → useRef 與 useState 的分工 · useRef useState
hooks之後生命週期方法改由useEffect承接,依賴陣列決定何時重跑
useEffect
第 8 段 → 生命週期方法到 useEffect · useEffect dependency array lifecycle methods
任何元件出錯時全域捕捉、避免整個應用崩潰;目前只有class元件有原生解
error boundary
第 10 段 → 全域錯誤邊界:卡關與正解 · error boundary componentDidCatch class component
WebSocket建立連線後持續推資料給客戶端,不必一直發請求,跟HTTP請求/回應相反
WebSocket
第 12 段 → WebSocket 與請求/回應模型的差別 · WebSocket Server-Sent Events polling
hook只能在元件或自訂hook的最頂層呼叫,不能寫在條件、迴圈或巢狀函式裡
Rules of Hooks
第 15 段 → hook 不能巢狀,與命名的建議 · Rules of Hooks

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

diffing演算法的兩個關鍵假設:不同型別整棵重建、同層子元素靠key配對
第一,不同型別的元素直接整棵重建,不往下比;第二,同一層的子元素靠key來配對,這就是列表沒給key、或拿陣列index當key會出錯的原因
證明 reconciliation
演練 這兩個假設證明了為什麼列表沒給key或拿index當key會出錯?memo擋re-render為什麼比讓diffing慢慢比對更好?
第 2 段 → Reconciliation 與 diffing 演算法
stop按鈕寫onClick={() => clearInterval(1000)},把延遲毫秒數當成計時器id傳了進去
clearInterval收到不存在的id只會靜靜地什麼都不做,不會報錯;這正是id卡在closure裡拿不到、bug難找的具體樣子
證明 closure
演練 這行程式碼證明了『id卡在closure裡拿不到』這個問題具體長什麼樣?為什麼這種bug不會報錯、特別難找?
第 14 段 → clearInterval 失效:提示改用 useRef
受試者想到debounce時自己發現不行——就算兩次點擊隔十秒仍各觸發一次
問題不是『點擊太密集』而是每次點擊都合法地又開一個計時器,就算兩次點擊隔十秒,一樣會有兩個interval在跑
證明 debounce
演練 這個反例證明了debounce解決的是什麼問題?連按start的bug真正的病灶跟這個有什麼不同?
第 16 段 → 連按 start 的 bug 與錯誤方向
編輯器跳出Type 'Timeout' is not assignable to type 'null'的錯誤
ref的型別被宣告成可以是null,但程式從來沒有把它設回null過——這是ref沒被清乾淨、狀態不可信的具體線索
證明 useRef
演練 這個TypeScript錯誤是什麼線索?它跟『ref沒有被設回null』這件事有什麼關係?
第 16 段 → 連按 start 的 bug 與錯誤方向

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

受試者的效能檢查順序
受試者的效能檢查順序是什麼?
由內而外:元件層(memoization擋不必要re-render)→狀態層(修prop drilling)→載入層(dynamic import+Suspense)→bundle層(大小/tree shaking/gzip)
第 1 段 → 效能優化:從程式碼內到打包外
critical rendering path漏掉的哪一層決定CSS屬性貴賤
critical rendering path漏掉的哪一層決定了CSS屬性的貴賤?
compositing(合成)層;改width/top會回到layout重算,改background-color從paint重來,改transform/opacity只動合成階段能完全交給GPU
第 3 段 → 瀏覽器渲染流程:Critical Rendering Path
選debounce還是throttle的判準
選debounce還是throttle的判準是什麼?
問中間過程的結果有沒有用;搜尋建議只有最後一次輸入有意義用debounce,捲動/拖曳這種要即時反饋用throttle
第 4 段 → Debounce 與 Throttle 的差別
this的四種綁定優先順序
this的四種綁定優先順序是什麼?
new綁定>明確綁定(bind/call/apply)>隱式綁定(obj.fn())>預設綁定(非嚴格模式globalThis、嚴格模式undefined);箭頭函式不參與這套規則
第 6 段 → this 的指向與三種調整方式
useEffect依賴陣列的思考方式跟lifecycle有什麼不同
useEffect依賴陣列的思考方式跟lifecycle有什麼不同?
不是『在哪個時間點做什麼』,而是『這個effect要跟哪些值同步』;依賴陣列不是效能開關,是宣告用到了哪些值,漏填會造成stale closure
第 8 段 → 生命週期方法到 useEffect
SSR改善的是什麼時間,不是什麼時間
SSR改善的是什麼時間,不是什麼時間?
改善『看得到』的時間(FCP/LCP);『用得動』的時間要等JS下載完成hydration,有時甚至會變晚
第 9 段 → SSR 為什麼快,又為什麼利於 SEO
error boundary攔不住哪些錯誤
error boundary攔不住哪些錯誤?
只攔得住render階段的錯誤;事件處理器、setTimeout、非同步程式碼裡的錯誤一律接不到,那些要靠try/catch
第 10 段 → 全域錯誤邊界:卡關與正解
虛擬列表最難的部分
虛擬列表最難的部分是什麼?
不是只渲染看得到的項目,而是撐出正確的捲軸高度、處理高度不固定的項目,要邊捲邊量測動態修正
第 11 段 → 一萬筆列表與 code splitting
WebSocket的room是原生功能嗎
WebSocket的room是原生功能嗎?
不是;原生WebSocket只給一條雙向管線,room、自動重連、斷線緩衝都要自己或靠Socket.IO這類函式庫實作
第 12 段 → WebSocket 與請求/回應模型的差別
這題最終的完整解法是什麼形狀
這題最終的完整解法是什麼形狀?
start時檢查if(!ref.current)才setInterval並把id存進ref.current;stop時clearInterval(ref.current)後把ref.current設回null
第 17 段 → 真正的修法:清掉 ref 並在啟動前檢查

RxJS operators and their dangerous consequences

Joshua Morony 前端資料流 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

檢查用bufferCount的Subject有沒有出路
  1. 檢查你的bufferCount/bufferTime來源是Observable還是Subject
  2. 如果來源不會complete,加一個逾時(bufferTime)或手動flush的觸發條件
  3. 測試:故意送進一批數量對不齊bufferCount的值(不是它的倍數),確認最後幾筆有沒有出路
今天就做 檢查你專案裡任何一處用了bufferCount的Subject,確認它有沒有complete的時機,沒有就加一個timeout或手動flush
第 3 段 → buffer 的坑:來源不 complete,值就卡住
幫distinctUntilChanged加自訂comparator比對內容
  1. 找出你專案裡用distinctUntilChanged比對物件/陣列的地方
  2. 問自己:這裡要比的是內容還是同一個實體
  3. 要比內容就傳入自訂comparator(如JSON.stringify比對或指定欄位比對)
  4. 注意comparator參數順序是(previous, current)
今天就做 檢查你專案裡一處distinctUntilChanged比對object的地方,加上自訂comparator,確認行為有沒有變
第 7 段 → 自訂比較函式,用內容取代參照
把share/shareReplay的設定包成自訂operator重複使用
  1. 把shareReplay和share的設定物件寫清楚:connector用ReplaySubject
  2. resetOnRefCountZero設true(沒人訂閱就停)
  3. resetOnComplete設false(來源完成不重置)
  4. resetOnError設false(錯誤結果也保留,避免重打失敗請求)
  5. 包成一個自訂operator重複使用
今天就做 在你的專案裡建一個shareWhileSubscribed.ts,把這組設定包成自訂operator,換掉專案裡裸用share()或shareReplay()的地方
第 11 段 → refCount 才是真正的開關

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

buffer像蓄水池,要等閘門打開(complete)才會把剩餘的水一次放出來
新 bufferCount湊不滿一批的值,只有來源complete時才會被沖出來;來源不complete就永遠卡住  ⇄  像 蓄水池要等到閘門打開才會把剩下不滿一車斗的水一次放出去;沒人開閘門,水就一直積著
第 2 段 → buffer:把值攢起來再一起送
distinctUntilChanged比對象像檢查身分證字號,不是看長相像不像
新 distinctUntilChanged預設用===比對物件,比的是參照不是內容  ⇄  像 檢查身分證字號判斷是不是同一個人,而不是比對兩人的長相或穿著是否相似
第 6 段 → distinctUntilChanged 比的是參照不是內容
refCount像共享單車最後一人還車才收走;shareReplay預設沒人還車也不收
新 refCount追蹤目前有幾個訂閱掛著,歸零才重置;shareReplay預設不歸零重置,永遠不放手  ⇄  像 共享單車最後一個人還車,系統才把這台車釋放給下一批人使用;若系統設定『永遠不回收』,車子就一直佔用資源
第 10 段 → shareReplay 補上了洞,也開了新的洞

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

bufferCount湊不滿一批時,靠來源complete把剩餘值沖出來
bufferCount
第 2 段 → buffer:把值攢起來再一起送 · bufferCount complete
Subject不會自己complete,湊不滿一批的值就永遠卡在buffer裡
Subject
第 3 段 → buffer 的坑:來源不 complete,值就卡住 · Subject
throttleTime預設leading edge,窗內來源被忽略且不會補送
throttleTime
第 4 段 → throttle:反過來的問題,值被丟掉 · throttleTime interval
throttle可能吃掉最後一個值,把最終狀態的處理移到completion handler
completion handler
第 5 段 → throttle 的坑:最後一個值可能被吃掉 · completion handler take
distinctUntilChanged預設用===比對物件,比的是參照不是內容
distinctUntilChanged
第 6 段 → distinctUntilChanged 比的是參照不是內容 · distinctUntilChanged reference equality
傳自訂comparator接管distinctUntilChanged的判準,用內容取代參照
comparator
第 7 段 → 自訂比較函式,用內容取代參照 · comparator JSON.stringify
share在中間插一個Subject,讓多個訂閱共用同一條串流、來源邏輯只跑一次
share
第 8 段 → share:把 cold 變 hot,避免重複執行 · share cold observable hot observable multicast
share用refCount追蹤訂閱數,歸零就重置,遇到立刻完成的來源會重跑
refCount
第 9 段 → share 的坑:來源立刻完成,refCount 歸零 · refCount of
shareReplay補上晚到訂閱者的洞,但預設refCount為false會永不放手
shareReplay
第 10 段 → shareReplay 補上了洞,也開了新的洞 · shareReplay memory leak
resetOnRefCountZero=true+resetOnComplete=false,補齊share與shareReplay各自的洞
resetOnRefCountZero
第 11 段 → refCount 才是真正的開關 · resetOnRefCountZero ReplaySubject

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

輸出括號標註前三筆(3)、最後一筆(1),證明最後一批是提早沖出來的
console輸出的括號標註:前三筆是(3),最後一筆是(1)——大小不一樣,代表最後那批確實是被提早沖出來的,不是湊滿的
證明 bufferCount
演練 這個括號標註證明了什麼?如果bufferCount湊滿了才送出,最後一筆的括號應該是多少?
第 2 段 → buffer:把值攢起來再一起送
console印出4、6、8、10、12全是偶數,一半的值被throttle丟掉
throttleTime預設leading edge:第0個值立刻放行後關窗120ms;第1個值(100ms)在窗內被丟;第2個值(200ms)窗已在120ms關閉,放行後再關窗到320ms;於是穩定變成隔一個放一個
證明 throttleTime
演練 這組數字證明被丟掉的值取決於什麼?如果來源間隔與throttle間隔互質,結果會有什麼不同?
第 4 段 → throttle:反過來的問題,值被丟掉
兩個內容相同的物件被印成different;同一個變數放兩次卻只印一次
連續兩個內容相同的{title:'subscribe'}物件都被印出(判定different);同一個變數someObject連續放兩次,console只印出一次(判定相等)
證明 distinctUntilChanged
演練 這兩步證明了distinctUntilChanged真正比較的是什麼?你的資料經過JSON解析後還會是同一個參照嗎?
第 6 段 → distinctUntilChanged 比的是參照不是內容
of(1)同步完成:第一次subscribe那行還沒執行完,訂閱就已經解除
of(1)是同步的,第一次subscribe那行還沒執行完,值就已經送出、串流就已經complete、訂閱就已經解除了,下一行才輪到第二個subscribe,兩個訂閱在時間上根本沒重疊過
證明 refCount
演練 這個時序證明了同步來源會讓refCount發生什麼事?換成非同步的HTTP請求會有一樣的問題嗎?
第 9 段 → share 的坑:來源立刻完成,refCount 歸零

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

作者挑選這五個坑的標準
作者挑選這五個坑的標準是什麼?
只講他最近真的被咬過的;這些坑的共同形狀是絕大多數情況下行為都是對的,只有特定條件成立時才偏離直覺
第 1 段 → RxJS 的陷阱不在於難,而在於不明顯
作者在真實情境裡怎麼踩到buffer卡住這個坑
作者在真實情境裡是怎麼踩到buffer卡住這個坑的?
為了效能把工作批次化,先灌一批初始資料進去、之後再陸續加新的;初始筆數不被批次大小整除+來源不會結束,導致幾筆資料像被吞了,直到很久以後才冒出來
第 3 段 → buffer 的坑:來源不 complete,值就卡住
throttle吃掉最後一個值的bug為什麼特別難查
throttle吃掉最後一個值這個bug為什麼特別難查?
throttle窗口從上一個放行值開始算,最後一個值出現的時刻由來源決定,兩者沒有協調機制,導致同樣程式碼有時會有時不會,重現不了、測試寫不出來
第 5 段 → throttle 的坑:最後一個值可能被吃掉
distinctUntilChanged對mutation的反效果
distinctUntilChanged對mutation(就地修改物件)會有什麼反效果?
如果狀態管理裡就地修改同一個物件,它會判定參照相等而把真正的變更擋掉,比誤放行更難察覺
第 6 段 → distinctUntilChanged 比的是參照不是內容
cold observable和加share之後有什麼不同
cold observable和加了share之後有什麼不同?
cold的每個訂閱各自從0開始數、來源邏輯各跑一遍;share之後兩個訂閱收到同步的數字,來源邏輯只跑一次(share在中間插一個Subject)
第 8 段 → share:把 cold 變 hot,避免重複執行
為什麼真實HTTP請求通常不會踩到這個坑
為什麼真實的HTTP請求通常不會踩到share遇立即完成來源這個坑?
HTTP是非同步的,第二個訂閱會在回應回來之前就建立好,ref count從1變2而不是掉到0;只有像of()這種同步立刻完成的來源才會觸發
第 9 段 → share 的坑:來源立刻完成,refCount 歸零
share預設的重置條件除了refCount歸零還有什麼
share預設的重置條件除了refCount歸零還有什麼?
來源complete本身也會讓它重置(resetOnComplete預設為true);of()這個例子裡兩個條件同時成立
第 9 段 → share 的坑:來源立刻完成,refCount 歸零
shareReplay(1)的1是什麼,開大這個數字有什麼風險
shareReplay(1)括號裡的1是什麼?為什麼開大這個數字會放大風險?
是緩衝多少個值;開大或不設上限,沒人訂閱時仍握著的緩衝值(記憶體)也會跟著放大,洩漏量更大
第 10 段 → shareReplay 補上了洞,也開了新的洞
開了refCount之後,晚到訂閱者拿得到最新值的但書
開了refCount之後,晚到的訂閱者拿得到最新值這件事有什麼但書?
只在還有其他訂閱者在線上時成立;一旦人數歸零,緩衝的值也會跟著被丟掉
第 11 段 → refCount 才是真正的開關

Real Senior Frontend System Design Interview 2026 (AI Coding Included)

theSeniorDev 前端底層與系統設計 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

用刪去法從UI圈出真正需要的state
  1. 拿一張你產品的UI截圖
  2. 把明確不在這次討論範圍的區塊逐一劃掉,並口頭確認『先不做這塊』
  3. 剩下的區塊標出哪些重新整理才會變、哪些完全靜態
  4. 對每個會變的欄位問:能不能從別的欄位算出來,能就刪掉不單獨存
今天就做 挑你現在專案裡一個畫面,用刪去法圈出真正需要的state,寫出來看有沒有可以刪掉的衍生欄位
第 3 段 → 階段二起手:從 UI 圈出真正的 state
把state依『後端資料/使用者事件』分兩欄窮舉
  1. 列出這個元件所有會變的畫面內容
  2. 逐一歸類:是後端送來的資料,還是使用者操作觸發的
  3. 後端來源的一律配上data/loading/error三件套
  4. 使用者操作觸發的,想清楚它會不會重新觸發fetch(那是查詢參數,不是獨立狀態)
今天就做 挑你正在做的一個元件,把它的state依『後端資料/使用者事件』分兩欄列出來,檢查有沒有東西被你錯放進state但其實是衍生的
第 7 段 → 狀態轉移與狀態管理選型
攤開選項A/B/C而不是替對方做決定
  1. 遇到技術選型問題時,先列出2-3個可行方案(A/B/C)
  2. 為每個方案寫一句決策因素(例如要顧bundle size就選B)
  3. 說出你會選哪個、理由是什麼,但保留讓對方用他知道的限制條件推翻你的空間
  4. 確認對方沒有隱藏約束(既有技術棧、時程、合規)沒被你考慮到
今天就做 下次開技術選型會議或code review時,用『A、B、C,就目前情境我選B,因為X』的句型講一次,而不是直接下指令
第 16 段 → junior 與 senior 的差別,以及唯一的建議

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

功能性需求像車子『能不能載我到B』,非功能性像『要吃多少油』
新 功能性需求是圖表要做到什麼、按鈕要哪些;非功能性是怎麼做到(效能、無障礙、可測試)  ⇄  像 車子的功能性是『能不能把我從A載到B』;非功能性是『要吃多少油』
第 2 段 → 階段一:功能性與非功能性需求
統一程式碼格式,像工廠生產線要求零件規格統一才能快速抓異常
新 格式與寫法一致,讓六千行的agent PR review得動,不是品味問題是review可行性問題  ⇄  像 工廠生產線要求每個零件規格統一,壞掉時才能一眼看出哪裡不對,而不用先搞懂這顆零件本來長怎樣
第 9 段 → 建置工具與 AI 時代的程式碼品質
只存基準值+當前值就能算出所有變化,像記帳只記起訖餘額不用存每筆交易
新 折線、當前價、增減數字全部能從一個新資料點+進頁面時的起點算出來,不需要額外狀態  ⇄  像 記帳只要記住起始餘額跟目前餘額就能算出總變化,不需要把每一筆交易明細都存起來才能算差額
第 12 段 → 即時更新:SSE,以及把狀態壓到最小

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

功能性需求問『要做到什麼』,非功能性問『怎麼做到』;先掛號、不展開
Non-Functional Requirements
第 2 段 → 階段一:功能性與非功能性需求 · Non-Functional Requirements Functional Requirements SLO Core Web Vitals
用刪去法圈出state:先劃掉不在範圍的區塊,再判斷剩下的哪些會變
State
第 3 段 → 階段二起手:從 UI 圈出真正的 state · State
前端state只有兩個來源:後端資料與使用者事件,窮舉後只剩兩組state
State Transition
第 7 段 → 狀態轉移與狀態管理選型 · State Transition useState TanStack Query
最小可行前端:框架、設計系統與design token、CSS Modules、語意化HTML
Design System
第 8 段 → 最小可行前端:框架、設計系統、CSS、無障礙 · Design System Design Token CSS Modules Semantic HTML
格式與寫法一致,讓六千行的agent PR review得動——不是品味問題,是review可行性問題
Prettier
第 9 段 → 建置工具與 AI 時代的程式碼品質 · Prettier oxlint End-to-End Test
Web效能三維度:載入速度、輸入反應、版面穩定;手段落在bundle splitting與SSR
SSR
第 10 段 → Web 效能的三個維度 · SSR Bundle Splitting CLS
資料形狀落差大或服務不受自己控制時,用BFF當前端的穩定介面
BFF
第 11 段 → BFF:讓介面穩定下來 · BFF Reverse Proxy GraphQL
即時更新選SSE:判準是通訊是不是真的雙向,這裡只需要重繪不用送資料回去
Server-Sent Events
第 12 段 → 即時更新:SSE,以及把狀態壓到最小 · Server-Sent Events Polling WebSocket
讀寫比高就該快取:過去唯讀的資料點能套積極快取政策,未來的資料不能快取
Read/Write Ratio
第 13 段 → 快取:用讀寫比決定快取什麼 · Read/Write Ratio CDN HTTP Caching
模型缺乏視覺智能,先準備穩定錨點產物(設計系統、狀態圖、循序圖)讓它只做內插
Stable Artifacts
第 14 段 → AI 開發工具鏈:模型、harness、審查代理 · Stable Artifacts Harness Sequence Diagram

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

型別演進痕跡:兩個獨立型別合併成一個,price_final直接消失
s04_455還是DateInterval與PriceData兩個獨立型別,到s04_560合併成DataSeries內含start/end/price_to_beat/price_target,price_final(衍生自最後一個資料點)整個消失
證明 State
演練 這個型別演進證明了『先寫出來再刪掉』的什麼價值?刪掉price_final的理由是什麼?
第 4 段 → 把畫面翻成資料模型
AI設計state的第一版過度複雜,且把target_price併錯位置、漏欄位
作者把UI丟給Claude,第一版回來是過度複雜的資料庫層schema,AI把target_price併進chart_data、也漏掉一些欄位;判斷是『傾向膨脹』而非幻覺
證明 Stable Artifacts
演練 這個案例證明了AI在設計state時的什麼傾向?為什麼你要先有能力判斷哪些細節必要,AI才好用?
第 5 段 → 用 AI 幫忙設計 state 的界線
HTTP/1.1每網域只有六個連線,一頁十個圖表各開一條SSE會卡死
HTTP/1.1下每個網域同時只有六個連線,一個SSE連線會一直佔著;解法是共用一條連線、在事件裡帶asset id分流,或走HTTP/2讓多條流共用同一個TCP連線
證明 Server-Sent Events
演練 這個限制證明了SSE在多圖表頁面上會遇到什麼問題?你會怎麼設計來避免它?
第 12 段 → 即時更新:SSE,以及把狀態壓到最小
已結束區間回Cache-Control: public, max-age=31536000, immutable;含當下的區間回no-store
落實成HTTP標頭:已結束區間的資料回Cache-Control: public, max-age=31536000, immutable;包含當下的區間回no-store或極短的max-age
證明 Read/Write Ratio
演練 這組HTTP header的具體值證明了『過去唯讀』這個判準怎麼落地成快取政策?
第 13 段 → 快取:用讀寫比決定快取什麼

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

這場面試的限制條件是什麼
這場面試的限制條件是什麼?為什麼這個限制很重要?
不能用電腦、不能用IDE或AI;只能依賴腦中那條路線,這正是為什麼『心智里程碑』比任何單項技術都值錢
第 1 段 → 白板面試現場與「心智里程碑」
作者怎麼處理無障礙與安全這類非功能性需求
作者怎麼處理無障礙與安全這類非功能性需求?
開頭先掛號、不展開('我們會在後面擴大設計時考慮');只在效能上追問SLO,因為那是唯一能給出具體數字的維度
第 2 段 → 階段一:功能性與非功能性需求
把price data和data series併成單一請求的理由
作者決定把price data和data series併成單一請求的理由是什麼?
簡單永遠優於複雜;併了之後loading/error只有一組,狀態形狀更乾淨
第 6 段 → data point 與請求合併的取捨
『畫面順序等於標記順序』對無障礙為什麼重要
『畫面順序等於標記順序』對無障礙為什麼重要?
螢幕閱讀器與鍵盤Tab走的是DOM順序;CSS可以讓視覺順序跟DOM順序不一致,導致鍵盤使用者要Tab很多次才到得了
第 8 段 → 最小可行前端:框架、設計系統、CSS、無障礙
Web效能三維度對應的Core Web Vitals指標
Web效能三個維度分別對應哪個Core Web Vitals指標?
載入速度=LCP、對輸入的反應=INP、版面穩定=CLS
第 10 段 → Web 效能的三個維度
判斷該不該上BFF的兩條判準
作者判斷該不該上BFF的兩條判準是什麼?
一是拿到的資料形狀跟想要的形狀差多少;二是那個服務是不是自己擁有、會不會常改API
第 11 段 → BFF:讓介面穩定下來
HTTP快取和TanStack的客戶端快取有什麼不同
HTTP快取和TanStack的客戶端快取有什麼不同?
HTTP快取由瀏覽器與CDN執行,跨分頁與重新整理都有效;TanStack活在記憶體裡,換頁就沒了,但能做樂觀更新與背景重抓,兩者互補
第 13 段 → 快取:用讀寫比決定快取什麼
作者怎麼因應金融機構的資料限制去選AI工具
作者怎麼因應金融機構的資料限制去選AI工具?
會問對方有沒有本地AI計畫,自架用Ollama跑15B左右的模型,堪用但跟雲端大模型差距明顯
第 14 段 → AI 開發工具鏈:模型、harness、審查代理
作者回頭列出三塊他刻意沒展開的地方
作者回頭列出三塊他刻意沒展開的地方是什麼?
元件的重用性與渲染策略(SSR/SSG)、整體前端架構(微前端、CSS架構)、行動裝置響應式
第 15 段 → 回頭看:還會再深掘的地方
junior和senior在系統設計面試裡的差別
junior和senior在系統設計面試裡的差別是什麼?
junior工具導向、直接說要用什麼;senior先講決策因素、攤開選項(A/B/C),不替對方做決定
第 16 段 → junior 與 senior 的差別,以及唯一的建議

Parallel Agent Orchestration with Orca: Local AI Mac

Joe Maddalone 工程職涯與架構角色 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

幫你的專案寫一個worktree用的setup script
  1. 為你的專案寫一個setup script(如pnpm install/依賴安裝)
  2. 確認每次建新worktree時該script會自動執行
  3. 測試:開一個新worktree,不手動裝任何東西,看agent能不能直接跑起來
今天就做 檢查你現在用worktree或類似隔離環境的專案,有沒有一個『建好就能跑』的setup script,沒有就寫一個
第 2 段 → Design Mode:一鍵開 worktree
review AI agent的diff時用行內註解取代重寫prompt
  1. 逐檔打開agent的diff,不要只看summary
  2. 針對不確定或不滿意的地方,在該行加註解說明你要什麼
  3. 把註解送回agent,不要自己動手改
  4. 改完再看一次diff,滿意才stage
今天就做 下次review AI agent寫的PR時,至少用行內註解(而非重寫一段prompt)反饋一處修改,記錄這樣做省了多少來回
第 8 段 → Diff 審查:加註解丟回 agent
同一任務派給兩個以上agent,比較diff只合併一個
  1. 挑一個明確、範圍小的任務
  2. 同時派給兩個以上不同的agent/模型做同一件事
  3. 比較他們的diff:誰的改動更小、更貼近你要的
  4. 只合併一個,其餘worktree直接刪掉
今天就做 挑你手上一個小任務(如修一個lint警告),同時用兩個不同的AI工具各做一次,比較diff選一個用
第 9 段 → 還能做更多:比稿、handoff、sub agents

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

Orca像工地的工頭,自己不動手,只負責派工、巡視、驗收
新 Orca自己不寫程式,只負責開工位、派任務、看進度、收成果  ⇄  像 工地的工頭不自己砌磚,只負責分派工人、巡視進度、驗收成果
第 1 段 → Orca 是什麼
workspace board像看板管理,掃一眼就知道誰做完不用逐個問
新 workspace board讓你不用逐個切終端機看進度,做完會自動浮上來  ⇄  像 kanban看板管理讓任務狀態可見化,管理者掃一眼看板就知道進度,不用逐個問
第 7 段 → Workspace Board:一眼看全部進度
在diff上加註解送回agent,像GitHub PR review在某行加comment
新 在diff上加註解送回agent修改,比重新打一段prompt更精準  ⇄  像 GitHub PR review時在某一行加comment請開發者修改,比在PR描述框裡重寫一段話更精準
第 8 段 → Diff 審查:加註解丟回 agent

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

Orca是agent orchestrator:不寫程式,只負責開工位、派任務、看進度、收成果
agent orchestrator
第 1 段 → Orca 是什麼 · agent orchestrator AI coding agent
按Cmd+N開新worktree=開新資料夾+新分支+啟動一個agent,setup script自動裝依賴
git worktree
第 2 段 → Design Mode:一鍵開 worktree · git worktree design mode setup script
點瀏覽器裡的元素,把它的markup複製給agent,指著畫面說要改哪裡
grab page element
第 3 段 → 內建瀏覽器 + 抓 DOM 元素 · grab page element DOM Nx
側邊面板隨時能人工介入:內建Monaco編輯器自己改檔、看git status確認agent動了哪裡
Monaco editor
第 4 段 → 側邊面板:編輯器、歷史、Git 狀態 · Monaco editor git status
Stage all→AI生成commit message→commit→Create PR,全程沒離開Orca
pull request
第 5 段 → Git 工作流:AI 寫 commit、開 PR · pull request stage commit message
工位彼此隔離,就能同時開多個worktree各派不同agent做不同任務
parallel agent orchestration
第 6 段 → 平行編排:三個 worktree、三個 agent · parallel agent orchestration Antigravity Pi
不逐個切終端機看進度,做完的agent自動浮上board,人再審
workspace board
第 7 段 → Workspace Board:一眼看全部進度 · workspace board
每個agent的用量額度各自獨立,用完就卡住,orchestrator幫不上忙
quota
第 7 段 → Workspace Board:一眼看全部進度 · quota
逐檔看agent的diff,在行上加註解比自己動手改或重打prompt更省力精準
diff
第 8 段 → Diff 審查:加註解丟回 agent · diff annotation
任務有先後依賴時,一個agent做完交給下一個;或用sub agents把大任務拆開
agent handoff
第 9 段 → 還能做更多:比稿、handoff、sub agents · agent handoff sub agents

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

開新worktree的agent選單列了13種可選,含Manage agents與Create more
對話框裡agent選單有Blank Terminal、Claude、Claude Agent Teams、Codex、OpenCode(勾選中)、Pi、OMP、Gemini、Antigravity、Kiro、Cline、Cursor、Hermes,底下還有Manage agents與Create more開關
證明 git worktree
演練 這份清單證明了Orca對agent的態度是什麼?這對你想用哪個agent有什麼影響?
第 2 段 → Design Mode:一鍵開 worktree
貼進OpenCode後顯示「Pasted ~25 lines」,是渲染出的HTML片段不是原始碼位置
抓元素複製的是該元素渲染後的DOM markup片段(~25行),不是原始碼檔案位置;agent收到後得自己investigate專案,在元件裡找到產生這段HTML的原始碼
證明 grab page element
演練 這個細節證明grab page element給agent的是什麼?為什麼agent還要自己去專案裡找對應的元件?
第 3 段 → 內建瀏覽器 + 抓 DOM 元素
Antigravity做到一半卡在「Individual quota reached...Resets in 68h6m28s」
Antigravity已經讀了CoursePost.astro、編輯了兩個.mdx的metadata,正要改tags頁時卡在額度用完的紅字提示,要等68小時才重置
證明 quota
演練 這個案例證明平行跑多個agent時,什麼是orchestrator管不到的?你會怎麼預先避免任務卡在額度用完?
第 7 段 → Workspace Board:一眼看全部進度
barnacle worktree的diff顯示Changes 8,比design mode那次的兩個檔案多很多
007這個任務(清理lint警告)的diff一次改了八個檔案,比design mode單一任務改兩個檔案多很多,這正是需要逐檔審查而非只看summary的原因
證明 diff
演練 這個對比證明什麼樣的任務更需要逐檔審查?你會怎麼決定要不要每個檔案都看過?
第 8 段 → Diff 審查:加註解丟回 agent

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

Agent與orchestrator的分工
agent與orchestrator的分工是什麼?
agent(OpenCode、Claude、Codex等)是真正動手改程式的工人;orchestrator(Orca)不寫程式,只負責開工位、派任務、看進度、收成果
第 1 段 → Orca 是什麼
Cmd+N開新worktree實際做了什麼
Orca按Cmd+N開新worktree時實際做了什麼?
開一個新資料夾+新分支+在裡面啟動一個agent;因為是新資料夾,依賴不會自動存在,所以要靠setup script跑安裝
第 2 段 → Design Mode:一鍵開 worktree
抓元素時除了複製markup還能做什麼
抓頁面元素時除了複製markup還能做什麼?
提示『press C to copy or S to screenshot』,所以也能直接截圖給agent看
第 3 段 → 內建瀏覽器 + 抓 DOM 元素
agent做完後worktree名字的變化
agent做完卡片改動後,worktree的名字發生了什麼變化?
從隨機名(prowfish)變成有意義的名字(分支改名feat-card-component),但終端路徑的資料夾名不變
第 4 段 → 側邊面板:編輯器、歷史、Git 狀態
AI生成的commit message怎麼來的
AI生成的commit message『Fix course card text overflow in flex layouts』是怎麼來的?
不是照抄作者的prompt,而是從diff(加flex-1 min-w-0、拿掉max-w-2xl)反推出這其實是flex版面的文字溢出問題
第 5 段 → Git 工作流:AI 寫 commit、開 PR
Orca怎麼確保依賴裝好才啟動agent
Orca怎麼知道該等setup script跑完才啟動agent?
透過環境變數ORCA_SEQUENCED_STARTUP_SCRIPT,確保依賴裝好agent才開始工作
第 6 段 → 平行編排:三個 worktree、三個 agent
『Improve』這個工具的角色
『Improve』這個工具在影片裡扮演什麼角色?
另外跑的agent/工具,先把專案掃一遍產出一份編號的小問題清單(plans/001、005、007…),讓後續分派有現成的任務單
第 6 段 → 平行編排:三個 worktree、三個 agent
Antigravity任務失敗時作者的處理方式
Antigravity任務失敗時,作者的處理方式是什麼?為什麼?
直接刪掉worktree,因為任務沒做完、審半成品不划算;若是關鍵任務,另一個做法是換agent接手同一個worktree
第 7 段 → Workspace Board:一眼看全部進度
平行分工、比稿、agent handoff三種用法的差別
平行分工、比稿、agent handoff三種用法的差別是什麼?
平行分工是N個agent做N個不同任務;比稿是N個agent做同一個任務、挑一個合併;handoff是任務有先後依賴,一個做完自動交給下一個,或用sub agents拆解
第 9 段 → 還能做更多:比稿、handoff、sub agents

Not Holding Back the Ocean

ethanniser.dev 工程職涯與架構角色 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 原文

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

區分自己愛的是手藝還是目的
  1. 列出你的專業裡讓你有差異化優勢的具體技能
  2. 問自己:這個技能本身讓你快樂,還是它幫你達成的結果讓你快樂
  3. 想像這個技能被工具完全取代,你還會想做這件事嗎
  4. 若答案是會,那是目的;若否,那是手藝,考慮怎麼把精力轉向目的
今天就做 花20分鐘寫下你工作中最讓你自豪的一項技能,問自己「我愛的是過程還是結果」,寫一句結論
第 4 段 → 我愛的是做出好軟體
把綁定舊工具的技能,換成可攜的工作習慣
  1. 列出你目前投入時間精進的三項能力
  2. 逐一判斷它綁定在特定工具/技能,還是通用的工作習慣(努力/學習/產出)
  3. 把繫在特定工具上的能力,想一個等值的可攜版本
  4. 選一項這週就開始練可攜版本
今天就做 挑你正在學的一個技術技能,寫下它背後真正可攜的能力是什麼,今天就找一個練習可攜能力(而非只練該技能語法)的方式
第 4 段 → 我愛的是做出好軟體

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

手寫程式的技藝可能被AI coding工具取代,就像剪膠卷被數位剪接取代
新 手寫程式是一種技藝,可能被AI coding工具部分取代  ⇄  像 剪膠卷是一種技藝,被數位非線性剪接軟體取代
第 3 段 → 剪膠卷,還是拍電影?
抗拒AI coding工具,就像當年可能抗拒tab補全一樣沒必要
新 AI coding工具讓你寫程式更快,你可能會抗拒  ⇄  像 tab補全早就讓你打字更快,你當時欣然接受、甚至享受那種快感
第 4 段 → 我愛的是做出好軟體

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

Differentiation:擅長一項稀缺技能能換到速度、信任與影響力
differentiation
第 1 段 → 遊戲規則已經變了 · differentiation
Normalization:過去能差異化的工作正變得稀鬆平常
normalization
第 2 段 → 眼前看到的兩件事 · normalization
Making movies:手藝(剪膠卷)會被取代,目的(拍電影)不會
making movies
第 3 段 → 剪膠卷,還是拍電影? · making movies cutting film
Hold back the ocean:面對必然的潮流,唯一理性選擇是順著走不抵抗
hold back the ocean
第 4 段 → 我愛的是做出好軟體 · hold back the ocean

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

擅長舊式軟體工程曾帶來:更快進步、圈內信任、放大影響力、賺錢
作者列出「擅長」曾經是資產的四個具體好處:能比別人更快進步、成為圈內可信的聲音(trusted voice)、放大影響力、也能賺錢
證明 differentiation
演練 這份清單證明了differentiation的價值來自什麼?當AI讓這些技能人人可得,貶值的是誰?
第 1 段 → 遊戲規則已經變了
50年前拍片要一整個團隊、幾萬美元、幾週;現在500美元的iPhone就能拍完
作者用具體數字對比:五十年前需要一整個團隊、花幾萬美元、耗時幾週;現在一支500美元的iPhone自己就能拍完
證明 making movies
演練 這個對比證明了工具革命對「目的」造成什麼影響?門檻降低對真正愛拍電影的人是好事還是壞事?
第 3 段 → 剪膠卷,還是拍電影?
逐字敲leetcode、翻Stack Overflow找bug、被tab補全加速:三段回憶分屬手藝的樂趣、苦、早已接受的加速
一個字一個字敲leetcode和Advent of Code是手藝本身的樂趣;翻Stack Overflow找一個小bug是手藝的苦;被tab補全加速的快感,代表他早就接受過一次工具讓自己變快
證明 hold back the ocean
演練 這三段回憶證明了作者愛的其實是過程還是結果?如果當年愛上tab補全,代表你愛的不是『每個字都自己打』?
第 4 段 → 我愛的是做出好軟體

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

作者對AI coding工具的第一反應
作者對AI coding工具的第一反應是什麼?為什麼?
不是興奮而是不安;因為他擅長的是舊式軟體工程,而「擅長」曾經是資產
第 1 段 → 遊戲規則已經變了
「如果你選擇不進化」這句話的重點
作者說『如果你選擇不進化』這句話的重點是什麼?
重點在「選擇」——可怕的不是AI進步本身,而是你選擇不跟著進化
第 2 段 → 眼前看到的兩件事
作者選電影剪接業當歷史對照
作者用什麼產業當歷史對照?走過什麼轉變?
電影剪接業:從手剪實體膠卷,轉向數位非線性剪接與數位攝影,模式跟軟體工程現在的變化一樣
第 3 段 → 剪膠卷,還是拍電影?
作者對堅持手動剪接的人留的餘地
作者留了什麼餘地給堅持手動剪接的人?
喜歡剪膠卷沒有錯,現在還是有人在剪,但得接受它不再是原本那份「業界標準做法」的工作
第 3 段 → 剪膠卷,還是拍電影?
作者混在一起講的兩波技術變革
作者把哪兩波技術變革混在一起講?這會影響結論嗎?
非線性剪接軟體(1990年代普及)取代手剪膠卷,和數位攝影機(2000年代後)普及讓iPhone能拍片,是兩波不同變革;但都支持「工具讓門檻大降」這個結論
第 3 段 → 剪膠卷,還是拍電影?
「hold back the ocean」的典故
「hold back the ocean」這句話典出哪裡?
英王Canute命令潮水退去卻退不了的傳說;面對必然的潮流,抵抗徒勞,唯一理性選擇是順著走
第 4 段 → 我愛的是做出好軟體
過去讓作者領先的三件事
作者說過去讓他領先的三件事是什麼?為什麼這三件事在新時代還管用?
努力、持續學習、產出看得見的成果;因為它們是工作習慣而非綁定舊工具的技能,不會被取代
第 4 段 → 我愛的是做出好軟體

My NEW AI Terminal and Code Editor // Orca Review

Christian Lempa 工程職涯與架構角色 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

把要並行做的功能拆成獨立worktree,依賴的放同一個
  1. 列出手上要做的功能,確認彼此是否依賴共同介面(API路徑、資料格式)
  2. 不互相依賴的各開一個worktree
  3. 彼此依賴的放同一個worktree按順序做,不要拆開
  4. 做完一個worktree就commit/publish並刪除它
今天就做 挑你現在專案裡下一個要做的功能,手動跑一次git worktree add建一個獨立目錄,感受一下和開新branch checkout有什麼不同
第 5 段 → Git worktree:實體隔離的 branch 目錄
review agent的commit前先列出diff裡的新增檔案清單
  1. stage全部變更前先看一次diff
  2. 標出哪些是agent自己加的、你沒特別要求的檔案(如健康檢查端點)
  3. 寫commit訊息說明這個worktree做了什麼feature
  4. publish並開merge request,記得要有人實際按下接受合併
今天就做 回顧你最近一次AI agent幫你寫的PR,列出diff裡至少一個你完全沒注意到的新增檔案,寫下它是做什麼的
第 11 段 → Source control:commit 與 merge request
幫你的CLI agent寫一條guardrail規則
  1. 列出你會用哪些CLI agent(如Claude Code、Codex)
  2. 找到每個agent對應的yolo/skip-confirmation旗標
  3. 決定哪些操作絕不能給yolo(如正式環境部署)
  4. 把guardrail寫成一句agent能讀的規則
今天就做 幫你正在用的CLI agent寫一條guardrail:哪些操作即使開了yolo/dangerously-skip-permissions也不能做,存進它的設定檔或CLAUDE.md
第 12 段 → Orchestration:一個 agent 指揮多個 sub agent

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

每個worktree實體隔離,像飯店讓每個房客住不同房間不共用一張床
新 worktree是磁碟上實體分開的目錄,不同agent不會改到同一份檔案  ⇄  像 飯店讓每個房客住不同房間,不會共用同一張床或同一套家具
第 5 段 → Git worktree:實體隔離的 branch 目錄
agent間傳訊像開會時有人插一句話,原本報告的人聽完接著講完
新 Orca把第二個agent產生的文字當成新的使用者輸入,塞進第一個agent的對話回合  ⇄  像 開會時有人插一句話進來,原本在報告的人聽完插話、回答完,接著繼續講完自己的內容
第 7 段 → agent 互相溝通:orchestration skill 實戰
Yolo模式像自動駕駛設到最高等級,油門煞車都交給系統
新 Yolo模式讓agent啟動時就跳過人工確認,一路做到底不用你按同意  ⇄  像 自動駕駛設到最高等級,油門煞車都交給系統,不用駕駛時時按確認
第 12 段 → Orchestration:一個 agent 指揮多個 sub agent

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

ADE 不是給人寫程式的 IDE,而是圍繞正在寫程式的 agent 建的管理工作空間
ADE (agent development environment)
第 1 段 → Orca 是什麼:ADE 與本片的問題 · ADE (agent development environment)
orchestration skill 教 agent 怎麼跟 Orca 的協調工具講話、彼此傳訊
orchestration skill
第 3 段 → 設定:agents、用量、orchestration skill · orchestration skill
每個worktree是磁碟上實體分開的目錄、各自一個Git checkout,讓多個agent不互踩檔案
Git worktree
第 5 段 → Git worktree:實體隔離的 branch 目錄 · Git worktree checkout
Orca監看每個terminal分頁的程序,認出是已註冊的agent命令就掛上session條目
agent session
第 6 段 → 跑第一個 agent:live coding 一個 demo app · agent session
orchestration skill讓一個agent能對另一個session送訊息,甚至spawn新的sub agent
sub agent
第 7 段 → agent 互相溝通:orchestration skill 實戰 · sub agent
quick command不寫死啟動指令,而是丟給agent一句prompt去決定怎麼啟動
agent prompt (quick command)
第 8 段 → Quick commands 與內建瀏覽器看成果 · agent prompt (quick command)
annotate把「哪個元素+你想怎麼改」配成一組,自動組成送給agent的prompt
annotate page element
第 9 段 → 瀏覽器標註:點 UI 元素直接送進 prompt · annotate page element CSS selector DOM path
手機端以worktree(branch)為單位列出Pinned/Active,能看到並回應agent的權限確認
device pairing
第 10 段 → Mobile companion 與驗證修改結果 · device pairing Orca Mobile
stage→commit→publish→開merge request→刪worktree,走完Git合併的生命週期
publish branch
第 11 段 → Source control:commit 與 merge request · publish branch stage commit git pull
Yolo模式讓Orca用全權限旗標啟動agent,不做人工確認
Yolo mode
第 12 段 → Orchestration:一個 agent 指揮多個 sub agent · Yolo mode

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

31種可安裝CLI agent,每個底下印著實際啟動指令
頁面列「31 agents」,每個底下印出Orca實際用來啟動它的命令,如claude --dangerously-skip-permissions、gemini --yolo;每個agent的整合本質上就是一條啟動命令
證明 ADE (agent development environment)
演練 這份清單證明了Orca對agent的整合方式是什麼?這對「換一種agent」代表什麼難度?
第 3 段 → 設定:agents、用量、orchestration skill
訊息出現在第一個agent的終端裡,格式像使用者親自打的話
截圖顯示第二個agent產生的訊息出現在第一個agent的終端,接著它進入「Listing pending tasks…Working…」;證明Orca不是特殊協定,是把訊息當成新的使用者輸入
證明 sub agent
演練 這個機制證明了為什麼任何CLI agent都能參與傳訊?只要agent具備什麼特性就行?
第 7 段 → agent 互相溝通:orchestration skill 實戰
每個標註元素打包了十幾項context,遠比URL、tab、元素本體多
Intent、CSS Selector、Location、Bounds、Classes、元素文字、Nearby text/elements、Computed styles、Full DOM path、原始HTML,最後才是你的Feedback
證明 annotate page element
演練 這份清單證明了為什麼agent能立刻讀檔改碼而不用先問你指的是哪裡?
第 9 段 → 瀏覽器標註:點 UI 元素直接送進 prompt
一次commit有19個檔案全部新增,含agent自己加的健康檢查端點
STAGED CHANGES 19個檔案全標A(新增),package-lock.json +1845行、+page.svelte +300行、agent自己加的routes/health/+server.ts與lib/entity-icons.ts
證明 publish branch
演練 這份清單證明「五分鐘做出一個app」實際產出了多少東西?身為審閱者你會先看哪個檔案?
第 11 段 → Source control:commit 與 merge request

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

Orca用量條顯示的是滾動時間視窗額度,不是月配額
Orca左下角的用量條顯示什麼?Reset按鈕做了什麼?
訂閱的滾動時間視窗額度(如ChatGPT Pro的5小時視窗與週視窗);Reset只重設Orca端統計起點,不會重設provider那邊的額度
第 2 段 → 第一眼:官網與介面配置
Add project時怎麼判斷資料夾是不是repository
Orca掃描父資料夾時怎麼判斷一個子資料夾是不是Git repository?
看子目錄底下有沒有.git;沒有的(如Obsidian vault)只能當一般資料夾加入,之後worktree/branch相關功能都不能用
第 4 段 → 加入專案:掃描、匯入、分群
作者說的「Pi agent用Codex model」是什麼意思
作者說「Pi agent with my default Codex model」是啟動了Codex CLI嗎?
不是;是Pi透過plugin用他ChatGPT Pro訂閱底下的GPT模型,整段只有Pi一種agent在跑
第 6 段 → 跑第一個 agent:live coding 一個 demo app
dev server的port和Docker Compose的port不同
開發模式(npm run dev)和Docker Compose模式的port有什麼不同?
走不同port;這次agent把Vite開發port改成5179,容器模式(compose)仍在3000
第 8 段 → Quick commands 與內建瀏覽器看成果
作者誤判了Proxmox示範資料的來源
作者以為agent讀到他真實的Proxmox節點,實際上是什麼?
網域是home.arpa(RFC 8375保留的家用範例網域)、IP是教科書式私有位址,是agent編造的種子資料,不是真的讀到他機器上的資料
第 8 段 → Quick commands 與內建瀏覽器看成果
annotate改完後頁面報錯,重啟dev server就好的原因
annotate改完後頁面報錯,重啟dev server就正常了,為什麼?
agent裝了新套件(icon庫),但Vite dev server啟動時已經決定好要預先打包哪些依賴,新裝套件要重啟才會被納入,不是玄學
第 10 段 → Mobile companion 與驗證修改結果
協調agent怎麼等sub agent做完
orchestration skill教協調agent怎麼等sub agent做完?
重複執行`orca terminal wait --for tui-idle`輪詢某個終端是否閒置,不是事件推送,所以協調agent本身也持續耗token
第 12 段 → Orchestration:一個 agent 指揮多個 sub agent
Yolo模式的實際做法
Yolo模式的實際做法是什麼?
不是Orca攔截確認,而是換一組跳過確認的啟動旗標(如codex --dangerously-bypass-approvals-and-sandbox);Pi本來就不問,所以沒有旗標
第 12 段 → Orchestration:一個 agent 指揮多個 sub agent
作者對Orca的最終評價
作者對Orca的最終評價是什麼?
不是「必裝」而是「值得繼續探索」;隔離worktree與annotation最成熟,orchestration能動但有代價,mobile companion還不穩定
第 13 段 → 結論:Orca 讓不會寫程式的人也能做專案

Message Queues in System Design Interviews w/ Meta Staff Engineer

Hello Interview 訊息與串流系統 產生於 2026-09-22 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

把非冪等的「累加」操作改寫成冪等的「設值」
  1. 找出consumer裡會被重複處理的操作
  2. 判斷它是否天然冪等(設值型通常是,累加型通常不是)
  3. 累加型改寫成先算好目標值再設值,或加去重表存訊息ID
今天就做 挑你專案裡一個「+1」式的寫入,改寫成冪等版本(設值而非增量),寫下你會查哪個欄位判斷是否已處理過
第 6 段 → 投遞保證:at-least-once 與冪等
為專案裡需要保序的操作設計partition key
  1. 列出哪些操作彼此需要保序(例如同一帳戶的存提款)
  2. 選最小粒度的相關實體當key(如accountId而非city)
  3. 檢查這個key的資料分佈會不會有hot partition(少數key佔大量流量)
  4. 順序與分佈衝突時決定犧牲哪一個
今天就做 針對你系統裡一個會用到queue的功能,寫下你會選哪個partition key、並說明為什麼不會hot partition
第 8 段 → Partition、consumer group、partition key
檢查consumer有沒有設max retry與DLQ
  1. 幫每個consumer設max retry次數
  2. 超過次數就把訊息移到DLQ而非放回主queue
  3. 幫DLQ加監控告警,不要讓壞訊息堆積沒人看
  4. 暫時性失敗的重試加指數退避(exponential backoff)
今天就做 檢查你專案裡任一個非同步job/queue消費者有沒有設max retry與DLQ,沒有就寫一段設定的pseudocode
第 10 段 → Poison message 與 DLQ

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

Producer/consumer解耦,像餐廳服務生把點單夾上ticket rail就去忙別桌
新 producer送出訊息後不等consumer處理完,consumer依自己容量取件  ⇄  像 餐廳點餐流程:服務生把單子夾上ticket rail就離開,廚師準備好自己取單
第 4 段 → 正式定義:producer、consumer、decoupling
Partition key的取捨跟資料庫shard key完全同構
新 partition key要在「順序」(相關訊息同key保序)與「均勻分佈」間取捨  ⇄  像 資料庫sharding的shard key:也要在「相關資料放一起」與「負載均勻」間選
第 8 段 → Partition、consumer group、partition key
Queue buffer只是延後容量問題,像刷卡先消費、帳單遲早要還
新 producer長期快過consumer時,queue只是把問題延後、買時間,不會消失  ⇄  像 信用卡先刷卡消費,帳單會累積,遲早要還,不會因為晚點才收到帳單就不用還
第 9 段 → Producer 快過 consumer:back pressure

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

Message queue 是 producer 與 consumer 之間的 buffer,讓接收與處理脫鉤
Message queue
第 1 段 → 本片議程:面試脈絡下的 message queue · Message queue
Producer送出即忘、consumer依節奏拉取;兩端互不知道對方,可各自獨立擴展
Decoupling
第 4 段 → 正式定義:producer、consumer、decoupling · Decoupling Producer Consumer
Consumer處理完要明確回ack,queue才刪訊息;沒ack就視為未處理重派
Acknowledgement (ack)
第 5 段 → 底層機制:ack 與防止重複消費 · Acknowledgement (ack)
At-least-once:訊息可能重送,consumer必須冪等;面試標準答案
Delivery guarantee
第 6 段 → 投遞保證:at-least-once 與冪等 · Delivery guarantee At-least-once At-most-once Exactly-once
把queue切成多個獨立子序列,不同worker平行處理,吞吐隨partition數擴展
Partition
第 8 段 → Partition、consumer group、partition key · Partition
一池worker瓜分partition;Kafka靠獨占partition避免重複消費
Consumer group
第 8 段 → Partition、consumer group、partition key · Consumer group
決定訊息進哪個partition;在順序(同key同partition)與均勻分佈間取捨
Partition key
第 8 段 → Partition、consumer group、partition key · Partition key Ordering guarantee Hot partition
Queue只是buffer不解決容量問題;producer長期快過consumer要把壓力推回producer
Back pressure
第 9 段 → Producer 快過 consumer:back pressure · Back pressure Queue depth
永久性失敗的poison message:設max retry,超過就移到DLQ隔離,主queue繼續前進
Dead letter queue (DLQ)
第 10 段 → Poison message 與 DLQ · Dead letter queue (DLQ) Poison message Max retry
現代queue把訊息複製到多個broker並持久化到磁碟,一台掛了資料還在
Durability
第 11 段 → Queue 掛了:durability 與 replay · Durability Replication

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

同步處理照片:回應6秒、濾鏡掛了整單失敗、尖峰從50衝到500000個上傳
3個~2秒工序疊加成6秒回應;濾鏡服務中途掛掉整個上傳失敗;被App Store推薦後流量從每秒50跳到5000甚至500000,server只能撐200
證明 Message queue
演練 這三個數字分別證明了queue要解決哪三種問題?延遲、脆弱、突發各對應queue的哪個性質?
第 2 段 → 動機案例:同步處理照片的三個問題
設頭像成photo 5天然冪等;post count+1不是,要改寫成設成54
「把user 123頭像設成photo 5」跑兩次結果一樣;「post count +1」跑兩次變+2,要改寫成「設成54」(=原值+1)才冪等
證明 Delivery guarantee
演練 這個例子證明了冪等的哪兩種實作方式?你系統裡哪個操作是非冪等、要怎麼改寫?
第 6 段 → 投遞保證:at-least-once 與冪等
存100再提50若落不同partition,提款可能先跑被拒;用accountId當key修正
「Evan deposits $100」與「Evan withdraw $50」若用不同key落到不同partition,提款先被處理而拒絕;改用accountId當key兩則就同partition依序處理
證明 Partition key
演練 這個例子證明了partition key選錯會造成什麼具體後果?你會怎麼選key避免同樣的錯?
第 8 段 → Partition、consumer group、partition key
Kafka訊息消費後不刪,留到retention到期,讓多個consumer group能獨立讀、能replay
Kafka把訊息持久化並跨broker複製;retention window可設一天、一週甚至永久;訊息消費後不刪除,靠consumer自己移動讀取位置
證明 Durability
演練 這個機制證明了Kafka和傳統queue的哪個關鍵差異?你的系統若需要重播歷史訊息,會選哪個技術?
第 11 段 → Queue 掛了:durability 與 replay

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

重新派送的前提是queue要偵測到worker沒有完成——這個機制當下沒講
作者說訊息會重新派給另一個worker,但這時還沒解釋什麼?
queue怎麼知道worker掛了——這個機制是ack,要到後面才講
第 3 段 → 引入 queue 後三個問題怎麼解
SQS visibility timeout 與 RabbitMQ prefetch limit 都是避免重複消費的機制
SQS的visibility timeout和RabbitMQ的prefetch limit分別怎麼避免重複消費?
SQS:取走後對其他consumer隱形一段時間,逾時再現;RabbitMQ:channel層級限制同時處理數+ack timeout
第 5 段 → 底層機制:ack 與防止重複消費
該引入queue的四個訊號
該引入queue的四個訊號是什麼?
非同步工作、突發流量、需要解耦、可靠性(下游不能丟工作)
第 7 段 → 何時該用 queue:四個訊號與一個反例
同步嚴格延遲需求的反例
什麼情況不該用queue?
同步工作流且有嚴格延遲要求(如500ms內回應);加queue會破壞這個約束
第 7 段 → 何時該用 queue:四個訊號與一個反例
Consumer數量上限與hot partition
partition數與consumer group有什麼上限關係?hot partition是什麼?
consumer不能多於partition數,多出來的沒partition可讀;hot partition是某個key的訊息量遠高於其他key,讓該partition過載
第 8 段 → Partition、consumer group、partition key
Producer持續快過consumer的三種對策
Producer長期快過consumer時的三種對策是什麼?
scaling(監控queue depth自動加consumer/partition)、back pressure(減速/拒收/回錯誤請client重試)、alerting(至少要對queue depth設告警)
第 9 段 → Producer 快過 consumer:back pressure
Kafka / SQS / RabbitMQ 的選擇準則
Kafka、SQS、RabbitMQ分別在什麼情況下選?
需要重播或多方獨立消費→Kafka(預設答案);只要簡單可靠的工作佇列且在AWS→SQS(standard高吞吐/FIFO嚴格排序);需要依內容路由到不同consumer→RabbitMQ
第 12 段 → 常見技術:Kafka、SQS、RabbitMQ
本片沒碰的兩個面向
本片作者自己說沒碰到的兩件事是什麼?
queue以外的替代方案(如簡單需求用資料庫job table就夠);pub/sub與queue的差異(多訂閱者各自收到 vs 一則訊息一個consumer處理)

How a Cloudflare Engineer Ships Production Code He Didn't Write | Dillon Mulroy

Jan-Niklas Wortmann 工程職涯與架構角色 產生於 2026-09-21 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

把 review 移到 PR 之前:本地逐檔註解、注入回 agent、修完才推給團隊
  1. agent 做完一段改動後跑 /plannotator review,開一個像 GitHub PR review 的本地網頁
  2. 逐檔加註解(哪裡不像你會寫的、哪裡型別太鬆)
  3. submit:註解直接注入回目前的 agent session,讓它修
  4. 重複「做 → review → 修」直到你認可每一行,才推成 PR
  5. 每個 PR 控制在 300–800 行,超過就拆成 stacked PR
今天就做 拿你手上任一個 agent 剛產出的 diff(或 git diff),先不要推,用 GitHub review 的方式逐檔寫下至少 5 條註解再叫 agent 修;沒有 Plannotator 就貼註解進對話
第 16 段 → Plannotator review 與 stacked PR
/plannotator last:直接編輯 agent 最後一則訊息,把來回對話堆成 spec
  1. 不要一開始就寫 markdown;在訊息裡跟 agent 討論實作計畫
  2. agent 提案後跑 /plannotator last,把它最後一則訊息載進註解工具直接改
  3. 送回去,agent 修正理解,再提案
  4. 累積到訊息看起來像一份 spec、你「終於相信」雙方對要做什麼有共識
  5. 這時才叫 agent 寫成 markdown 並實作
今天就做 下一個要交給 agent 的功能,先問它「你打算怎麼做」,把它的回答複製出來逐句刪改(不是回訊息,是改它的話)再貼回去,來回三次後再讓它動手
第 17 段 → annotate / last:對話堆成 spec
開始一項工作:先問 agent 你已經知道答案的問題(技術棧、library、既有抽象)
  1. 列出這個功能會用到的技術與 codebase 既有抽象(例:Hono 怎麼運作、Durable Objects 怎麼做 SQLite)
  2. 每個主題開一個分支問 agent,讓它去研究;你知道答案,所以能判斷對錯並糾正
  3. 來回到你對它的理解放心,把最後一則整理成「這些是你需要知道的事」
  4. /tree 跳回根部(零 context),留下那段小結或寫進 markdown
  5. 從根部問下一個主題,重複;全部帶回主線後再用 Plannotator 迭代 tech spec
今天就做 挑你專案裡一個你很熟的 library,問 agent「X 是什麼、怎麼設定、API 有哪些」,逐句對照你的認知,把它答錯的地方糾正到你滿意,存成一段 200 字內的小結
第 18 段 → /tree:先問自己知道答案的問題
tech spec 三部分:型別與 interface、call stack、先寫測試;650–1,000 行
  1. 寫 TypeScript 型別與 interface:邊界在哪、需要哪些 adapter、各 interface 的實作
  2. 讓 agent 列出它要實作的 call stack:改既有還是開新路徑、每一步的輸入輸出型別與可能錯誤
  3. 讓 agent 先寫出它將要寫的測試(red),你 review 測試
  4. 才讓它實作到通過(green),再 refactor
  5. 反過來「幫既有 function 寫測試」不要做——agent 會把 bug 當規格
今天就做 拿你下一個要做的小功能,只寫它的 input/output 型別與一個 interface(不寫實作),加三個會失敗的測試,然後把這三樣丟給 agent 實作,看它有沒有開你不想要的新路徑
第 19 段 → spec 長得像 code
/tree 跳回時選「什麼都不做」:自己手動整理小結再 /copy,不讓模型決定留什麼
  1. 分支做完後,先把最後一則訊息整理到「這些是你需要知道的事」的程度(用 /plannotator last 改)
  2. /tree 跳回目標節點,選「什麼都不做」(不自動摘要);它不回滾 git,只影響 context
  3. 用 /copy 把整理好的小結帶到對話的另一個位置,或寫成 markdown 從別處引用
  4. 實作完成後跳回第零則(零 context)寫 e2e 測試,測試 agent 不被實作過程的假設污染
  5. 給位置加標籤,之後叫 agent 去看某個標籤
今天就做 在你現在的 agent 對話裡,把最近一段研究的結論手動壓成 10 行內的「你需要知道的事」,開一個全新對話只貼這 10 行,看 agent 能否直接開工
第 20 段 → /tree 細節:三種回法、幾百則訊息
把幾百次 review 裡反覆糾正的同一件事寫進 skill,讓 agent 做第一輪 review;省五分鐘就算贏
  1. 回顧最近的 review 註解,找出重複出現的糾正(例:不要 is Record、不要一路傳 unknown、不要重複驗證)
  2. 把它們寫成一份 skill 文件:你在軟體設計、架構、review 上的品味與原則
  3. 讓 agent 在產出後先用這份 skill 自我 review 一輪,再交給你
  4. 不期待一開始就好;只要少糾正一件重複的事就是勝利
  5. 已經手動走過幾百次的流程(研究分支 → spec → 迭代 → review)才排進 queue 自動化,判斷不自動化
今天就做 翻你最近 10 個 PR 的 review 留言,挑出出現兩次以上的糾正,寫成一個 5 條以內的檢查清單放進專案的 CLAUDE.md 或 agent 規則檔
第 22 段 → 看得見的 queue:把品味放進 skill

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

Ethan Niser 的電影業比喻:手工剪接→數位,難的是以手藝為身份的人怎麼安放自己
新 精靈已出瓶,選擇成為新工具的 craftsman、繼續為結果負責(still own the outcomes)  ⇄  像 電影剪接師從膠卷手工剪接轉到數位非線性剪輯
第 2 段 → 從看空到 all-in
生產力更高卻更不快樂,像工程師轉 EM:小成就的頻率消失了
新 Dillon 管理的是 agent 而非人,但同樣失去每解一個小問題的小成就,六個月沒進 flow state  ⇄  像 工程師轉 engineering manager:不再寫 code,一天可能一個小成就都沒有,只有延遲很長的團隊產出回饋
第 4 段 → 更不快樂:滿足感的來源變了
主持人:以前靠跟難題搏鬥學習,現在丟一顆 Advil(prompt)就有東西
新 junior 靠 prompt 幾分鐘得到能動的東西,跳過搏鬥  ⇄  像 止痛藥:症狀立刻消失,但身體沒學會對抗病因
第 7 段 → 下一代怎麼訓練
角色不消失,交接物改變:PM 從 4,000 字 PRD 變成能跑的 MVP;工程師從 PR 變成 PR+測試影片
新 PM 用精簡 spec 做 MVP/原型交給工程師打磨,Cloudflare 的 PM 已開始這樣做  ⇄  像 PRD:PM 花兩週寫給設計、工程、QA、行銷的正式需求文件,到處 bikeshed
第 10 段 → PM 與工程師:MVP 取代長 PRD
「你就是自己的 sub-agent」:sub-agent 的價值全都要,代價(摘要由模型決定)不接受
新 /tree 分支研究 → 親手審過的小結帶回主線;一個 session 幾百則訊息、各有分支  ⇄  像 sub-agent:主 agent 派出去研究,完成後只把摘要帶回主 context
第 20 段 → /tree 細節:三種回法、幾百則訊息

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

macro hard problem:架構、抽象、資料流、型別;現在全是 macro 所以更累
macro hard problem
第 5 段 → 只剩 macro 難題,沒有 flow · macro hard problem dopamine hit flow state
system design 直覺過去是 micro 搏鬥的副產品;micro 被 LLM 拿走,副產品消失——下一代怎麼訓練是最大未知
system design
第 7 段 → 下一代怎麼訓練 · system design apprenticeship
AI 移除「做」的成本、不移除「協調」的成本,所以角色壓縮後跨團隊同理心與 product-minded 更重要
product-minded engineer
第 9 段 → 角色壓縮:QA 30 秒、軟技能更重要 · product-minded engineer T-shaped
agentic engineering 沒有捷徑:要學的是判斷力,只能從自己的失敗長出來,只是壓縮在幾個月裡
agentic engineering
第 14 段 → 讓模型照你的方式寫 · agentic engineering YC founder
harness 做得越少越好:小 system prompt、幾個 tool、最少注入 context——否則每版飄移,建不了 baseline
harness
第 15 段 → Herdr 與 Pi:harness 越少越好 · harness Pi baseline system prompt
stacked PR:從未 merge 的分支再開分支,PR 指向前一個;讓 300–800 行的小 PR 在多步驟功能上仍可行
stacked PR
第 16 段 → Plannotator review 與 stacked PR · stacked PR atomic
shared understanding:spec 不是先寫好再實作,而是對話累積到「終於相信有共識」才固化成 markdown
shared understanding
第 17 段 → annotate / last:對話堆成 spec · shared understanding spec
/tree:對話當成一棵樹,每個主題在自己的分支研究,只把審過的精簡小結帶回根部——你就是自己的 sub-agent
/tree
第 18 段 → /tree:先問自己知道答案的問題 · /tree compaction /copy
agent 擅長填空、不擅長設計空格;人設計空格(型別、interface)、agent 填實作——spec 因此長得像 code
spec
第 19 段 → spec 長得像 code · spec interface call stack TDD
sub-agent 的問題不在能力,在誰做「摘要」這個決定——摘要就是決定什麼進主 context,是最重要的 macro 決定
sub-agent
第 21 段 → 為何不用 sub-agent · sub-agent non-deterministic

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

Opus 4.5 之前只讓 Claude Code 寫 5–10% 的 code,一個假期後變成幾乎全部
直到 2025 年 11 月他都公開 bearish,對 Dario「12 個月內 AI 寫 90–100% code」翻白眼;用了 Opus 4.5 後發現跟 Sonnet 3.5/3.7 明顯不同,產出一樣、做法 100% 不同(whiplash)
證明 agentic engineering
演練 這個數字的跳躍證明了轉向的動力是信仰還是實測?你自己有沒有一個「用了才改觀」的版本分界,它改變了你哪個習慣?
第 2 段 → 從看空到 all-in
全端功能的例子:UI→endpoint→商業邏輯→索引;決定架構後的實作小難題是休息也是心流,現在全被 LLM 接走
micro 難題:這個 class 怎麼寫、用哪個 library、漏掉的錯誤路徑——每個是一個 dopamine hit;被拿掉的正是「省力又有回饋」的那一層。且 LLM 讓決策影響更遠更快,錯的 macro 決定會被同樣速度放大
證明 macro hard problem
演練 這個例子證明了「更累」來自哪一層消失?既然 macro 能力變成瓶頸,你一天能做幾個好的 macro 決定,要怎麼安排一天?
第 5 段 → 只剩 macro 難題,沒有 flow
Cloudflare 五月裁 20%(約 1,100 人),隔天開幾百個完全不同的職缺;砍的是 scrum master、三層 director 等中間層
Dillon 承認有 overhiring 成分,但判斷「壓倒性是 AI 驅動」的證據是裁完馬上補、補的職位不同——是換人不是瘦身。這是內部員工的主觀判斷,他自己也聲明可能有未承認的原因
證明 product-minded engineer
演練 「裁完有沒有補、補什麼」這個指標證明了什麼?用它看你熟悉的公司的裁員新聞,哪些是財務理由掛 AI 招牌、哪些是組織重排?
第 8 段 → Cloudflare 裁 1,100 人
推 PR 後 agent 自動寫 Playwright 測試、錄影交回,以前半天的 QA 現在 30 秒——但他還是自己看錄影
Dillon 刻意說這不是主張裁掉 QA;agent 跑 Playwright 是把 micro 工作交出去,人看錄影是「人在迴圈裡」的 QA,不是 AFK。例:派 agent 去別團隊 codebase 探索問題後再去溝通
證明 product-minded engineer
演練 這個例子證明角色壓縮的具體長相是什麼?你的 PR 流程裡哪一步可以讓 agent 做、但你仍要親眼確認結果?
第 9 段 → 角色壓縮:QA 30 秒、軟技能更重要
主持人:先寫紮實測試再讓 LLM 實作,效果好十倍;反過來幫既有 function 寫測試全是垃圾
red-green:測試也是「空格」——先定好,agent 填實作。幫既有 function 補測試時 agent 是在「描述現況」而非「填空」,會把 bug 也當成規格
證明 spec
演練 這個對比證明了 agent 在哪種任務結構下可靠?你專案裡「補測試」的工作,要怎麼改寫成「先定空格」的形式?
第 19 段 → spec 長得像 code
spec 650–1,000 行,PR 只有 300–800 行:spec 比實作長,思考成本刻意放在動手前
spec 三部分合計 650–1,000 行;每個 PR 控制在 300–800 行;agent 的速度優勢用在「迭代次數」而不是「單次產量」
證明 spec
演練 這兩個數字的比例證明了什麼?你最近一次讓 agent 做的功能,動手前寫了幾行、產出幾行——比例反過來了嗎?
第 19 段 → spec 長得像 code

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

line for line:AI 產出跟自己寫的每一行都相同——比「能動」高很多的標準,後面所有做法都為了它
Dillon 對 AI 產出的標準是什麼?跟 vibe coding 差在哪?
line for line:看不出是誰寫的;vibe coding 的標準只是「能動」
第 1 段 → 開場:六個月沒自己寫 code · line for line
「AFK loop 派」與「讀每一行派」:Dillon 站後者,這決定他對 sub-agent、自動降級、多 agent 平行的態度
AFK loop 派和讀每一行派差在哪?Dillon 是哪派?
前者讓 agent 自主循環直接 commit;後者堅持人是 reviewer 與決策者。Dillon 是後者:責任歸屬不變(所以是工具),影響幅度空前(所以不只是工具)
第 3 段 → 工具不定義工作? · AFK loop
「像回到 junior」不是能力退步,而是工具操作技能歸零:judgement 還在、operation 要重學
為什麼有人說 AI 寫 code 很輕鬆、有人說很痛苦?
標準不同:「能動」幾分鐘就達成;「line for line」得像 junior 反覆試。judgement(知道好抽象長怎樣)與 operation(讓 agent 產出它)是兩種技能
第 6 段 → 像回到 junior
多 agent loop 對中位數開發者不實際的三個獨立理由:成本、harness 飄移、缺 receipts
Dillon 質疑 lab 推的多 agent loop 的三個主張各是什麼?
成本:API 計費下 dynamic workflow 貴到不能日用;一致性:harness 每版行為都變沒 baseline;證據:lab 說有效但沒可驗證成果。用「中位數」不用「平均」,因為平均被重度使用者拉高
第 11 段 → Loops 不適合中位數開發者 · median developer API billing receipts
把判斷加上日期(「今天,2026 年 6 月 25 日」):拒絕用未來的可能性合理化今天的成本
「永遠再六個月」的矛盾 Dillon 怎麼面對?Uber 的例子說明什麼?
承認矛盾、給判斷標日期;Uber 四個月燒光全年 AI 預算後設硬上限——token 從無窮金子變成需治理的稀缺資源。品質升、價格降要同時發生才解 ROI 僵局
第 12 段 → 永遠再六個月:ROI 與預算 · ROI
zero data retention:企業採用 AI 的硬門檻,資料治理先於能力
為什麼 Fable 對 Cloudflare 這種企業第一天就 non-starter?
沒有 ZDR——會保留 prompt 與 code 拿去訓練,法務直接否決,跟品質無關;其他模型都有 ZDR(2026 年 6 月狀態,供應商政策可能變)
第 13 段 → Fable 為何是 non-starter · zero data retention
prompt cache 綁定特定模型:200k context 在 Fable 上一路 cache hit,自動降級到 Opus 4.8 就得整份重付
為什麼自動降級模型會讓成本不可預測?
cache 綁模型,換模型等於全新未快取的輸入從頭計價;而且 thinking 被拿走違反「可預測性先於自主性」的原則
第 13 段 → Fable 為何是 non-starter · cache hit thinking
Dillon 工具鏈:Ghostty + Herdr(tmux 替代)、Pi(極簡 harness)、Plannotator
Dillon 的三層工具各是什麼?
第一層 Ghostty 終端機 + Herdr(agent 追蹤、對人和 agent 友善的 CLI);第二層 Pi harness;第三層 Plannotator(本地 review / annotate)
第 15 段 → Herdr 與 Pi:harness 越少越好 · terminal multiplexer Pi Plannotator

How Web Sockets work | Deep Dive

ByteMonk 網路與 API 基礎 產生於 2026-09-21 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

自己算一次 Sec-WebSocket-Accept,確認公式與 RFC 範例一致
  1. 取客戶端的 Key 字串,例如 dGhlIHJhbXBsZSBub25jZQ==(不要先 base64 decode)
  2. 字串尾端接上 GUID 258EAFA5-E014-47DA-95CA-C5AB0DC85B11
  3. 對接好的字串做 SHA-1,得到 20 個位元組
  4. 把 20 位元組 base64,應得 s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
  5. 伺服器把它放進 101 回應的 Sec-WebSocket-Accept;客戶端用同一公式算一次比對
今天就做 開終端機跑 python3 -c "import hashlib,base64;print(base64.b64encode(hashlib.sha1(b'dGhlIHJhbXBsZSBub25jZQ==258EAFA5-E014-47DA-95CA-C5AB0DC85B11').digest()))",確認輸出是 s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
第 3 段 → 伺服器驗證並算出 Sec-WebSocket-Accept
照位元圖讀一個 frame:從第一個 bit 到 payload,每個欄位對一次
  1. bit 0:FIN(1 = 訊息最後一片)
  2. bit 1–3:RSV1–3,沒協商擴充就都是 0
  3. bit 4–7:opcode(0x1 文字、0x2 二進位、0x0 接續、0x8 close、0x9 ping、0xA pong)
  4. bit 8:Mask(客戶端→伺服器一律 1;伺服器→客戶端一律 0)
  5. bit 9–15:payload length;0–125 直接是長度,126 再讀 16 bit,127 再讀 64 bit
  6. Mask=1 才有接下來 32 bit 的 masking key
  7. 剩下的就是 payload;Mask=1 時每個位元組要跟 key 依序 XOR 還原
今天就做 把畫面 344 秒的位元串抄下來(FIN=1、opcode 0001、Mask=1、length 0001101),自己數一次它是「13 位元組、有遮罩的文字 frame、來自客戶端」,再用 Wireshark 或瀏覽器 DevTools 的 WS 分頁對照一個真實 frame
第 5 段 → WebSocket frame 的結構

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

Key/Accept 像接頭暗語:證明「你知道規矩」,不證明「你是誰」
新 Sec-WebSocket-Key → Accept 的計算:只有讀過 RFC 的實作知道要接哪個 GUID 再 SHA-1  ⇄  像 接頭暗語(「天王蓋地虎」→「寶塔鎮河妖」):答得出來代表是自己人,但暗語人人可學
第 3 段 → 伺服器驗證並算出 Sec-WebSocket-Accept
「HTTP 連線被 WebSocket 取代」:同一條電話線,講到一半換一種語言
新 101 回應結尾的空行之後,同一個 socket 的下一個位元組就依 WebSocket 格式解讀;沒有另開連線  ⇄  像 打電話時雙方約好「接下來改講英文」,電話線沒換、對象沒換,只有講的語言換了
第 4 段 → 握手完成、失敗條件與 URL 格式
masking 不是加密:key 就放在同一個 frame 裡,誰都能解——這是設計本意
新 masking:隨機 4 位元組 key 跟 payload 逐位元組 XOR,key 隨 frame 一起送  ⇄  像 加密(如 TLS):沒有金鑰的人看不懂內容
第 6 段 → Masking:讓流量看起來不像 HTTP
分片像串流上傳:不用等整個檔案準備好、也不用一次配整段記憶體
新 fragmentation:發送端在總長未知時就能開始送,接收端用固定大小 buffer 逐片處理,早到的片段先用  ⇄  像 HTTP chunked transfer encoding:回應長度未知時分塊送,最後一個 0 長度 chunk 表示結束
第 7 段 → Fragmentation:大訊息分片與 FIN 位元

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

WebSocket 是獨立的 TCP 協定,不是 HTTP 的延伸;跟 HTTP 唯一的交集是 handshake 會被讀成 upgrade request
WebSocket
第 1 段 → 為什麼要從 HTTP 升級成 WebSocket · WebSocket TCP HTTP
WebSocket handshake:一個帶三個關鍵標頭的 HTTP GET 加一個 101 回應,把這條連線從 HTTP 切成 WebSocket
WebSocket handshake
第 2 段 → 客戶端發起 handshake:請求標頭 · WebSocket handshake upgrade request
Sec-WebSocket-Accept = base64(SHA-1(Key + 固定 GUID)),證明伺服器讀過規格,不證明身分
Sec-WebSocket-Accept
第 3 段 → 伺服器驗證並算出 Sec-WebSocket-Accept · Sec-WebSocket-Accept GUID SHA-1
WebSocket frame:升級後的資料單位,標頭依序 FIN、RSV、opcode、Mask、length、key,再接 payload
WebSocket frame
第 5 段 → WebSocket frame 的結構 · WebSocket frame FIN opcode Mask payload length masking key payload data
masking:瀏覽器用腳本無法指定的隨機 key 對 payload XOR,讓線上位元組不可預測、排不成 HTTP,防舊 caching proxy 被污染
masking
第 6 段 → Masking:讓流量看起來不像 HTTP · masking XOR masking key
fragmentation:大訊息切成多個 frame,第一片帶型別 opcode、後續 0x0 continuation、只有最後一片 FIN=1
fragmentation
第 7 段 → Fragmentation:大訊息分片與 FIN 位元 · fragmentation continuation frame FIN

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

畫面上完整的 upgrade GET 八行標頭:Host、Upgrade、Connection、Key、Origin、Protocol、Version
GET /chat HTTP/1.1;Host: server.bytemonk.com;Upgrade: websocket;Connection: Upgrade;Sec-WebSocket-Key: dGhlIHJhbXBsZSBub25jZQ==;Origin: http://bytemonk.com;Sec-WebSocket-Protocol: chat, superchat;Sec-WebSocket-Version: 13
證明 WebSocket handshake
演練 這八行裡哪三行少一行伺服器就該拒絕?Origin 行作者沒提,伺服器能拿它做什麼?你在 DevTools Network 裡怎麼找到這個 request?
第 2 段 → 客戶端發起 handshake:請求標頭
RFC 6455 範例:Key dGhlIHJhbXBsZSBub25jZQ== 算出 Accept s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
GUID 固定為 258EAFA5-E014-47DA-95CA-C5AB0DC85B11(畫面 210 秒打錯成 4D3N-…,N 不是十六進位);SHA-1 在這裡只是壓指紋,不涉及安全強度,已被視為不安全也沒關係
證明 Sec-WebSocket-Accept
演練 這個範例證明了什麼樣的伺服器會被排除、什麼樣的不會?如果你在寫一個 WebSocket 伺服器,收到 Key 後哪一步做錯客戶端就會斷線?
第 3 段 → 伺服器驗證並算出 Sec-WebSocket-Accept
344 秒的實例 frame:FIN=1、RSV 000、opcode 0001(文字)、Mask=1、length 13、32 位元 key
FIN=1 表示沒有分片;opcode 0001 是文字 frame(必須是合法 UTF-8);Mask=1 表示是客戶端送出的;payload length 0001101 = 13 位元組;伺服器送出的 frame 沒有 masking key,標頭少 4 位元組
證明 WebSocket frame
演練 光看 Mask 位元就能判斷方向——為什麼?如果這個 frame 的 length 欄位是 126,接下來的位元組你要怎麼解讀?
第 5 段 → WebSocket frame 的結構
2010 年 Huang 的 cache poisoning:惡意網頁經 WebSocket 送出長得像 HTTP 的文字,proxy 誤快取後污染他人
攻擊者控制的腳本能決定 payload 原文,排成 GET /script.js HTTP/1.1 …,讓 caching proxy 以為是 HTTP 請求/回應並存下來;同一台 proxy 後面的其他人就拿到被污染的 script。masking 讓腳本無法預測出線位元組,攻擊失效
證明 masking
演練 這個攻擊證明了「誰」是威脅來源?為什麼由此推出「只有客戶端→伺服器要遮罩」和「key 每個 frame 要重抽」這兩條規則?
第 6 段 → Masking:讓流量看起來不像 HTTP
分片的兩個動機:避免超大訊息撐爆 buffer;讓接收端邊收邊處理(534 秒畫面「Gradual delivery」)
payload length 本身可表到 2^63,所以不是「送不了」,而是接收端得先配好整段記憶體;分片讓總長未知(串流產生)的資料也能開始送,接收端用固定 buffer 逐片處理
證明 fragmentation
演練 這兩個動機證明了分片解決的是誰的問題——發送端還是接收端?上傳大檔案時你會希望函式庫怎麼切、多大一片?
第 7 段 → Fragmentation:大訊息分片與 FIN 位元
五個場景:聊天、網頁即時更新、多人遊戲、協作畫布、股價報價——共同點是伺服器要主動推且推得頻繁
聊天與股價:伺服器主動推、訊息小而頻繁(持續連線+2 位元組標頭);遊戲與協作畫布:雙向、低延遲、順序敏感(同一條 TCP 的有序傳輸);上傳大檔/串流更新:fragmentation
證明 WebSocket
演練 把這五個場景各對回一個機制(持續連線/小標頭/有序 TCP/分片)——你手上的產品哪個功能符合「伺服器要主動推且頻繁」?
第 8 段 → 應用場景與結語

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

WebSocket 真正解的不是「每個 request 重開連線」,而是「伺服器不能主動推」
HTTP/1.1 已有 keep-alive,WebSocket 還多解了什麼?
HTTP 仍是客戶端問、伺服器答的單向發起模式;WebSocket 連線開著後雙方隨時可主動送資料
第 1 段 → 為什麼要從 HTTP 升級成 WebSocket
為什麼要 Connection: Upgrade 和 Upgrade: websocket 兩個標頭而不是一個
Connection: Upgrade 與 Upgrade: websocket 各負責什麼?
Connection 是 hop-by-hop 標頭,告訴中間 proxy 這條連線要換協定;Upgrade 才指名換成 websocket。缺一個都應拒絕
第 2 段 → 客戶端發起 handshake:請求標頭 · Connection: Upgrade Upgrade: websocket
Sec-WebSocket-Key 不是密碼、不是加密,只是 16 個隨機位元組的 base64(24 字元、== 結尾)
Sec-WebSocket-Key 的長相與真正目的?
16 隨機位元組 base64 = 24 字元 == 結尾;目的是防非 WebSocket 客戶端誤打誤撞建連線。JS 不能自設 Sec- 開頭標頭,只有真 WebSocket API 能送
第 2 段 → 客戶端發起 handshake:請求標頭 · Sec-WebSocket-Key base64
Sec-WebSocket-Version 固定 13;Sec-WebSocket-Protocol 是可選的子協定候選清單
Sec-WebSocket-Version 的值是多少?Sec-WebSocket-Protocol 做什麼?
RFC 6455 定案後固定 13;Protocol 列客戶端希望用的應用層子協定(如 chat, superchat),伺服器從中選一個回
第 2 段 → 客戶端發起 handshake:請求標頭 · Sec-WebSocket-Version Sec-WebSocket-Protocol
101 Switching Protocols:從這個回應之後,同一條連線改用 Upgrade 標頭指定的協定
伺服器同意升級時回什麼?回應帶哪些標頭?
101 Switching Protocols;帶 Upgrade: websocket、Connection: Upgrade、Sec-WebSocket-Accept(可再帶選定的 Sec-WebSocket-Protocol)
第 3 段 → 伺服器驗證並算出 Sec-WebSocket-Accept · 101 Switching Protocols
handshake 失敗的三個條件:Accept 不符、標頭缺少、狀態碼不是 101 → 不建連線、不送 frame、客戶端關掉
客戶端在什麼情況下必須放棄 WebSocket 連線?
Sec-WebSocket-Accept 值不符、必要標頭缺少、狀態碼不是 101——三者任一發生就不送 frame 並關閉
第 4 段 → 握手完成、失敗條件與 URL 格式
ws:// 預設 port 80、wss:// 預設 443(影片說 port 0 是口誤);URL 不允許 #fragment
ws 與 wss 的預設 port 各是多少?URL 格式有什麼限制?
ws→80、wss→443(同 http/https);格式 ws(s)://host[:port]path[?query],不允許 #fragment
第 4 段 → 握手完成、失敗條件與 URL 格式 · ws:// / wss://
wss 的順序:TCP 三向交握 → TLS handshake → 才送 HTTP upgrade GET,所以 handshake 本身也是加密的
用 wss 時 WebSocket handshake 排在哪個位置?
TCP 交握 → TLS 交握 → HTTP upgrade GET;安全來自 TLS(只有 wss 有),Key/Accept 不提供安全或身分驗證
第 4 段 → 握手完成、失敗條件與 URL 格式 · TLS handshake
payload length 的三段式:0–125 直接是長度;126 → 再讀 16 bit;127 → 再讀 64 bit
payload length 只有 7 bit,怎麼表示超過 125 位元組的訊息?
值 126 表示後面接 16 bit 實際長度,127 表示接 64 bit;小訊息只花 2 位元組標頭,大訊息不受限
第 5 段 → WebSocket frame 的結構 · payload length
opcode 常見值與控制 frame
opcode 0x0 / 0x1 / 0x2 / 0x8 / 0x9 / 0xA 各是什麼?哪些是控制 frame?
0x0 接續片段、0x1 文字、0x2 二進位、0x8 close、0x9 ping、0xA pong;0x8–0xA 是控制 frame
第 5 段 → WebSocket frame 的結構 · opcode
masking 的方向規則:客戶端→伺服器一律遮罩,伺服器收到未遮罩必須關連線;伺服器→客戶端不可遮罩
哪個方向的 frame 要遮罩?違反會怎樣?
只有客戶端→伺服器要(威脅來自不受信任的瀏覽器腳本);伺服器收到未遮罩 frame 必須關閉連線;伺服器送出的不可遮罩
第 6 段 → Masking:讓流量看起來不像 HTTP
分片的交錯規則:兩則資料訊息的片段不可交錯(A1 A2 B1 A3 不合法),但控制 frame 可以插在片段之間
分片中的訊息,哪些 frame 可以插進來、哪些不行?
另一則資料訊息的片段不行;控制 frame(ping、pong、close)可以——所以傳大訊息到一半仍能回 pong 保活
第 7 段 → Fragmentation:大訊息分片與 FIN 位元
什麼時候不該用 WebSocket:單向推用 SSE、請求回應式 API 用 HTTP/2;WebSocket 的代價是每條連線都是伺服器上的長期狀態
哪些情況 WebSocket 不是最佳選擇?代價是什麼?
只需伺服器單向推→SSE 更簡單且走純 HTTP;大量 request/response→HTTP/2 多工已解連線成本;WebSocket 每條連線是長期狀態,擴展要處理連線親和性與斷線重連
第 8 段 → 應用場景與結語 · Server-Sent Events

How Senior Frontend Engineers Think in System Design Interviews

I Code It 前端底層與系統設計 產生於 2026-09-21 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

Clarify:跳去解法前先問三個問題,蒐集真正會翻轉答案的變數
  1. 問:我們要設計的使用者體驗是什麼?(對應分頁例:搜尋結果還是動態牆)
  2. 問:規模、一致性、交付速度各有多重要?(對應協作例:要不要上 CRDT)
  3. 問:我們真正有哪些限制?(既有程式碼、團隊、技術棧、時程)
  4. 把需求分成功能性(做什麼)與非功能性(做得多快、多可靠、多能擴);後者常被省略,要主動問
  5. 症狀檢查:如果三十秒內就開始講技術名詞,代表太早解題
今天就做 挑你專案裡下一個要做的功能(或一張 ticket),在動手前寫下這三個問題的答案各一行,並標出哪些是非功能性需求、哪些是限制
第 5 段 → 解法三步驟:釐清、選擇、解釋
Choose:選一個依目前所知說得通的起點,不是最先進的方案
  1. 畫出最薄的端到端路徑:介面 → API → 資料 → 回到畫面,每段都可以很粗糙
  2. 每個二選一都先選好實作、好理解的那個(搜尋結果先 offset、feed 先 cursor、即時性先用一般 request-response API)
  3. 把「什麼需求出現時會換掉」寫在旁邊(例:即時需求明確到足以正當化 WebSocket 時才引入)
  4. 複雜度必須被需求觸發,不是被預測
今天就做 拿你專案裡一個正在猶豫的技術選擇,寫下它的 steel thread 版本(最簡單能跑通的那個)加一句「當 ___ 出現時我會換成 ___」
第 6 段 → 選一個合理起點:steel thread
Explain:用四句話講取捨,聽起來像個能做決定的人,而不是列五個方案等面試官挑
  1. 這是我會開始的方案
  2. 我認為它為什麼符合目前的需求
  3. 我接受哪些取捨
  4. 在什麼情況下我會回頭重新檢視這個設計
  5. 不要假裝選擇是完美的;不假裝通常更好
今天就做 把你最近一個技術決定(換函式庫、加快取、選分頁方式)用這四句話寫成四行,貼進 PR description 或團隊頻道
第 7 段 → 把取捨講清楚
把技術名詞翻譯成產品問題:不用記架構,也能推出架構
  1. 遇到 offset vs cursor → 先問「資料會不會在使用者瀏覽期間變動?他需不需要回到特定位置?」
  2. 遇到 WebSocket vs polling → 先問「使用者能容忍多久的延遲?」
  3. 遇到 CRDT vs 後寫覆蓋 → 先問「兩個人多常撞到同一個東西?撞到的代價多大?」
  4. 答完產品問題,技術選擇會自己浮現
今天就做 列出你專案裡三個曾經爭論過的技術二選一,每個各寫一句「它其實在問的產品問題是什麼」
第 8 段 → 總結:把模式用在情境裡

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

「討論取捨是好事,困在取捨裡不是」:像在餐廳看菜單太久,桌上什麼都沒上
新 有能力的工程師懂太多反而不敢下手,不斷列選項、比選項,設計停在原地等確認  ⇄  像 點餐:菜單越豐富越難點,但不點就永遠沒東西吃;點錯了下次換就好
第 2 段 → 真實工程不是選擇題
steel thread 不等於「先做個簡單版本」,差在「端到端」三個字
新 steel thread:第一版必須貫穿整條路徑,每段都粗糙也沒關係  ⇄  像 MVP/先做簡單版:先把某一層做出來看看
第 6 段 → 選一個合理起點:steel thread
四句話回答句型就是口說版的 ADR
新 面試回答:起點、為什麼合理、接受什麼取捨、何時回頭重看  ⇄  像 Architecture Decision Record:情境、選了什麼、放棄了什麼、預期後果
第 7 段 → 把取捨講清楚

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

卡住不是知識不足,而是看得見多個都說得通的選項卻不知道怎麼選一個、講清楚、往前走
analysis paralysis
第 1 段 → 卡住的不是知識,是選擇 · analysis paralysis
trade-off:會讓人癱瘓的都是「沒有絕對優劣、只有適用情境」的成對選項
trade-off
第 1 段 → 卡住的不是知識,是選擇 · trade-off
constraint:既有 codebase、團隊、技術棧、導入成本——全在技術之外,卻決定方案勝負
constraint
第 2 段 → 真實工程不是選擇題 · constraint
cursor-based pagination 指向資料本身的位置,對「翻頁期間資料位移」天生免疫
cursor-based pagination
第 3 段 → 例一:分頁 — 搜尋結果 vs 動態牆 · cursor-based pagination
衝突的機率 × 衝突的代價,共同決定你需要多複雜的 conflict resolution
conflict resolution
第 4 段 → 例二:即時協作 — 白板 vs 看板 · conflict resolution
Clarify → Choose → Explain:資訊不完整時仍能推進的三個動作,也是資深工程師的日常
Clarify-Choose-Explain
第 5 段 → 解法三步驟:釐清、選擇、解釋
non-functional requirements(效能、規模、一致性、可用性、時程)才是架構的真正驅動力,最常被省略
non-functional requirements
第 5 段 → 解法三步驟:釐清、選擇、解釋 · non-functional requirements
steel thread:先做最薄但貫穿介面→API→資料→畫面的可運作路徑,等需求推你才加複雜度
steel thread
第 6 段 → 選一個合理起點:steel thread · steel thread
two-way door decision:附上撤回條件的決定是可逆的,可逆的就快速選,不可逆的才慢下來比較
two-way door decision
第 7 段 → 把取捨講清楚 · two-way door decision

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

面試官多半看四件事:理解問題、做合理決定、解釋取捨、持續推進
作者列的四件事:能不能理解問題、做出合理決定、解釋取捨、並持續推進;沒有一件是「找到正確架構」
證明 Clarify-Choose-Explain
演練 這四件事證明面試在測什麼?下次被問「設計一個系統」時,你會先做四件事裡的哪一件、怎麼讓面試官看到?
第 1 段 → 卡住的不是知識,是選擇
搜尋結果要位置感→page-based;動態牆持續長新內容→cursor+無限捲動
搜尋結果:使用者想翻頁、回上一頁、比較不同部分,「頁」符合心智模型;feed:使用者不會想「去第七頁」,新內容隨時冒出,列表不斷長大
證明 cursor-based pagination
演練 這組對照證明了「分頁策略取決於什麼」?你手上的列表頁,資料在瀏覽期間會變嗎、使用者需要回到特定位置嗎——所以你會從哪種開始?
第 3 段 → 例一:分頁 — 搜尋結果 vs 動態牆
feed 用 offset 分頁時,新貼文插到最前面會讓使用者往下捲時重複看到同一則
AI 補充:這是真實可觀察的 bug,不是理論疑慮;搜尋結果是一次查詢的靜態快照,所以 offset 的弱點在那裡幾乎不發生
證明 cursor-based pagination
演練 這個 bug 證明 offset 在什麼條件下才會出問題?你怎麼用「資料變動頻率」這一個變數就推出該用哪種分頁?
第 3 段 → 例一:分頁 — 搜尋結果 vs 動態牆
白板:高頻細粒度、常同時動同一物件→需 CRDT/OT;看板:離散動作、少撞→後寫覆蓋就夠
白板(繪圖、設計工具)多人持續互動同一區域,順序與衝突都重要;看板(Trello、Jira)搬卡、改標題、加留言,更新不連續、衝突好推理,簡單模型就夠
證明 conflict resolution
演練 這組對照證明衝突解決的複雜度由哪兩個變數決定?你專案裡的協作功能,兩人多常撞到同一個欄位、撞到的代價多大?
第 4 段 → 例二:即時協作 — 白板 vs 看板
好題目測的不是你認不認得某個模式,而是能不能在情境中套用它
作者回收兩個例子:分頁不只是 offset 對 cursor,而是產品體驗與使用者如何在資料中移動;即時協作不只是 WebSocket 或 CRDT,而是互動模式、衝突模型、需要多少複雜度
證明 Clarify-Choose-Explain
演練 這句話證明了「多練題目」為什麼沒用?面試官真正想確認的是「明天把你放進專案會怎樣」,你會怎麼在回答裡讓他看到這一點?
第 8 段 → 總結:把模式用在情境裡

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

offset 與 cursor 分頁的差別
offset-based 與 cursor-based pagination 各怎麼取下一批?
offset:跳過 N 筆再取 M 筆(LIMIT/OFFSET);cursor:從這個標記(排序鍵)之後再給我 N 筆
第 1 段 → 卡住的不是知識,是選擇 · offset-based pagination cursor-based pagination
total cost of ownership:不只寫程式的時間,還有每次上線、除錯、招人、交接的持有成本
「導入成本太高」的成本包含哪些?
從導入到汰換之間的全部:寫、維運、除錯、升級、教學、交接——不只開發時間
第 2 段 → 真實工程不是選擇題 · total cost of ownership
「我會從這個方向開始(start with)」把決定從宣稱最佳降級成可修正的起點
作者回答分頁題時刻意用哪個措辭?為什麼?
「I'd start with」——把決定變成可修正的起點,免除必須正確的壓力
第 3 段 → 例一:分頁 — 搜尋結果 vs 動態牆
「怎麼傳」(WebSocket)與「衝突怎麼解」(CRDT/OT/LWW)是兩個獨立的決定
選了即時同步是不是就得整套上 CRDT?
不是。傳輸方式(WebSocket vs polling)和衝突解決(LWW / OT / CRDT)是可以分開決定的兩個維度
第 4 段 → 例二:即時協作 — 白板 vs 看板 · WebSocket CRDT operational transformation
CRDT 與 operational transformation 的差別
CRDT 和 OT 各靠什麼讓大家收斂到同一結果?
CRDT:資料結構設計本身保證各方套用後必然收斂,不需中央仲裁;OT:把編輯記成操作,並發抵達時依先後轉換參數
第 4 段 → 例二:即時協作 — 白板 vs 看板 · CRDT operational transformation
YAGNI:在真正需要之前不要先實作某個功能或抽象
YAGNI 是什麼?
You Aren't Gonna Need It——極限編程原則:真正需要之前不先實作功能或抽象
第 6 段 → 選一個合理起點:steel thread · YAGNI
前端架構決定哪些可逆、哪些不可逆
前端裡哪些決定是兩向門、哪些接近單向門?
可逆:換分頁策略、換狀態管理函式庫;不可逆:資料模型、跨團隊 API 契約
第 7 段 → 把取捨講清楚

3 Frontend Skills AI Can't Replace (become AI-proof)

theSeniorDev 前端底層與系統設計 產生於 2026-09-21 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

把測試交給 AI 的前提是規格:指定要測什麼行為、覆蓋率、金字塔分層;驗收看它測行為還是測實作
  1. 列出這個模組對外承諾的行為(不是內部函式)
  2. 指定分層:單元為主、整合次之、E2E 最少
  3. 指定覆蓋率目標
  4. 讓 agent 寫,review 時逐個測試問:這在測行為還是測實作細節?
  5. 測實作細節的刪掉重寫
今天就做 挑你專案裡一個沒有測試的模組,先寫 5 條它承諾的行為,再讓 agent 據此產測試,刪掉任何斷言內部實作的測試
第 6 段 → 自動化測試:規格給對,AI 就交得出來 · testing pyramid test coverage
掃一個 AI 產出的元件找寫死值:任何 [Npx]、hex 色碼、magic number 都換成設計代號
  1. grep className 裡的 \[\d+px\]、#[0-9a-f]{3,6}、任意數字
  2. 每一個問:設計系統裡有沒有對應的尺度/代號?
  3. 有就換;沒有就問設計系統該不該加,而不是留數字
  4. 把這條規則寫進 agent 的指令檔
今天就做 在你的專案跑 grep -rn 'text-\[' src/ 與 grep -rn '#[0-9a-fA-F]\{6\}' src/,把前 5 個結果換成設計代號
第 7 段 → code quality 之一:寫死的 CSS 值 · hardcoded value atomic CSS class
把該被重用的東西在架構上做成顯而易見:抽成模組間共享的 library,agent 不必讀完全世界也找得到
  1. 找出兩處以上幾乎一樣的元件/工具函式
  2. 抽到 shared/ 或 packages/ui 這種名字就說明用途的位置
  3. 原處改成 import
  4. 在 agent 指令檔寫一句:新元件先查 shared/
今天就做 在你的專案找一個被複製過的小元件(Button、Text、Card),抽成共享模組並把兩處都改成 import
第 8 段 → code quality 之二:內嵌元件與重複 · code duplication memoization
無障礙交給 agent 的三條指令:優先語意 HTML→不得已才 aria 標角色→確認 tab 順序與畫面一致
  1. 能用 button/nav/main 就不用 div + onClick
  2. 真的得用 div 時加 role 與 aria-*(標錯比不標更糟)
  3. 用 Tab 鍵走一遍,順序要跟視覺順序一致(CSS 可能改了視覺順序但 DOM 沒變)
  4. 把這三條寫進 design system 的元件,讓 AI 從那裡學
今天就做 打開你專案的一個表單頁,只用 Tab 與 Enter 走完整個流程,記下第一個順序跳掉或到不了的元素
第 11 段 → 無障礙:定義清楚,所以大多能自動化 · semantic HTML ARIA tab order
對每個 state 問「能不能由別的 state 推導」,能就刪掉改成計算;同一份後端資料只取一次
  1. 列出畫面用到的所有 useState / store 欄位
  2. 每項問:拿掉它能不能從其他欄位算出來?
  3. 能算的改成 render 時計算或 selector
  4. 找同一個 API 被兩個元件各打一次的地方,提到共同的資料層
今天就做 打開你專案裡 AI 產出最多的那個頁面,用 DevTools Network 數同一個端點被打了幾次,把重複的收斂成一份
第 12 段 → state 與資料:收斂成 essential state · essential state derived state

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

邊界不只是給人看的,也是給模型看的索引:agent 靠它知道該讀哪裡、不必讀完全世界
新 模組邊界決定 agent 改一個功能要讀進多少程式碼  ⇄  像 書的目錄與章節:讀者靠它跳到要看的地方,不必從頭讀
第 2 段 → AI 給你漂亮介面,不給你邊界
好架構是穿過所有功能點的迴歸線,不是精準通過某一點的線:對每個功能都不是最好,對全部都夠好
新 穩定的架構 = 所有功能的折衷  ⇄  像 統計的迴歸線 vs 過擬合曲線
第 3 段 → 每加一個功能就重塑一次架構
畢卡索《公牛》系列:從寫實一步步簡化到幾條線仍認得出——essential state 是找出不能再少的那組線
新 essential state:刪到不能再刪、其他都能推導的最小狀態  ⇄  像 畢卡索公牛石版畫:逐步抽象仍保留辨識度
第 12 段 → state 與資料:收斂成 essential state
工廠烤蛋糕:產量暴增時你不可能每個都吃,但必須真的抽樣嘗過——每次 agent 做完就開 PR 真的 review
新 AI 產出量暴增下,人的注意力放在輸出的抽樣檢查  ⇄  像 工廠品管:抽樣檢驗而不是全檢
第 13 段 → 從哪裡開始:看你的產品環境

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

frontend system design:模組怎麼切、狀態放哪、什麼該封裝成服務、元件間用什麼介面——不是 sharding 與 LB
frontend system design
第 1 段 → 怕被 AI 取代?先看這三個技能 · frontend system design
clear boundaries:每個模組職責明確、內部不外流、只透過介面互動——對人好維護,對 AI 便宜
clear boundaries
第 2 段 → AI 給你漂亮介面,不給你邊界 · clear boundaries programming to interfaces
token efficiency:完成同一項修改要餵給模型的 token 量;架構品質從長期指標變成每天結帳的成本
token efficiency
第 2 段 → AI 給你漂亮介面,不給你邊界 · token efficiency
overfitting:AI 每加一個功能就把周圍改成最適合它的樣子——單一功能的最佳解對整個系統是過擬合
overfitting
第 3 段 → 每加一個功能就重塑一次架構 · overfitting
code smell:不一定是錯但預示設計問題的徵兆;AI 的失手有規律,認得出才能對抗過擬合
code smell
第 7 段 → code quality 之一:寫死的 CSS 值 · code smell
context window:模型這一次能看到的世界;擴大視野的成本超線性,所以「叫它掃完整個 codebase」不成立
context window
第 8 段 → code quality 之二:內嵌元件與重複 · context window
technical debt:外表能跑、分數不差,債仍在底下堆——dead code 污染 context、duplication 污染改動成本
technical debt
第 10 段 → 放著不管的代價:8.9% 重複、153 個死檔案 · technical debt dead code static analysis
essential state:不能再少的那組資料,其他畫面上的東西都能從它算出來;AI 孤立生元件會到處長 local state
essential state
第 12 段 → state 與資料:收斂成 essential state · essential state derived state single source of truth local state

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

效能工作:診斷佔 5%、實作佔 90%,被自動化的是那 90%——價值移到「報告出來前就說出這是架構問題」
最新模型對 Core Web Vitals 這種可量測目標表現好、甚至提架構建議;作者當場把 Web Performance 塗灰,但保留「判斷是設計問題還是最佳化問題」給人
證明 frontend system design
演練 這個比例證明效能知識的價值位置移到哪裡?你上次看 Lighthouse 報告時是在做那 5% 還是那 90%?
第 5 段 → 網站效能:被塗灰的技能
最新模型產出:一行 text-2xl 的 Tailwind class 裡突然插進 sm:text-[28px]
同一個 className 前半是設計系統語彙(text-2xl)、後半是為了這次 prompt 硬塞的像素值;後果:不能隨裝置縮放、與其他頁面不一致。截圖右下角標了模型名,不是舊模型的老毛病
證明 code smell
演練 這個例子證明 AI 的失手是隨機的還是有規律的?你會怎麼在 review 時一眼掃出這類混用?
第 7 段 → code quality 之一:寫死的 CSS 值
元件定義在另一個元件函式體內:每次 render 產生新元件型別→子樹卸載重建、狀態丟失,不只是整潔問題
Spotlight 函式體內宣告的 text 元件每次 render 重建、無法被別處重用→重複增生;React 裡還會導致子樹 unmount/remount、內部 state 丟失——效能與正確性兩層
證明 code smell
演練 這證明 nested component 的代價在哪兩個層面?你會怎麼把它抽出去並決定放進哪個共享 library?
第 8 段 → code quality 之二:內嵌元件與重複
作者自家掃描:153 個未使用檔案、220 個未使用匯出、9,760 行重複(8.9%);可維護性 89.7 卻標 good
Dead Code 413 個問題;Duplication 9,760 行、8.9%(靜態分析,一模一樣不是相似);4,780 個函式裡 367 個超過複雜度門檻;平均可維護性 89.7 = good——整體分數好看,問題集中在幾百個地方
證明 technical debt
演練 這份報告證明「總分好看」為什麼不可信?你會先處理 dead code 還是 duplication,理由是哪一種污染?
第 10 段 → 放著不管的代價:8.9% 重複、153 個死檔案

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

判斷一項技能會不會被 AI 吃掉:成功條件能不能寫清楚——能就會被自動化,人只剩檢查
作者用什麼判準決定一項前端技能該塗灰?
成功條件是否明確可寫:design into code、效能、測試、無障礙都能→塗灰;決定邊界與取捨不能→彩色
第 1 段 → 怕被 AI 取代?先看這三個技能
白板上塗灰不是刪掉:灰色 = 仍要懂但不再是你的差異化;消失的是工作內容不是職位
design into code 被塗灰是什麼意思?五個人的活變成什麼?
仍需有人懂但不靠它勝出;變成一個人 + AI + design system——同一個人要定規則、驅動 agent、驗收
第 4 段 → design into code:受衝擊最大的那一塊 · design into code design system
AI 在測試上的真正貢獻:把一件長年排不進時間的工作變得便宜到值得做,作者專案覆蓋率因此比以前高
面對「AI 寫的測試在測空氣」的批評,作者把責任放在哪?
放回規格——你要給出測試的本質(要測什麼),苦工才交出去
第 6 段 → 自動化測試:規格給對,AI 就交得出來
一致性不是潔癖:使用者的大腦在一致的介面上耗費較少,能把注意力留給真正在變的東西
為什麼寫死 28px 除了不能縮放還有問題?
破壞一致性→提高 cognitive load;使用者要重新學這個地方的規則
第 7 段 → code quality 之一:寫死的 CSS 值 · cognitive load hardcoded value rem atomic CSS class
技能光譜:上端是架構取捨、下端是執行細節、中間是規格轉程式碼;AI 吃中間且越吃越寬,人往兩端走
為什麼很多人被評為「聽起來不夠資深」?
只有一端:只會架構驗收不了產出,只會細節說不出模組怎麼切;面試問的正是沒標準答案的取捨
第 9 段 → 兩端都要:高層架構 + 鑽到細節
無障礙是 non-functional requirement,商業上總被推著趕快做完,所以自動化誘因特別大;品質要內建在來源
以前的兩週無障礙衝刺,現在換成什麼做法?
做 design system 的人先做對,AI 從那裡學——品質內建在來源而不是事後補救
第 11 段 → 無障礙:定義清楚,所以大多能自動化 · non-functional requirement

Frontend System Design: Performance API, Chrome, React Profiler

I Code It 前端底層與系統設計 產生於 2026-09-21 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

把「這頁很慢」變成三個必答題:有多慢?哪一部分慢?跟什麼比?答不出就不要動手改
  1. 寫下「有多慢」:一個數字與單位(ms)
  2. 寫下「哪一部分慢」:是哪個操作/哪段互動
  3. 寫下「跟什麼比」:改動前的自己,同一段程式、同一批資料、同一台機器、同一種 build
  4. 確認測量夠便宜,你真的會跑第二次
今天就做 挑你專案裡一個大家都說「有點慢」的操作,只寫下三個答案(多慢/哪裡/跟什麼比),寫不出的那一格就是今天要補的量測
第 1 段 → 「頁面很慢」不是資訊
量第一條 baseline:performance.now() 前後各取一次,相減得 duration,印出來
  1. const start = performance.now()
  2. 執行受測函式(例如 analyzeLogs(logs, query))
  3. const duration = performance.now() - start
  4. console.log 帶名字印出;跑多次取中位數(瀏覽器會刻意調粗時鐘解析度)
  5. 保留這段測量碼:改演算法或搬 web worker 後用同一段再量一次
今天就做 在你專案裡挑一個純同步的函式(排序、過濾、格式化),用 performance.now() 夾住跑 5 次,把中位數寫進該檔案頂端的註解當 baseline
第 3 段 → performance.now 量出第一條 baseline · performance.now() DOMHighResTimeStamp
mark/measure 把一個操作拆成有名字的步驟,getEntriesByType('measure') 撈回來丟 console.table
  1. performance.mark('search-start') → 執行 → performance.mark('search-end')
  2. performance.measure('search', 'search-start', 'search-end');filter、aggregate 比照
  3. performance.getEntriesByType('measure').map(e => ({name: e.name, duration: e.duration}))
  4. console.table(...) 印成表,看比例不看絕對值
  5. 每次量完 clearMarks() / clearMeasures(),否則多按幾次會越撈越多
今天就做 把上一筆那個函式的內部拆成 2–3 個步驟,各打一組 mark/measure,用 console.table 印出來,找出佔比最高的那一步
第 4 段 → mark / measure:把一個操作拆成有名字的步驟 · performance.mark() performance.measure() performance.getEntriesByType() console.table()
錄一段互動:先設 CPU throttling 4x–6x,按錄(Cmd/Ctrl+E)、再做跟 baseline 同一個操作、停止
  1. DevTools → Performance 面板,右下 Environment settings 把 CPU 設成 4x 或 6x
  2. 先按錄製(Cmd/Ctrl+E),再做互動——順序反了那次互動不在時間軸上
  3. 重跑跟 baseline 完全相同的操作(同一個搜尋字串),兩邊數字才對得起來
  4. 停止錄製,先看左下 Summary 的 Scripting / Rendering / Painting 比例
今天就做 對你 baseline 量過的那個操作,開 4x CPU throttling 錄一次,只寫下 Summary 四個數字(Scripting/Rendering/Painting/System),判斷該去查 JS 還是樣式
第 6 段 → Chrome Performance:錄下整段互動 · CPU throttling
只 profile 在意的那棵子樹:包 Profiler、掛 onRender、固定看 id / phase / actualDuration 三個值
  1. import { Profiler } from 'react'
  2. <Profiler id="Results" onRender={onRender}><ResultsList logs={logs} /></Profiler>
  3. function onRender(id, phase, actualDuration, baseDuration, startTime, commitTime) { console.log(id, phase, actualDuration) }
  4. 只包懷疑的那塊,不包整個 app;多個 Profiler 共用 callback 靠 id 分辨
  5. actualDuration 跟 baseDuration 一起看:差距就是 memo 省下的
今天就做 把你專案裡最重的一個列表元件用 <Profiler> 包起來,觸發一次更新,記下 phase 與 actualDuration,再跟 baseDuration 比一次看 memo 有沒有省到
第 8 段 → React Profiler:只量你在意的那棵子樹 · onRender actualDuration baseDuration

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

Performance API 是碼錶:開始前按下、結束按停,差就是 duration——但夾住的程式必須是同步的
新 performance.now() 前後各取一次相減  ⇄  像 手上的碼錶:按下、按停、讀差值
第 2 段 → Performance API:瀏覽器內建的碼錶
每個工具都是拼圖的一塊,有一整片自己看不到的區域
新 Performance API/Chrome Performance/React Profiler 各有盲點,合起來才有完整圖像  ⇄  像 盲人摸象:每個人摸到的都是真的,但都不是全貌
第 9 段 → 三個數字擺在一起才有結論
三個工具是三種解析度:自己插的探針 → 瀏覽器全景錄影 → 框架的一次 commit;由粗到細用
新 Performance API(你打點的粒度)、Chrome Performance(每個 task 與 call stack)、React Profiler(一棵子樹一次渲染)  ⇄  像 顯微鏡換倍率:先低倍找到區域,再高倍看細節
第 11 段 → 總結:先量,再決定要優化什麼

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

baseline:優化前先量到的一組可重跑的數字;「頁面很慢」要逼成有多慢、哪裡慢、跟什麼比
baseline
第 1 段 → 「頁面很慢」不是資訊 · baseline performance optimization
Performance API 的天花板:只知道多久(你的程式碼從哪行到哪行),不知道為何(JS?layout?paint?)
profiler
第 5 段 → Performance API 的天花板:知道多久,不知道為何 · profiler layout
Chrome Performance 面板:不必安裝的錄製式 profiler,記錄各執行緒所有工作的時間軸
Chrome DevTools Performance panel
第 6 段 → Chrome Performance:錄下整段互動 · Chrome DevTools Performance panel CPU throttling
main thread 被佔住畫面就不更新;long task = 主執行緒連續超過 50ms 的工作
main thread
第 7 段 → 火焰圖指出:時間全花在 main thread · main thread long task
flame chart:橫軸時間、縱軸呼叫深度、越往下是被誰呼叫;self time ≈ total time 才代表時間燒在這個函式自己
flame chart
第 7 段 → 火焰圖指出:時間全花在 main thread · flame chart self time
React Profiler:<Profiler id onRender> 包住子樹,每次 commit 回報 actualDuration
React Profiler
第 8 段 → React Profiler:只量你在意的那棵子樹 · React Profiler onRender phase actualDuration baseDuration
bottleneck:佔掉最多時間的那段;三個數字並排才看得出來——30ms React ÷ 400ms 互動 ≈ 7%,React 不是問題
bottleneck
第 9 段 → 三個數字擺在一起才有結論 · bottleneck

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

demo:20,000 筆 log 的 analyzeLogs 印出 Log analysis took 9.50ms——數字小不代表方法沒用
const start: DOMHighResTimeStamp = performance.now(); const result = analyzeLogs(logs, query); const duration = performance.now() - start → 9.50ms(20,000 筆);檔案註解:You learn how long — not which stage is slow
證明 baseline
演練 這個 9.5ms 證明了 baseline 的價值在哪(絕對值還是可重跑)?你會用同一段碼去比哪兩個實作?
第 3 段 → performance.now 量出第一條 baseline
拆解結果:search 8.20ms、filter 0.10ms、aggregate 0.20ms——96% 落在 search,這種傾斜才是拆解買到的
console.table:search 8.20ms / filter 0.10ms / aggregate 0.20ms,合計 8.5ms;search 佔 96%
證明 bottleneck
演練 這張表證明了「拆解」的目的是找比例不是找精度?看到 96% 你下一步會去改哪裡、不改哪裡?
第 4 段 → mark / measure:把一個操作拆成有名字的步驟
demo:Log Analyzer 吃 50,000 筆 log 花 473.9ms,可切 Main thread / Worker
頁面標 Ingested 50,000 logs、Where analysis runs: Main thread / Web Worker、LAST RUN 473.9 ms;Local metrics LCP 0.07s、CLS 0.00、INP 24ms
證明 Chrome DevTools Performance panel
演練 這個 demo 從一開始就有 Main / Worker 切換,證明了什麼樣的實驗設計才能做前後對照?你的專案有沒有這種可切換的開關?
第 6 段 → Chrome Performance:錄下整段互動
Summary:13.53s 錄製裡 Scripting 3,373ms、Rendering 205ms、Painting 139ms——沒放大就知道不必查樣式
Scripting 3,373ms / Rendering 205ms / Painting 139ms / System 264ms;放大 2.73–3.75s 呼叫堆疊 … runDemoAnalysis → simulateHeavyWork 209.49ms(self 193.36ms),來源 src/lib/analysis.ts;Frames 軌一格 39.5ms 掉幀
證明 flame chart
演練 self 193 / total 209 證明了該優化這個函式本身還是它呼叫的下層?如果 self 很小 total 很大,你會去改哪裡?
第 7 段 → 火焰圖指出:時間全花在 main thread
React render 30ms vs 整次互動約 400ms:只看 Profiler 會去優化元件,只看總時間會冤枉 React
React (30ms) Rendering 字幕卡 vs 互動約 400ms(含多次分析)→ 約 7%;前提是兩個數字量的是同一次互動的同一段時間
證明 bottleneck
演練 這個 7% 證明了工具的盲點是對稱的?你手上哪個效能結論其實只來自單一工具?
第 9 段 → 三個數字擺在一起才有結論

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

Performance API:now、mark/measure、getEntries、PerformanceObserver
Performance API 包含哪幾組介面?
now() 取時間;mark() / measure() 打點命名;getEntries 系列取回;PerformanceObserver 非同步監聽
第 2 段 → Performance API:瀏覽器內建的碼錶 · Performance API
為什麼用 performance.now() 不用 Date.now():單調遞增、頁面載入為原點、不受系統校時影響
量耗時為什麼要用 performance.now() 而不是 Date.now()?
performance.now() 單調遞增、以頁面載入為原點、帶小數毫秒;Date.now() 在 NTP 校時瞬間可能量出負值
第 3 段 → performance.now 量出第一條 baseline · performance.now()
measure 的結果留在瀏覽器 performance timeline,Chrome 錄製工具也讀得到;now() 只活在區域變數
performance.measure() 比 performance.now() 多買到什麼?
結果進 performance timeline 緩衝區,DevTools Performance 面板錄製時看得到同一批命名區段;now() 的值只在你的變數裡
第 4 段 → mark / measure:把一個操作拆成有名字的步驟 · performance.measure()
mark/measure 兩個坑:名稱打錯不報錯只安靜少一列;不清會累積
用 mark/measure 最常踩的兩個坑?
字串名稱打錯不會報錯、只是表上少一列;mark 與 measure 會累積,要 clearMarks() / clearMeasures()
第 4 段 → mark / measure:把一個操作拆成有名字的步驟
「Performance API 答不了為什麼」不絕對:PerformanceObserver + LoAF 可部分歸因
Long Animation Frames API 能做到什麼、做不到什麼?
回報造成卡頓的長影格,附腳本來源與樣式/版面耗時;但給不出 call stack——「layout 佔多久」與「哪一行觸發」仍是兩件事
第 5 段 → Performance API 的天花板:知道多久,不知道為何 · Long Animation Frames API
兩個工具各答一題:Performance API 答「花了多久」,Chrome Performance 答「時間花在哪」
Performance API 跟 Chrome Performance 面板各回答什麼問題?
Performance API:這個操作花了多久;Chrome Performance:瀏覽器把時間花在哪(哪個執行緒、哪個函式)
第 7 段 → 火焰圖指出:時間全花在 main thread
onRender 六個參數:id, phase, actualDuration, baseDuration, startTime, commitTime
React Profiler 的 onRender 有哪些參數?後兩個做什麼用?
id, phase, actualDuration, baseDuration, startTime, commitTime;startTime/commitTime 用來把 React commit 對到 Chrome 時間軸
第 8 段 → React Profiler:只量你在意的那棵子樹 · onRender
baseDuration = 假設完全沒 memo 時的估計渲染時間;跟 actualDuration 一起看才知道該不該加 memo
baseDuration 是什麼?為什麼只看 actualDuration 不夠?
整棵子樹無 memo 全部重渲染的估計時間;actual 遠小於 base 代表 memo 有效,兩者接近代表 memo 沒省到(或沒加)
第 8 段 → React Profiler:只量你在意的那棵子樹 · baseDuration actualDuration
Profiler 數字只當 A 實作對 B 實作的相對比較,不是 production 絕對值
React Profiler 的數字可以拿去對外宣稱嗎?為什麼?
不行,只當相對比較:dev 有 StrictMode 灌水與 profiling overhead,production build 預設關掉 Profiler;數量級差距(30 vs 400)不會因誤差翻盤
第 10 段 → Profiler 的數字怎麼讀才對 · profiling overhead production build
StrictMode 開發模式把 render 跑兩次逼出不純副作用,actualDuration 偏高且不是穩定兩倍
為什麼 StrictMode 下的 actualDuration 不能「除以二」?
第二次 render 通常較快(快取已熱),偏高的倍率不穩定
第 10 段 → Profiler 的數字怎麼讀才對 · StrictMode
Chrome 時間軸左側的 Scheduler ⚛ / Components ⚛ 軌就是 React 寫進瀏覽器時間軸的自訂軌道
怎麼在同一條時間軸同時看 React 的工作與底下的 JS 呼叫堆疊?
Chrome Performance 錄製時 React 會寫入 Scheduler ⚛ 與 Components ⚛ 兩條自訂軌道
第 10 段 → Profiler 的數字怎麼讀才對
找到瓶頸後的下一半:worker、caching、rendering strategy、virtualization、code splitting
作者點名的「找到瓶頸之後」有哪五個手段?共同前提是什麼?
web worker、caching、rendering strategy、virtualization、code splitting;每個都改變系統結構,必須先有 baseline 才知道賺賠
第 11 段 → 總結:先量,再決定要優化什麼 · virtualization rendering strategy web worker code splitting

Lauren Tan grokbot workshop 中文字幕

Casey 工程職涯與架構角色 產生於 2026-09-21 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

每觀察到一種失敗模式,就把它寫成一個 skill——P-Stack 不是設計出來的,是一次一個長出來的
  1. 打開所有 tool call 與思考區塊,看 agent 在哪裡失敗
  2. 判斷失敗類型:沒讀程式碼就斷言、不知道功能在哪、猜而不驗
  3. 把對應的做法寫成一份 SKILL.md(例如「不要猜、去把程式碼讀出來、多用 subagent」)
  4. 像教一個程式能力強但零脈絡、五秒前才報到的新人:寫下來
  5. 之後用 eval 確認它真的有效
今天就做 翻你最近一次 agent 做錯的對話,找出 tool call 裡它「該做而沒做」的那一步,寫成一條 10 行內的 skill 或 rules 檔放進專案
第 8 段 → P-Stack 是觀察失敗模式長出來的 · skill subagent
迭代迴圈:當後座駕駛觀察 tool call → 寫 skill → eval 量分 → 換模型的 judge 打分 → slash loop 爬到滿分
  1. 初期不當被動觀察者:打開所有 tool call、讀思考區塊,像 pair programming 忍不住問「為什麼不這樣做」
  2. 找到失敗點,為它做一個 skill
  3. 用 eval 給分;judge agent 換另一個模型,避免寫與評的偏誤相關
  4. 用 slash loop 讓模型自己改到全部十分(hill climbing)
  5. 回到第一步觀察新的失敗;記住分數只反映 rubric,品味體現在 rubric 上
今天就做 拿你正在用的一份 rules/skill,寫 3 條 rubric(各一句「做得好是什麼樣」),讓 agent 跑 3 次同一個小任務並用另一個模型打分
第 10 段 → 當後座駕駛,再讓 eval 爬山 · judge agent hill climbing pair programming
每次你必須在 PR 上留言糾正,就是壞味道:問怎麼把它變成 lint 規則、CI failure,或從根本讓它不可能發生
  1. 翻最近的 review 留言,找出重複出現的糾正
  2. 問三個問題,依優先序:能不能讓它根本不可能發生(架構/目錄)?能不能變 CI failure?能不能變 lint 規則?
  3. 實作最強的那一層;rules/skill 只當補強
  4. 留言從此不再需要——那條不變量改由工具維持
今天就做 翻你最近 10 條 review 留言(或你常對 agent 說的糾正),挑一條最常重複的,今天就寫成一條 eslint/ruff 規則或一個 CI 檢查
第 17 段 → 選技術棧,也把 review 變成規則 · lint rule invariant code smell

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

管 agent 的技巧和管人的技巧高度重疊——整場的類比基礎
新 和 agent 一起寫程式:怎麼分配、怎麼信任、怎麼教它  ⇄  像 當工程經理帶人:教新人、建立信任、決定放多少手
第 1 段 → 講者背景:從 Meta React 到 Cursor
eval 是 skill 的單元測試:每次改 skill 就跑,不需要特殊框架
新 eval:一組任務 + 評分準則,量測 skill 改了之後 agent 表現有沒有變好  ⇄  像 單元測試:改程式就跑,紅了就知道改壞了
第 9 段 → 用 eval 當 skill 的單元測試
工程師像主廚:不再自己煮每道菜,而是帶線上廚師與副主廚、設計整個廚房環境
新 近乎放手:人只在「觀察失敗」與「訂 rubric」兩處出現,中間全自動  ⇄  像 主廚設計菜單、動線與廚房規則,廚師照著做
第 10 段 → 當後座駕駛,再讓 eval 爬山
「在 AI slop 之前我們就有 human slop」:agent 就是幾萬人 monorepo 裡「能力不均的貢獻者」的一員
新 agent 大量產出、品質不均的程式碼  ⇄  像 大公司 monorepo 早就面對大量能力不均的貢獻者,答案是把標準做進工具而不是要求每個人變強
第 13 段 → 真正的風險是沒有護欄的綠地專案
花錢請人 vs 花 token 把程式庫整理到最笨的 agent 都能寫好:月成本 vs 一次性資本支出
新 前期重構、補約束很燒 token,但做完後每個 agent、每位新同事都免費受益,還換到模型降級空間  ⇄  像 請人是持續的薪資支出;蓋基礎建設是一次性投資,之後邊際成本趨近零
第 18 段 → token 成本:這筆投資的 ROI

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

trust curve:縱軸信任、橫軸能同時跑的 agent 數,左端逐行盯著看、右端 auto-merge 早上在 main 上事後 review
trust curve
第 3 段 → 信任曲線:從盯著看到自動合併 · trust curve in the loop auto-merge
verification:讓 agent 自己把程式跑起來、抓 trace、開模擬器實測——不保證「好」程式,只保證「正確」
verification
第 5 段 → 第一項技能:verification · verification CPU trace heap snapshot
skill:用 markdown 寫、教 agent 在特定情境怎麼做事的指令檔;每一個都是從一種失敗模式長出來的
skill
第 6 段 → control glass:人不該是驗證瓶頸 · skill P-Stack
feature map:一個功能一個 md,從使用者視角寫「是什麼、怎麼抵達、怎麼用程式驅動」,讓爛回報也能對到功能
feature map
第 7 段 → feature map:教 agent 怎麼走到功能 · feature map ARIA label
eval:agent 的單元測試——主 agent 訂 rubric,派一批 subagent 在看不出被評測的目錄裡跑,跨模型看表現
eval
第 9 段 → 用 eval 當 skill 的單元測試 · eval rubric evaluation awareness
guardrail:限制貢獻者能做什麼、讓錯誤做法難以完成的機制——大公司為最弱工程師設計的護欄剛好也擋住 agent
guardrail
第 13 段 → 真正的風險是沒有護欄的綠地專案 · guardrail brownfield greenfield
organic architecture:沒人設計、由 agent 一次次選最方便的捷徑堆出來的結構,你看不懂、agent 也只是照著堆
organic architecture
第 13 段 → 真正的風險是沒有護欄的綠地專案 · organic architecture vibe coding
co-location/最短路徑就是最好的路徑:一個 feature 的所有程式碼放同一目錄,把 agent 愛抄的捷徑設計成正解
co-location
第 16 段 → 分層防線:硬的擋、軟的補 · co-location dependency graph AGENTS.md

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

agent 第一百次篤定說「找到兇手了」卻抓錯:傷害不在答錯,而在答錯時語氣跟答對一樣
AI 補充:這是 calibration(信心校準)問題——會說「我不確定」的模型輸出可以分流(確定的直接採用、不確定的人工看),永遠篤定的模型逼你每次全部檢查。後面所有解法都在替 agent 補上它給不出的信心來源
證明 trust curve
演練 這證明了信任問題的根源是什麼(能力不足還是信心不可分流)?你會怎麼用外部訊號(測試、CI)替 agent 補上校準?
第 2 段 → 核心問題:你怎麼信任 agent
GitHub 貢獻卡:3,159 commits,Jan '26 後衝到 750 附近、上月一千個 PR——只證明量,不證明品質
帳號 poteto、排名第 6;長條圖 Jan '22 到 Jul '26 前幾年貼底,Jan '26 起竄升。她主動把「這些程式碼品質如何」拿出來當後半場待答題
證明 trust curve
演練 這張圖證明了什麼、沒證明什麼?如果你要用自己的產出量說服團隊採用 agent,還需要補哪一種數據?
第 4 段 → 產出曲線佐證這條路走得通
救 glass 效能第一週:自己錄 trace 貼截圖,agent 每次篤定指認卻改錯——沒有 verification 時你就是瓶頸
每次往返消耗人的一次注意力,agent 數量上限 = 人的注意力 ÷ 每次往返成本;這是信任曲線左端為什麼那麼陡的機制解釋。解法是 control glass skill 把操作應用程式、抓 trace 交還給 agent
證明 verification
演練 這段經驗證明了瓶頸在哪一端?你現在跟 agent 合作時,哪一件事是你在幫它「看」?把它交還給 agent 要先做什麼?
第 6 段 → control glass:人不該是驗證瓶頸
判斷 agent 在幻覺的依據不是「答案錯」而是「tool call 顯示它沒讀過那段程式碼」——過程指標
過程指標可以在錯誤造成後果之前就發現;這解釋了她為什麼堅持打開所有 tool call、讀思考區塊
證明 skill
演練 過程指標 vs 結果指標證明了什麼?你會在自己的 agent 流程裡設哪一個「它應該先做什麼」的檢查點?
第 8 段 → P-Stack 是觀察失敗模式長出來的
Benny 收到「切換 panel focus 會打開空白側欄」的回報,在前一個 commit 重現、修正版消失:自動做了一次小 bisect
Slack #glass-oncall-assistant 頻道;回覆標題 Reproduced but already fixed on main,附 view cloud agent 連結。做到這件事需要 control skill(能操作)+ feature map(知道 apps 側欄在哪、怎麼觸發 panel focus)
證明 verification
演練 Benny 證明了 cloud agent 的複利來自什麼(模型能力還是環境設定)?你的團隊哪一種重複性回報最適合先交給這種 agent?
第 11 段 → 先在本地觀察,再放上雲端
跳級的失敗不是「agent 做不出東西」而是「你沒能力判斷對錯」:瓶頸從輸入端移到輸出端,吞吐量沒變卻多付 token
AI 補充:一千個雲端 agent 的一千個 PR 沒有 verification 與硬約束時,還是回到你一個人逐一審查。曲線量的是你的判斷能力,不是 agent 的能力;別人的 skill 編碼的是別人的標準,信任是借來的
證明 trust curve
演練 這解釋了「沒有捷徑」的機制——你現在在曲線哪個位置?下一步該做 verification、雲端、還是 auto-merge?為什麼不能跳?
第 12 段 → 沒有捷徑:信任要自己長出來
六百多個 PR 把 Grokbot 重構到 Dune:現在幾乎不看程式碼,早上二十個 PR 已合併也不怕半夜混進效能退步
收穫不只利己:設計師、PM、GTM 都能安全地加功能。邏輯是把檢查位置換了——品質在架構與 CI 先擋,人只事後抽驗
證明 guardrail
演練 「不看程式碼」成立的前提是什麼?你的專案有哪一條規則現在只靠 review 留言維持,如果它變成 CI 你就能少看多少?
第 14 段 → 投資約束,換來不用讀程式碼
禁註解的理由:agent 會把你對某個 PR 的一句「這裡不要這樣寫」寫成程式碼裡的永久全域規則
agent 分不清「這次的指正」與「永遠的規則」,對脈絡時效性沒概念——跟它會把過期 skill 當現行規則是同一種病。另補:Electron main/renderer 是兩個行程靠 IPC 溝通,16ms = 1000/60 是硬性物理預算
證明 guardrail
演練 這證明了 agent 缺什麼能力?除了註解,你的專案還有哪個地方會累積「一次性指正被當永久規則」的垃圾?
第 15 段 → Dune 的硬約束:連註解都禁
PM 用 iMessage 般的 Grokbot 直接送修 bug 的 PR,她 review 完常直接蓋章:介面決定誰進得來,架構決定能不能合
Cursor 原本的 agent window/CLI/IDE 預設開發者心智模型(檔案樹、終端機、diff);Grokbot 把互動改成聊天串、agent 擬人化成有名字的同事。能讓 PM 的 PR 直接蓋章的不是介面,是 Dune 五條規則與兩層硬檢查
證明 guardrail
演練 這證明了嚴格架構是成本項還是擴張項?你的專案若讓非工程師送 PR,第一個會被擋住的是介面還是缺乏硬約束?
第 19 段 → Grokbot:非工程師的 Cursor 時刻

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

四個例子涵蓋四層驗證:跑程式=正確性、CPU trace=效能、heap snapshot=記憶體、iOS 模擬器=實際介面
她舉的四種 verification 各驗證哪一層?
跑程式→正確性;CPU trace→CPU 效能;heap snapshot→記憶體洩漏;iOS 模擬器→使用者實際介面
第 5 段 → 第一項技能:verification · CPU trace heap snapshot
control glass skill:多實例不互相干擾,主 checkout 用 CDP 9222,worktree 各自 port
一個好用的 verification skill 大半內容是什麼?為什麼?
不是「怎麼操作」而是「多個 agent 同時跑時怎麼不互相干擾」(worktree 分流 port、獨立 user-data-dir、doctor 唯讀檢查)——這是平行化的前提
第 6 段 → control glass:人不該是驗證瓶頸 · Chrome DevTools Protocol worktree skill
feature map README:只教「怎麼使用功能」不寫「怎麼實作」,因為程式碼變得比文件快——文件能長期存活的關鍵
feature map 為什麼刻意不寫實作細節?
實作變得比文件快,寫了就會過期;只寫使用者視角的導航、baseline preconditions 與 driving conventions(ARIA label、data-* 屬性)
第 7 段 → feature map:教 agent 怎麼走到功能
eval 一定要一批 subagent 而不是跑一次:單次結果分不出「skill 改好了」和「這次運氣好」
為什麼 eval 要派一批 subagent 而不是跑一次?
模型輸出有隨機性,要一批才有分佈可看
第 9 段 → 用 eval 當 skill 的單元測試 · subagent
subagent 的目錄要命名得看不出是在被評測:agent 有評測意識,察覺時行為會變
eval 的 subagent 目錄為什麼要刻意取一個看不出在評測的名字?
evaluation awareness:agent 察覺被評測會改變行為,量到的就不是平常表現(類似人類的霍桑效應)
第 9 段 → 用 eval 當 skill 的單元測試 · evaluation awareness
judge agent 換模型不是儀式:同一模型既寫又評,偏好風格會同時出現在兩邊,分數失去鑑別力
為什麼 judge agent 要刻意用另一個模型?
讓評分者的偏誤與被評者的偏誤不相關;同模型自評會偏好自己的風格
第 10 段 → 當後座駕駛,再讓 eval 爬山 · judge agent
PR 沒有硬上限但鼓勵拆:git 歷史是脈絡來源,不讀程式碼時 revert 是唯一安全網,混五件事的 PR 會讓它失效
agent 時代為什麼更該把工作拆成多個 PR?
不讀程式碼時 git 歷史是唯一還讀得動的東西、revert 是最後的安全網;一個 PR 混多件事,revert 會連帶砍掉無辜修改
第 14 段 → 投資約束,換來不用讀程式碼 · revert pull request
Dune 的硬約束:禁 useEffect(CI 失敗)、禁程式碼註解;原則是 agent 做不好的事全部禁掉
Dune 禁了哪兩樣讓人挑眉的東西?背後原則是什麼?
useEffect(React 最大 foot gun)與程式碼註解;agent 做不好的事情就全部禁掉,讓 CI 機械性地失敗
第 15 段 → Dune 的硬約束:連註解都禁 · useEffect foot gun
五層防線:codebase、static analysis、rules/BugBot、skills、style guide;前兩層讓 CI 紅
五層防線的排序與分界線在哪?
codebase → static analysis → rules/BugBot → skills → style guide;前兩層是硬約束(CI 變紅),三到五層 agent 會忘記,只能疊加不能當唯一防線
第 16 段 → 分層防線:硬的擋、軟的補 · static analysis BugBot
Rust 例子的限度:編譯器保證記憶體安全與型別正確,不是邏輯正確;「編過就不用驗證」會跟 verification 矛盾
「Rust 能編過就有信心」該怎麼正確解讀?
編譯器擋掉的那一類錯誤(記憶體、型別)人不必再檢查;邏輯正確仍要靠 verification。前提是禁止 agent 寫 unsafe
第 17 段 → 選技術棧,也把 review 變成規則 · borrow checker unsafe

15 Fullstack Concepts Every Frontend Should Know

theSeniorDev 網路與 API 基礎 產生於 2026-09-21 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

在 DevTools 驗證 DNS:第一個 document 請求的 Remote Address 就是查到的 IP 與 443 埠
  1. 開 DevTools → Network,重新整理
  2. 點第一個 document 類型的請求
  3. Headers 面板找 Remote Address:<IP>:443
  4. 用 nslookup 或 dig 查同一個網域,比對 IP 是否一致
今天就做 打開你自己專案的正式站,在 DevTools 找出 Remote Address,再用 dig 查一次,記下 TTL 是幾秒
第 2 段 → DNS:先問到 IP 才能發請求
讀一個靜態資源的快取故事:找 status、age、cache-control、etag 四個欄位兜成一句話
  1. Network 面板點一個 .js 或 .css
  2. 看 Status 是不是 (from memory/disk cache)
  3. Response Headers 找 cache-control 的 max-age、immutable
  4. 找 age,算 max-age − age = 還能直接用多久
  5. 找 etag,想像過期後帶它去問伺服器的那個請求
今天就做 對你專案正式站的主 JS bundle 做一次這五步,寫下一句「這個檔案還能直接用 N 天,過期後靠 etag X 驗證」
第 4 段 → HTTP 快取與 E-Tag
polling:setInterval 包 fetch 週期問狀態,completed 就 clearInterval;React 要放 useEffect
  1. 在 useEffect 裡 setInterval(() => fetch('/payments/:id/status'), 3000)
  2. 回 pending 繼續、回 completed 就 clearInterval
  3. useEffect 的 cleanup 也要 clearInterval,避免每次 render 多開一個計時器
  4. 生產環境改指數退避:沒結果就把間隔加倍並設上限
今天就做 在你的專案找一個要使用者手動重整才看得到結果的狀態(任務、付款、build),用 useEffect + setInterval 加 3 秒輪詢與 cleanup
第 6 段 → Polling:最土但最簡單的即時 · polling polling interval
替一個實體設計 REST 端點:複數名詞、CRUD 對 POST/GET/PUT/DELETE、關聯巢狀、篩選走 query
  1. 挑一個領域實體(Product),列出它的複合子型別(Price、Rating)
  2. 集合 /products、單筆 /products/:id、關聯 /products/:id/prices
  3. 建立 POST、讀 GET、整筆更新 PUT、部分 PATCH、刪 DELETE
  4. 篩選排序分頁用 ?category=&sort=&page=
  5. 檢查:不看文件能不能猜出每個端點做什麼
今天就做 拿你專案裡一個現有的 API,把它的端點重寫成 REST 風格的五行清單,標出哪些現在不 RESTful
第 10 段 → REST:把 HTTP 標準化

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

HTTP response 像一封信:狀態行、headers(資料的資料)、空行、然後才是內容
新 HTTP response 的結構:status line + headers + 空行 + body  ⇄  像 一封信:信封上的收件資訊、信件內容
第 1 段 → HTTP:一封信的來回
IP 位址像不動產:早期被企業政府整段買走,剩下的越來越貴,所以才有 IPv6
新 IPv4 位址枯竭與 IPv6 遷移  ⇄  像 土地有限、早期被大戶圈走,後來者買不起
第 3 段 → IP:機器之間如何找到彼此
WebSocket 像打電話、HTTP 像寄信:開一條通道後不必每則訊息重新協商
新 WebSocket 全雙工長連線  ⇄  像 HTTP 一問一答(每則訊息重新寄一封信)
第 7 段 → WebSocket:改成打電話
proxy 代表客戶端(VPN,伺服器以為請求來自代理);reverse proxy 代表伺服器(LB、Gateway,客戶端以為它就是伺服器)
新 reverse proxy:客戶端只看到它,看不到後面真正的服務  ⇄  像 VPN 這種 proxy:替你出去,伺服器看到的是代理的 IP
第 14 段 → API Gateway:重複的事只做一次

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

TCP:在不可靠的 IP 上用序號、ACK、重傳做到「不掉包、照順序」,代價是延遲與 header
TCP
第 1 段 → HTTP:一封信的來回 · TCP IP
DNS:把網域名稱換成 IP 的分散式查詢系統,每次 fetch 之前都先跑一次(命中快取就不出去)
DNS
第 2 段 → DNS:先問到 IP 才能發請求 · DNS domain name IANA
HTTP caching:TTL 內直接用本地副本;過期後帶 ETag 問伺服器,沒變就重設計時器,變了才下載
HTTP caching
第 4 段 → HTTP 快取與 E-Tag · HTTP caching TTL max-age ETag
TLS:先用非對稱加密交換一把 secret key(憑證證明公鑰屬於這個網域),之後改走快的對稱加密
TLS
第 5 段 → TLS:為什麼要換兩種加密 · TLS HTTPS asymmetric encryption symmetric encryption certificate authority
WebSocket:單一 TCP 連線上的全雙工通道,兩邊隨時能送;從 HTTP Upgrade 請求開始所以沿用 443 與 TLS
WebSocket
第 7 段 → WebSocket:改成打電話 · WebSocket
Server-Sent Events:訂閱一次後伺服器沿同一條 HTTP 連線單向推文字事件,自動帶 Last-Event-ID 重連
Server-Sent Events
第 8 段 → SSE:AI 應用的串流來源 · Server-Sent Events EventSource
REST:把實體當資源,URI 用複數名詞、操作用 HTTP 動詞、篩選分頁用 query、關聯用巢狀路徑
REST
第 10 段 → REST:把 HTTP 標準化 · REST URI CRUD query parameter
GraphQL:前端一次查詢跨多個資源並明確列出欄位,同時解掉 under/over-fetching;schema 自帶型別
GraphQL
第 12 段 → GraphQL:前端自己決定要什麼 · GraphQL schema under-fetching over-fetching
API Gateway:坐在所有微服務前的 reverse proxy,把認證、限流、快取、SSL 終結這些 edge function 集中做一次
API Gateway
第 14 段 → API Gateway:重複的事只做一次 · API Gateway reverse proxy edge function microservice VPC
Backend For Frontend:為特定前端而建、由前端團隊自己擁有的聚合層,只做聚合與裁剪,不塞商業規則
Backend For Frontend
第 15 段 → BFF:前端擁有自己的後端 · Backend For Frontend

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

DevTools 四線索:200 (from memory cache)、age、max-age、etag——age 比 max-age 就知道過期沒
age 是「這份在快取裡待了多久」(由 CDN 回報);672803 秒 vs max-age 31536000(一年)還早;immutable 表示這個 URL 內容永遠不變,連驗證請求都省——這就是 build 工具在檔名塞內容雜湊的理由
證明 HTTP caching
演練 看到 age 與 max-age 你怎麼算還剩多久?immutable 成立的前提是什麼?你的 build 產物有沒有滿足?
第 4 段 → HTTP 快取與 E-Tag
作者說交握「至少五個來回」是 TLS 1.2 以前的粗估;TLS 1.3 完整交握 1-RTT、重連 0-RTT
TLS 1.2 含 TCP 三次交握約 5 RTT;TLS 1.3 壓到 1-RTT,session resumption 可 0-RTT,所以今天 HTTPS 額外延遲遠比影片小
證明 TLS
演練 這個數字證明 HTTPS 的「慢」是版本問題還是本質問題?你的伺服器用的是 TLS 1.2 還是 1.3?
第 5 段 → TLS:為什麼要換兩種加密
ChatGPT 的 conversation 端點是 EventStream,一排 delta 事件就是畫面逐字浮現的字
LLM 一次生成一個 token 正好是單向串流,所以現代 AI 應用幾乎都用 SSE;代價:只能傳文字、單向、HTTP/1.1 下受同網域連線數上限拖累
證明 Server-Sent Events
演練 這證明了為什麼 AI 應用選 SSE 而不是 WebSocket?如果你要做一個串流回覆的聊天介面會怎麼選?
第 8 段 → SSE:AI 應用的串流來源
同一組端點:桌面一個視圖要打十幾個端點(under-fetch),手機拿回一堆不顯示的 JSON(over-fetch)
兩個問題的共同根源是欄位由誰決定(控制權在後端);REST 的常見折衷是替每個畫面另開端點 /products/mobile-list,端點爆炸且不再 RESTful
證明 GraphQL
演練 這兩張圖證明問題是效能還是控制權?你的專案有沒有「為某個畫面特製」的端點?
第 11 段 → Under-fetch 與 Over-fetch
N+1:24 個產品的 GraphQL 查詢展開成 25 次 SQL,比原本的 REST 還慢;DataLoader 批次成一次 WHERE id IN
resolver 先查清單(1)再每筆查細節(N);DataLoader 利用事件迴圈一個 tick 收集 .load(id),tick 結束合併成 SELECT ... WHERE id IN (...),同請求內去重快取。ORM 的 lazy loading 是一模一樣的問題
證明 GraphQL
演練 這證明了 GraphQL 什麼時候會白換?凡是要聚合多筆查詢,你會加哪一層?
第 13 段 → N+1 問題與 DataLoader
作者待過 300 個微服務的公司:出一個功能要同時協調平台、產品、物流三個團隊的 backlog
每個微服務屬不同團隊、各有 PM 與 backlog;BFF 讓前端團隊自己擴充;手機可另有 Mobile BFF。代價:每多一個 BFF 就多一個要部署、監控、值班的服務,由原本只寫前端的團隊承擔
證明 Backend For Frontend
演練 這證明 BFF 解的是技術問題還是組織問題?你的團隊出一個功能要等幾個其他團隊?
第 15 段 → BFF:前端擁有自己的後端

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

TCP 不是「不掉包」,是掉了會補:封包還是會掉,靠序號與 ACK 重傳;傳資料前還要三次交握
TCP「保證不掉包」精確的說法是什麼?開始傳資料前要先做什麼?
封包會掉但 TCP 用序號/ACK 重傳補回,應用層看起來像沒掉;先三次交握
第 1 段 → HTTP:一封信的來回 · TCP status code
DNS 查詢有多層快取:瀏覽器→OS→遞迴解析器→根/TLD/權威;每筆記錄自有 TTL;傳統是明文(所以有 DoH)
瀏覽器做 DNS lookup 實際會經過哪幾層?為什麼有 DoH?
瀏覽器快取→OS 快取→遞迴解析器→根/TLD/權威伺服器;DNS 傳統明文,DoH 用 HTTPS 包起來
第 2 段 → DNS:先問到 IP 才能發請求
HTTPS 用 443 埠(作者口誤 440);17.x.x.x 整段是 Apple;NAT 讓 IPv4 枯竭被拖延二十幾年
HTTPS 預設埠?為什麼 IPv4 早就枯竭你卻沒感覺?
443;家裡和公司所有裝置透過 NAT 共用一個對外 IPv4,對內用私有位址
第 3 段 → IP:機器之間如何找到彼此 · port IPv4 IPv6
憑證的作用不是加密資料,是「證明這把公鑰屬於這個網域」;真正保護流量的是對稱金鑰
HTTPS 給你的兩件事各來自哪裡?自簽憑證少了哪一件?
機密性來自加密(對稱金鑰)、身分可信來自憑證鏈;自簽憑證有加密但沒人背書
第 5 段 → TLS:為什麼要換兩種加密 · certificate authority
三種即時的梯度:polling 最簡單最浪費、SSE 單向且自動重連、WebSocket 雙向但要自己管狀態
polling / SSE / WebSocket 各適合什麼?
polling:偶爾變的狀態;SSE:伺服器單向推(AI 串流、通知);WebSocket:雙向即時(聊天、協作)
第 8 段 → SSE:AI 應用的串流來源
tRPC 是 TS 專用型別推導函式庫、不規範線上格式;JSON-RPC 是語言無關協定,MCP 用的是後者
tRPC 跟 JSON-RPC 差在哪?MCP 底層用哪個?RPC 為什麼容易耦合?
tRPC 靠 TS 型別做端到端型別安全、無線上格式;JSON-RPC 是語言無關協定,MCP 用它;耦合來自「函式名即 API」的設計不是傳輸方式
第 9 段 → RPC / tRPC 與 MCP · RPC tRPC MCP JSON-RPC
idempotency:PUT 送一次和十次最終狀態一樣;PATCH「數量加一」重送會多加——客戶端自動重試時是正確性問題
為什麼 PUT 比 PATCH 好推理?字幕裡把 idempotent 聽成什麼?
PUT 冪等,網路不穩重送也安全;相對操作的 PATCH 重送會疊加;字幕誤成 important
第 10 段 → REST:把 HTTP 標準化 · idempotency
GraphQL 的代價:對同一個 /graphql 發 POST 所以不能以 URL 當快取鍵;伺服器要限制查詢深度與複雜度
把查詢形狀交給前端,伺服器端多了哪兩個問題?
HTTP 快取失效(都是 POST /graphql);要加查詢深度/複雜度限制防惡意巢狀查詢
第 12 段 → GraphQL:前端自己決定要什麼
DataLoader 以請求為單位:每個請求 new 一個,跨請求共用會讓使用者看到別人的舊資料
DataLoader 最常踩的雷是什麼?
跨請求共用同一個 instance——它有請求內快取,共用會洩漏別人的資料;每個請求 new 一個
第 13 段 → N+1 問題與 DataLoader · DataLoader batching N+1 problem
「Gateway 之後內網走 HTTP」的前提是信任 VPC 內所有東西;零信任架構要求服務間仍用 mTLS
作者說 Gateway 後面走 HTTP 就好,這個結論有什麼前提?主流的反方是什麼?
前提是信任整個 VPC;zero trust 主張服務間 mTLS,因為一台被攻陷平坦內網就無險可守
第 14 段 → API Gateway:重複的事只做一次 · VPC
負載平衡的前提是實例無狀態:session 放記憶體會被分到另一台就登出;LB 自己也是單點,要託管+健康檢查
round robin 和 least connections 差在哪?水平擴展前要先處理什麼?
round robin 依序輪流;least connections 送給連線數最少的。先讓實例無狀態(session 外置到 Redis 或 sticky session),LB 交給託管服務跨可用區
第 16 段 → 負載平衡:消除單點故障 · load balancing round robin least connections single point of failure

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

水平擴展的前提是 stateless:先把 session 外移到 Redis/DB,再加機器;本機用 Nginx + Docker Compose 兜一個
  1. 確認 server 沒把跨請求狀態存在記憶體(session、暫存);有就外移到 Redis 或資料庫
  2. 做不到就退用 sticky session,但接受分流失衡與該機不能隨便重啟
  3. docker compose 起 2–3 個相同的 app instance
  4. 前面擺 Nginx 做 upstream 分流,反覆打請求確認落在不同 instance
今天就做 在你的 Node/Python 專案旁寫一個 docker-compose.yml:兩個 app 服務+一個 Nginx upstream,用 curl 打 10 次看回應的 hostname 有沒有輪流
第 8 段 → Load balancing:單台 server 的天花板 · stateless
cache busting 標準組合:檔名塞內容雜湊、index.html 不快取、其餘設一年不變
  1. bundler 輸出檔名帶內容雜湊(app.a3f9c2.js)——內容一變檔名就變,舊檔永遠不必失效
  2. index.html 設 Cache-Control: no-cache(或極短)
  3. 其餘帶雜湊的資產設 Cache-Control: max-age=31536000, immutable
  4. 記住 CDN 是 pull 模式:每個地區第一位使用者仍付完整回源延遲
今天就做 打開你專案正式站的 DevTools Network,看 index.html 與任一 JS chunk 的 Cache-Control 標頭,對照這三條寫下哪一條不符
第 10 段 → CDN:跟光速借時間 · cache busting cache invalidation
code splitting 三刀:先切 route,再切重量級依賴(動態 import),最後 vendor 分離;搭配 prefetch 別切太碎
  1. 第一刀:按 route 切(React.lazy / 路由層動態 import),/login 不載 dashboard 的圖表
  2. 第二刀:圖表庫、富文字編輯器、日期選擇器等幾百 KB 只在互動後用的東西,改動態 import
  3. 第三刀:vendor chunk 分離,讓第三方套件的快取跨發版仍有效
  4. 檢查反效果:切太碎會製造瀑布式請求;點了才載會把延遲搬到 INP
  5. 加 prefetch:閒置或 hover 連結時先偷偷載好
今天就做 跑一次你專案的 bundle 分析(vite-bundle-visualizer 或 webpack-bundle-analyzer),找出最大的一個非首屏依賴,改成動態 import 並重新量 chunk 大小
第 16 段 → Code splitting 與 lazy loading · code splitting lazy loading eager loading module bundler
即時通訊選型:只有伺服器要說話用 SSE,雙方都頻繁說話用 WebSocket,低頻狀態查詢用 polling
  1. 先問通訊的形狀:單向還是雙向?頻率多高?
  2. AI 應用是不對稱的(送一個 query、吐幾千 token)→ SSE
  3. 聊天室兩邊都在送 → WebSocket(overhead 大、吃伺服器)
  4. 簡單低頻狀態 → polling(setTimeout 即可,注意 race condition 與 scale)
  5. SSE 要帶 Authorization 時原生 EventSource 不行(只支援 GET),改 fetch 手動讀 stream
今天就做 打開 ChatGPT 或 Claude 的 DevTools Network,找 conversation 請求,確認 Content-Type 是 text/event-stream,再看你自己專案裡有沒有一個 polling 其實該換成 SSE
第 18 段 → 即時通訊:polling、WebSocket、SSE · polling WebSocket server-sent events event stream

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

AI 取代的是局部、可驗證的實作,取代不了跨模組的取捨——前端的價值搬到「決定系統長什麼樣」
新 前端工程師該補的是 system design:按鈕該屬於哪個微前端、狀態放哪、誰能改  ⇄  像 寫一個按鈕元件有標準答案,可以被自動化;沒有標準答案的決策不能
第 1 段 → 開場:為什麼前端要學系統設計
架構決定 diff 的可讀性,diff 的可讀性決定你敢不敢用 AI 產出的東西
新 同一段 code 在 20 萬行單體要跑全套回歸,在 3,000 行微前端讀 diff 就知道影響範圍  ⇄  像 code review:PR 越小越敢 approve
第 3 段 → AI 的真正瓶頸:verification time
「AI 補上你缺的那一半,所以一個人吃整條切片」在 CRUD 成立,在需要深度的地方失效
新 vertically integrated engineer:配合 agent 獨立吃下整條垂直切片  ⇄  像 full-stack 工程師的老問題:廣度換深度
第 5 段 → 垂直切片、職涯兩極與 blast radius
有 --color-primary 和既有 <Button>,agent 面對的是選擇題不是申論題——約束越強,驗證越快
新 design system 讓 coding agent 不會「發明」品牌色,錯了也一眼看得出  ⇄  像 考試的選擇題錯誤率遠低於申論題,而且改起來快
第 11 段 → Design system:對抗視覺分歧
SSR 是「為了買菜造一台 F1 賽車」;真正的複雜度不在渲染而在你多了一台要維運的伺服器
新 SSR 解白畫面但引入 hydration 空窗(看得見按不動,傷 INP),且要維運 Node:快取、記憶體洩漏、冷啟動、瀏覽器有 Node 沒有的 API  ⇄  像 買菜開 F1:性能過剩,維護成本卻是真的
第 17 段 → 渲染策略:CSR、SSG、ISR、SSR

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

verification time:coding agent 把實作時間壓到近零,推高驗證時間;AI 時代架構目標=讓系統容易被驗證
verification time
第 3 段 → AI 的真正瓶頸:verification time · verification time agentic coding
microfrontends:前端單體拆成獨立部署的應用,執行時由 shell 組合;判準是「有幾個團隊在互相卡住」
microfrontends
第 4 段 → 前端還是單體:微前端與 shell · microfrontends micro frontend shell independent deployability
vertical slice:一個微前端+同領域微服務=一個團隊獨立擁有、獨立發布的單位;全片所有拆分都為了它
vertical slice
第 5 段 → 垂直切片、職涯兩極與 blast radius · vertical slice vertically integrated engineer
API gateway:把每個服務都要重做的 edge functions(快取、TLS、認證、限流)抽到單一入口,只實作一次
API gateway
第 6 段 → API Gateway:把 edge functions 收成一次 · API gateway edge functions rate limiting
BFF:前端團隊自己擁有的後端,負責聚合與裁切;判準是擁有權不是技術——由前端部署、前端 on-call
backend for frontend
第 7 段 → BFF:前端團隊自己擁有後端 · backend for frontend over-fetching under-fetching
CDN:光速是不可談判的限制(跨洋一來回 200–250 ms),唯一解法是把檔案搬近;本質是分散式快取
CDN
第 10 段 → CDN:跟光速借時間 · CDN edge location cache hit
design system:對抗垂直切片解耦後的 visual divergence;token 先、元件後;也是 agent 產出穩定的分水嶺
design system
第 11 段 → Design system:對抗視覺分歧 · design system visual divergence design tokens CSS custom properties
monorepo:所有切片收進單一 repo 共用工具鏈;對 agent 的價值是能沿 import 追蹤真實依賴,跨服務邊界重構
monorepo
第 13 段 → Monorepo:給 agent 看得見邊界 · monorepo polyrepo code style drift
Core Web Vitals 把「好慢」拆成三個可歸因的數字:LCP(載入)、INP(互動)、CLS(穩定)
Core Web Vitals
第 15 段 → Core Web Vitals 與關鍵渲染路徑 · Core Web Vitals LCP INP CLS critical rendering path
渲染策略的判準是「這一頁的內容在什麼時候才能確定」:build 時→SSG、會過期→ISR、每次不同→SSR/CSR
server-side rendering
第 17 段 → 渲染策略:CSR、SSG、ISR、SSR · client-side rendering static site generation incremental static generation server-side rendering hydration

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

40 人同一個單體 → 每天 1.5 小時 stand-up;溝通路徑 n(n-1)/2,40 人=780 條
作者:40 人共同開發一個 monolith,stand-up 開一個半小時;two pizzas team(5–9 人)是上限,AI 讓它變 one pizza。AI 補充:溝通路徑 n(n-1)/2,40 人=780 條(Brooks 定律)
證明 microfrontends
演練 這個數字證明了拆分的驅動力是人不是技術?你的團隊現在幾條溝通路徑,stand-up 多長?
第 2 段 → 從單體到微服務:團隊撐不住了
gateway 對外 HTTPS、對內 HTTP 省 handshake——2015 年主流,zero trust 時代已不推薦
作者:後端全躲進 VPC、只有 gateway 一扇門,內部走 HTTP 省 TLS handshake。AI 補充:某服務被入侵後內網裸奔,現代用 mTLS 或 service mesh 讓基礎設施吸收 TLS 成本
證明 API gateway
演練 這個例子證明了 gateway 的「單一入口」同時是安全邊界與單點故障?面試被問「內網要不要加密」你會怎麼回答?
第 6 段 → API Gateway:把 edge functions 收成一次
痛點:每個小欄位都要去別的團隊 backlog 排隊——大公司工程師最常見的卡點
作者輔導的大公司工程師最常卡在:前端要一個欄位得等後端團隊排程;BFF 把決定權搬到前端團隊。若 BFF 最後仍由後端維護,只是多了一層網路跳躍
證明 backend for frontend
演練 這證明了 BFF 解的是排程問題不是資料問題?你上次等別的團隊加欄位等了多久?
第 7 段 → BFF:前端團隊自己擁有後端

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

微服務不是讓系統更快更省(多半相反),而是讓團隊能平行前進
作者主張微服務的好處是什麼?不是什麼?
是:團隊能平行前進;不是:系統更快或更省資源(通常多付網路延遲與運維成本)
第 2 段 → 從單體到微服務:團隊撐不住了 · microservices monolith
Conway's law 原本是描述性的;作者用的是反向:先切團隊,架構自己長成那樣(inverse Conway maneuver)
作者說「想要小團隊就得切系統」用的是 Conway's law 的哪個方向?
反向(inverse Conway maneuver):先照想要的架構切團隊,架構就會長成那個樣子
第 3 段 → AI 的真正瓶頸:verification time · Conway's law
shell 管 auth、routing、語言、global state——不能被拆掉的中心,也是單點故障,所以做得極薄
微前端的 shell 負責什麼?為什麼要做得極薄?
auth、routing、語言、global state 等跨切片功能;它掛了整站就掛,所以越薄越好
第 4 段 → 前端還是單體:微前端與 shell · micro frontend shell global state
微前端三個現實代價:bundle 重複、global state 形狀變動=無型別分散式 API、樣式洩漏
作者沒講的微前端三個代價?
bundle 重複(各帶一份 React,靠 Module Federation 共享);版本偏移(shell 的 global state 一改全部要跟);樣式洩漏(要做 CSS 隔離)
第 4 段 → 前端還是單體:微前端與 shell
blast radius:一次變更實際能波及的範圍;小切片=小 PR=少 context=AI 更準、驗證更短
blast radius 是什麼?它給「小 PR」什麼實質理由?
一次變更或故障實際能波及的範圍;小切片=小 PR=少 context,出事時壞的少、找得快,AI 更準、驗證更短
第 5 段 → 垂直切片、職涯兩極與 blast radius · blast radius cognitive load
GraphQL 當 BFF 的兩個坑:快取變難(不再是 URL 快取)、查詢複雜度攻擊要防
用 GraphQL 做 BFF 作者沒提的兩個代價?
快取不再是單純 URL 快取;要防查詢複雜度攻擊
第 7 段 → BFF:前端團隊自己擁有後端 · GraphQL
調校過的 Node.js 約 2,000–10,000 並發(數量級);超過就水平擴展+load balancer
單台 Node.js server 大約撐多少並發?超過怎麼辦?
2,000–10,000(I/O 密集看連線數、CPU 密集看核心數,只是數量級);開多個相同 instance 前面擺 application load balancer
第 8 段 → Load balancing:單台 server 的天花板 · load balancing horizontal scaling concurrent requests
Docker image 的「作業系統」只是使用者空間,kernel 共用宿主機——所以幾百毫秒就啟動
Docker image 裡有作業系統嗎?這解釋了什麼?
只有使用者空間檔案與函式庫,kernel 共用宿主機;所以啟動不必開機(幾百 ms),Linux 容器在 Windows 要靠 VM
第 9 段 → 容器化:Docker 與 orchestration · Docker Docker image
前端工程師不必會 Kubernetes,但要能說出它在架構裡的位置:接手多實例、失敗重啟、負載平衡
container orchestration 在架構裡做什麼?前端要會到哪?
把一堆 image 的多實例、健康檢查、失敗重啟、負載平衡自動化;前端只要能說出它的位置,不必會操作
第 9 段 → 容器化:Docker 與 orchestration · container orchestration Kubernetes
MCP 是工具接進 LLM 的統一介面;Figma MCP server 讓設計稿變成可查詢的結構化資料
MCP 為什麼重要?對前端的實際意義是什麼?
把「有哪些工具、吃什麼參數」標準化,同一個 server 任何 agent 都能用;設計稿不再是要人眼翻譯的圖,agent 可以問「這元件用了哪個 token」
第 12 段 → Design-to-code MCP 與 Atomic CSS · MCP MCP server
Atomic CSS(Tailwind 是一種實作):CSS 大小不隨專案成長、刪元件不留孤兒樣式;代價是 HTML 塞滿類別
Atomic CSS 的收益與代價?跟 design system 的關係?
收益:CSS 檔不隨專案長、無孤兒樣式;代價:HTML 可讀性下降。不衝突:token 定義值、atomic class 提供用法、元件封裝組合
第 12 段 → Design-to-code MCP 與 Atomic CSS · Atomic CSS Tailwind CSS
repo 邊界≠部署邊界:Nx/Turborepo 靠依賴圖只重建受影響的套件;成本在規模與擁有權(CODEOWNERS)
monorepo 跟獨立部署衝突嗎?真正的成本在哪?
不衝突,Nx/Turborepo 靠依賴圖只部署受影響的;成本是 repo 大到 git/CI 變慢,以及所有人能改所有東西、要用 CODEOWNERS 畫回邊界
第 13 段 → Monorepo:給 agent 看得見邊界
MCP UI:LLM 回文字外附「渲染某元件」指令,前端解析後渲染;實作必須是宣告式白名單(元件名+驗證過的 props),絕不回 HTML
MCP UI 怎麼運作?安全邊界是什麼?
harness 提供 tool registry,LLM 回「元件名+參數」由前端渲染;前端事先註冊元件與 props schema 當白名單,不執行任意標記,第三方渲染用 iframe/sandbox
第 14 段 → MCP UI:讓 LLM 回你元件而不只是字 · MCP UI model harness tool registry
三指標各自主因:LCP=TTFB/阻塞資源/主視覺圖/等資料;CLS=沒尺寸的圖與延遲字體;INP=主執行緒長工作
LCP、CLS、INP 各自最常見的元凶?
LCP:慢 TTFB、同步 CSS/JS、沒優化的主視覺圖、CSR 等資料;CLS:img 沒 width/height、字體延遲;INP:主執行緒被長工作佔住(React 整棵子樹重繪)
第 15 段 → Core Web Vitals 與關鍵渲染路徑
實驗室數據(Lighthouse)與真實使用者數據(field / CrUX)常對不上,Google 排名看後者
Web Vitals 有哪兩種資料來源?優化該盯哪個?
lab(Lighthouse 在你機器模擬)與 field(真實使用者裝置與網路);排名用 field,盯錯來源是常見浪費
第 15 段 → Core Web Vitals 與關鍵渲染路徑
SSE 跑在普通 HTTP 上:既有基礎設施照用、內建重連;限制是 HTTP/1.1 每網域 6 條連線
SSE 相對 WebSocket 的好處與限制?
好處:既有 HTTP 基礎設施全用、自動重連與續傳內建;限制:HTTP/1.1 每網域 6 條並行連線(多分頁互卡,HTTP/2 沒此問題)、原生只支援 GET
第 18 段 → 即時通訊:polling、WebSocket、SSE · server-sent events

Keep Those WebSocket Connections Alive!

Covalence 網路與 API 基礎 產生於 2026-09-21 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

先搬家不加功能:socket 邏輯抽成 sockets/index.ts 的 configure(server),主檔傳入 app.listen()
  1. 新建 sockets/index.ts,export default function configure(server: Server)
  2. 把主檔 app.listen 之後的 WebSocket 邏輯(含 'upgrade' 的 handleUpgrade)整段剪過去
  3. 主檔改成 configureSockets(app.listen(...)),把 http Server 物件整個傳進去
  4. 跑一次確認行為沒變,再開始加 heartbeat
今天就做 在你手上的專案挑一段擠在入口檔裡的 socket 或 route 設定,抽成一個 configure(server) 函式放到獨立檔案,跑起來確認行為不變
第 2 段 → 重構:socket 邏輯搬到 sockets/index.ts · http.Server WebSocketServer
給每條連線一個狀態位:connection 第一行 ws.isAlive = true,用 module augmentation 讓 TS 接受
  1. wss.on('connection', ws => { ws.isAlive = true; ... }) 放在 on('error') / on('message') 之前
  2. 新建 typings/ws.d.ts,第一行 import WebSocket from 'ws'(少了這行會整個蓋掉套件型別)
  3. declare module 'ws' { interface WebSocket { isAlive: boolean } }
  4. 檔名用被擴充的套件名命名,之後找得到
今天就做 在你專案裡挑一個第三方套件的物件,用 typings/<套件名>.d.ts 的 module augmentation 幫它加一個自訂屬性,確認 tsc 不再抱怨
第 4 段 → 標記 isAlive 並擴充 ws 型別 · module augmentation declaration file
server 端 interval:遍歷 wss.clients,isAlive false 就 terminate,否則先設 false 再 ping
  1. 在 configure 內、wss.on('connection') 之後宣告 const interval = setInterval(fn, HEARTBEAT_INTERVAL)
  2. fn 裡 wss.clients.forEach(client => ...),不要用 readyState 篩
  3. if (!client.isAlive) { client.terminate(); return; }
  4. client.isAlive = false; ping(client) —— 這兩行是一組動作
  5. 等 message 處理器收到 pong 把 isAlive 設回 true
今天就做 在你任一個 Node 長連線服務(或本片的範本)加上這段 interval,把間隔設 3 秒,用 console.log 看 firing interval 的節奏
第 5 段 → server 端 interval:不活就 terminate · terminate setInterval
client heartbeat():每次收到 ping 就重設 pingTimeout = setTimeout(close, 間隔 + 緩衝)
  1. const HEARTBEAT_TIMEOUT = 1000 * (5 + 1),把 server 間隔與 1 秒緩衝寫清楚
  2. function heartbeat() 無參數(client 同時只有一個 ws)
  3. ws.pingTimeout = setTimeout(() => { ws.close(); /* 這裡接重連或跳 modal */ }, HEARTBEAT_TIMEOUT)
  4. 改 server 的 HEARTBEAT_INTERVAL 時記得同步改這裡的 5,兩邊沒有共用來源
今天就做 在你前端任一個 WebSocket 連線(或範本的 public/js/app.ts)加上 heartbeat() 骨架,把 timeout 設 6 秒並在回呼裡 console.log('server silent')
第 7 段 → client 端 heartbeat:timeout 加緩衝 · setTimeout buffer (timing)
驗證指標:每次 firing interval 後的 pong 數 = 活著的連線數;開三個分頁看 1、2、3,關掉看遞減
  1. npm run dev,interval 開頭 console.log('firing interval'),收到 pong 時 log 'pong'
  2. 開 1 個分頁看 1 個 pong,再開到 3 個看 3 個;互送訊息確認廣播正常
  3. 逐一關分頁看 pong 遞減到 0
  4. 補 wss.on('close', () => clearInterval(interval)),否則 server 關了 interval 還在跑
  5. 真正的死連線要另外驗:DevTools 把網路切 offline,等兩個 interval 看 server 是否 terminate
今天就做 把你的 heartbeat 跑起來開 3 個分頁看 pong 數;再把其中一個分頁的 DevTools 切成 offline,等兩個 interval 確認 server 有踢掉它
第 12 段 → 實測多連線與 server close 清理 · clearInterval

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

狀態掛在 socket 物件上,而不是另開一個 Map 記錄
新 ws.isAlive 直接掛在每條連線物件上,wss.clients 遍歷時就地讀寫  ⇄  像 另開一個 Map<socket, state> 或字典來記每條連線的狀態
第 4 段 → 標記 isAlive 並擴充 ws 型別
server 是主動方用 setInterval 固定節奏出擊;client 是被動方用可重設的 setTimeout 當看門狗
新 client 每收到一次 ping 就把倒數歸零重來,真的響了代表 server 太久沒來  ⇄  像 watchdog timer:系統要定期「餵狗」,沒餵就重啟;或手機不碰螢幕就自動鎖定
第 7 段 → client 端 heartbeat:timeout 加緩衝

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

dead connection:底層已斷、應用層仍以為連著的 WebSocket,訊息一直送卻永遠沒回音
dead connection
第 1 段 → 死連線問題與 ping/pong 解法 · dead connection WebSocket
ping/pong:一方定期送小訊息,另一方收到就回;一段時間沒回就當對方死掉
ping/pong
第 1 段 → 死連線問題與 ping/pong 解法 · ping/pong
heartbeat:server 用 setInterval 每隔 HEARTBEAT_INTERVAL 對每條連線 ping,期待收到 pong 的整套機制
heartbeat
第 3 段 → 設計 heartbeat:間隔、值、ping 函式 · heartbeat setInterval
binary frame:用訊框型別區分 ping 與一般文字訊息,client 不必解析內容就能分流
binary frame
第 3 段 → 設計 heartbeat:間隔、值、ping 函式 · binary frame
module augmentation:.d.ts 先 import 模組,再 declare module 同名並宣告同名 interface 來合併屬性
module augmentation
第 4 段 → 標記 isAlive 並擴充 ws 型別 · module augmentation declaration file
buffer (timing):client timeout 要比 server 間隔多留一段,吸收網路抖動,避免正常的 ping 晚幾十毫秒就被誤判
buffer (timing)
第 7 段 → client 端 heartbeat:timeout 加緩衝 · buffer (timing)
擴充瀏覽器內建 WebSocket 型別:WebSocketExt extends WebSocket,建立時 as WebSocketExt
type assertion
第 8 段 → clearTimeout 與 WebSocketExt 型別 · type assertion interface extends

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

客服工單案例:一方掉線、雙方都沒察覺,客戶只能猜要不要重整,結果永遠失去一個客戶
客戶或客服其中一方斷線、雙方都不知道,訊息持續送出但沒回音;客戶自己猜要不要重新整理、整個流程重來
證明 dead connection
演練 這個案例證明了死連線的代價落在哪一端(server 資源還是使用者體驗)?你的專案裡哪個長連線功能掉線後使用者會完全沒感覺?
第 1 段 → 死連線問題與 ping/pong 解法
為什麼掉線了卻不知道:對方沒送 FIN/RST(切網路、闔筆電、NAT 清表)時,TCP 一直停在已連線
AI 補充:WebSocket 建在 TCP 上;本端 socket 停留在「已連線」直到真的送資料且重試逾時(可能好幾分鐘)才報錯。應用層只被動等訊息就永遠不會知道
證明 dead connection
演練 這解釋了為什麼「不送資料就不會發現」——你會怎麼用它說服同事 heartbeat 不是多餘開銷?
第 1 段 → 死連線問題與 ping/pong 解法
小坑:ws.send(1, { binary: true }) 會先轉字串,實際送出 0x31('1')不是 0x01
AI 補充:本片 client 只檢查 isBinary 不比對值所以還能動;若 client 也要比對值,server 得改成 ws.send(Buffer.from([HEARTBEAT_VALUE]), { binary: true })
證明 binary frame
演練 這個坑證明了「binary 選項」跟「內容真的是位元組」是兩回事——你的 ping 若之後要帶版本號或時間戳,你會怎麼包?
第 3 段 → 設計 heartbeat:間隔、值、ping 函式
判定一條連線死掉需要一整個 interval,斷在剛 ping 完之後最多延遲兩個 interval 才被踢
這一輪放下旗子(isAlive=false),下一輪來檢查旗子有沒有被 pong 舉回來;所以不是收到 pong 的瞬間判定,而是下一輪
證明 heartbeat
演練 「最多兩個 interval」證明了間隔秒數與「多久發現斷線」的關係——如果產品要求 30 秒內發現,你會把 interval 設多少?
第 5 段 → server 端 interval:不活就 terminate
單執行緒 Node 裡 interval 與 message 回呼不會同時跑,沒有並發;但 RTT 超過一個 interval 會誤判活連線
AI 補充:作者說不會有 race condition 是對的(無並發衝突);時序邊界是 pong 來回時間 > interval 時,活著的連線也會被 terminate,所以間隔不能小於預期最差 RTT
證明 heartbeat
演練 這證明了 interval 的下限由什麼決定?你的使用者若在行動網路上 RTT 可能 2 秒,5 秒間隔安全嗎?
第 6 段 → 收 pong:binary 且值相符才算活著
邊界:heartbeat() 只在第一個 ping 之後才被呼叫,連線建立到第一個 ping 之間 client 沒有看門狗
AI 補充:server 若在這段時間(最多一個 interval)就死了,client 永遠不會逾時。比較完整的做法是在 'open' 事件裡也呼叫一次 heartbeat()
證明 heartbeat
演練 這個邊界證明了看門狗該從哪一刻開始計時?你會在 onopen 加什麼?
第 11 段 → 接上 onclose / onmessage · MessageEvent

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

本片的 ping/pong 是應用層自己定義的一問一答,不是 WebSocket 協定內建的 ping/pong 控制訊框
本片的 ping/pong 跟 RFC 6455 內建的 ping/pong 控制訊框一樣嗎?
不一樣。本片是應用層用 message 自訂的;協定層的 ws.ping() 走控制訊框,瀏覽器端沒有 API 能發
第 1 段 → 死連線問題與 ping/pong 解法
WebSocketServer 用 noServer: true 建立,要自己接管 HTTP 'upgrade' 事件呼叫 handleUpgrade
為什麼 configure 需要拿到整個 http Server 而不只是 app?
wss 用 noServer: true 建立,要在 server.on('upgrade') 裡自己呼叫 wss.handleUpgrade,所以需要能掛底層事件的 http.Server
第 2 段 → 重構:socket 邏輯搬到 sockets/index.ts · WebSocketServer http.Server
HEARTBEAT_INTERVAL 示範用 5 秒,正式建議 10–30 秒,依 client 可接受的最大延遲決定
heartbeat 間隔示範用幾秒?正式環境建議多少?
示範 1000 * 5;正式 10–30 秒,看 client 能接受多晚發現斷線
第 3 段 → 設計 heartbeat:間隔、值、ping 函式
死連線用 terminate() 不用 close():close 會等對方回 close 訊框,死連線永遠等不到
踢掉死連線為什麼用 terminate() 而不是 close()?
close() 走正常關閉握手、等對方回 close 訊框;terminate() 直接銷毀底層 socket,立刻從 wss.clients 移除
第 5 段 → server 端 interval:不活就 terminate · terminate
heartbeat 的 forEach 不管 readyState:目標正是「readyState 還是 OPEN 但其實已死」的連線
為什麼 heartbeat 遍歷 clients 時不用 readyState === OPEN 篩選?
死連線的 readyState 也還是 OPEN;用它篩反而漏掉目標
第 5 段 → server 端 interval:不活就 terminate · readyState
server 收 pong:isBinary 且 data[0] === HEARTBEAT_VALUE 才把 isAlive 設回 true
server 怎麼判斷一則 message 是 pong?
isBinary 為 true 且 data(Buffer)的第一個位元組 data[0] === HEARTBEAT_VALUE;否則走原本廣播
第 6 段 → 收 pong:binary 且值相符才算活著 · Buffer
heartbeat() 開頭要 clearTimeout 舊的再 set:不清的話第一個 timeout 6 秒後照樣響,連線會每 6 秒斷一次
client 每次收到 ping 不先 clearTimeout 舊計時器會怎樣?
舊的 timeout 沒取消,即使 ping 都正常到,6 秒後還是會響並 ws.close()——「clear 再 set」是正確性不是優化
第 8 段 → clearTimeout 與 WebSocketExt 型別 · clearTimeout
兩種擴型別的差別:augmentation 改全域套件型別;extends + as 只影響你明確斷言的那個變數
server 端與 client 端擴充 WebSocket 型別各用什麼方法?為什麼不同?
server:ws 套件型別 → module augmentation 合併;client:瀏覽器內建 WebSocket → interface extends + as 斷言,不動全域型別。兩者都只影響型別檢查
第 8 段 → clearTimeout 與 WebSocketExt 型別
pingTimeout 型別照抄 NodeJS.Timeout 能過,可攜寫法是 ReturnType<typeof setTimeout>
前端 setTimeout 的回傳型別要怎麼寫才可攜?
ReturnType<typeof setTimeout>;編輯器顯示 NodeJS.Timeout 只是因為 tsconfig 看得到 @types/node
第 8 段 → clearTimeout 與 WebSocketExt 型別
client 回 pong 用 Uint8Array(1)、data[0] = 1 再 ws.send(data);直接 send(1) 會變 text 訊框
瀏覽器端為什麼不能 ws.send(1) 當 pong?
WebSocket.send 只收 string / Blob / ArrayBuffer / ArrayBufferView,數字會轉成字串 '1' 以 text 送出,server 的 isBinary 是 false。Uint8Array 會自動以 binary 送
第 9 段 → 補上 pong:Uint8Array 送回 server · Uint8Array
isBinary(obj):Object.prototype.toString.call(obj) === '[object Blob]' 判斷是不是 Blob
client 怎麼判斷 message.data 是 binary?為什麼不用 instanceof Blob?
Object.prototype.toString.call(obj) === '[object Blob]';比 instanceof 通用:不受物件覆寫 toString 影響、跨 iframe/realm 也能用
第 10 段 → isBinary:用 toString 判斷 Blob · Blob Object.prototype.toString.call
前提:瀏覽器 binaryType 預設 'blob';設成 'arraybuffer' 就要改比對 ArrayBuffer
isBinary 比對 '[object Blob]' 依賴什麼預設?
ws.binaryType 預設 'blob';改成 'arraybuffer' 後收到的是 ArrayBuffer,比對字串要跟著改
第 10 段 → isBinary:用 toString 判斷 Blob
onclose 要 clearTimeout(ws.pingTimeout):連線已關,沒清的倒數 6 秒後會錯誤觸發重連邏輯
client 的 close 事件裡要做什麼?為什麼?
clearTimeout(ws.pingTimeout);否則使用者自己關或 server 踢掉後,舊倒數仍會響並錯誤觸發重連
第 11 段 → 接上 onclose / onmessage · clearTimeout
關分頁時瀏覽器會送 close 訊框,server 靠 'close' 事件移除 client,不是靠 heartbeat 踢掉
影片的三分頁實測驗證到「死連線被 heartbeat 踢掉」了嗎?
沒有。關分頁是正常關閉路徑(close 訊框);要驗死連線得讓 client 不回 pong,例如 DevTools offline 等兩個 interval
第 12 段 → 實測多連線與 server close 清理
參數關係:INTERVAL 決定多久發現死連線(最壞兩個間隔);client TIMEOUT 必須 > INTERVAL + 最差網路延遲
server HEARTBEAT_INTERVAL 與 client HEARTBEAT_TIMEOUT 要滿足什麼關係?
TIMEOUT > INTERVAL + 最差 RTT;INTERVAL 越大發現越慢(最壞 2 倍)、開銷越小;兩個常數分寫兩檔,改一邊要改另一邊
第 13 段 → 總結:間隔調大,開銷小但必要

Real Frontend System Design (from a Senior Engineer)

theSeniorDev 前端底層與系統設計 產生於 2026-09-21 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

從一句題目到低保真草稿:鎖定一個頁面 → 寫 user story → 畫只有區塊的草圖當共用座標系
  1. 挑流量最集中的一個頁面(作者選商品列表頁)
  2. 把功能需求寫成 3–5 條「使用者可以…」的 user story
  3. 畫低保真草稿:只有區塊與位置,不畫視覺細節
  4. 之後抽狀態、算讀寫比、切微前端都指這張圖
今天就做 拿你目前專案的一個頁面,10 分鐘內寫下 4 條 user story,並在紙上畫出只有方框的低保真草稿,每個方框標一個名字
第 3 段 → 從使用者故事到低保真草稿 · user story low-fidelity mockup
irreducibility test:逐一拿掉每個狀態變數,UI 還做得出來就砍掉
  1. 用「外部資料進來 / 使用者動作進來」兩個標籤掃過草稿列出狀態
  2. 每個遠端資料都是 loading / error / data 三個
  3. 逐一嘗試拿掉一個狀態變數,若 UI 仍能運作就砍
  4. 剩下的分成狀態原始值(字串、數字)與領域實體(product、brand)
  5. 超出範圍的狀態(購物車)主動宣告排除,不要默默略過
今天就做 打開你專案一個有 3 個以上 useState 的元件,逐一問「這個能不能從其他 state 算出來」,至少砍掉或改成推導值一個
第 4 段 → 抽狀態與不可再簡測試 · irreducibility test
資深度也由你問的問題決定:每個問題都要夾住一個後面的決策
  1. 預期多少流量?有無時段或地理分布差異?(→ 估算與 CDN)
  2. 有無 Prime Day 這種尖峰?(→ 要不要為峰值付費)
  3. 主要瀏覽器與裝置?(→ bundle 預算與圖片尺寸)
  4. 載入速度是否關鍵?(→ SSR / CSR)
  5. 無障礙有無法規要求?(→ A / AA / AAA 做到哪級)
  6. 有無特殊資安顧慮?
今天就做 挑一個你手上正在做的功能,寫下 5 個你會問 PM 的問題,每題後面標註它會決定哪一個技術決策;答不出「決定什麼」的問題刪掉
第 7 段 → 六大非功能需求與該問的問題
back of the envelope:每一步都是「總量 × 一個佔比」,佔比來自前面問出的分布
  1. 月造訪 → 週(÷4)
  2. 週 → 高峰日(週二到週四佔一半)→ 單日
  3. 單日 → 尖峰小時(晚上七到十點佔一半,再 ÷3)
  4. 尖峰小時造訪 × 每次造訪頁面數 = 尖峰每小時頁面瀏覽
  5. 每一步跟面試官講清楚假設;Prime Day 之類事件先排除
今天就做 拿你產品(或任一個你知道月活的網站)的月造訪數,15 分鐘內推到尖峰每秒請求數,每一步寫下用了哪個假設佔比
第 8 段 → 流量估算:back of the envelope · back of the envelope calculation
可用性檢查:對著架構圖逐個元件問「這個只有一份嗎」,只有一份的就是天花板
  1. 列出架構圖每個元件
  2. 逐一問「只有一份嗎?掛了誰接手?」
  3. 有冗餘的(CDN 節點、LB 後實例、唯讀副本)略過
  4. 只有一份的(負載平衡器、主資料庫、DNS)標成單點故障
  5. 用 active-passive 補:一台接流量一台待命;記得待命要定期演練、切換空窗即停機
今天就做 畫你目前負責系統的架構圖(10 個元件以內),圈出所有只有一份的元件,每個寫一句「它掛了使用者會看到什麼」
第 17 段 → 可用性、單點故障與 active-passive · single point of failure active-passive pattern

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

飛機比喻:座位數、引擎是功能需求;航程、油耗、噪音是非功能需求
新 功能需求由看得到的 UI 滿足,非功能需求落在效能、可擴展性、安全性這些看不見的地方  ⇄  像 飛機:要做到什麼(座位、起落架)vs 怎麼做到(航程、油耗、碳排)
第 2 段 → 功能與非功能需求(飛機比喻)
Level 1 到 3 是同一招用三次:把資產、渲染、資料依序搬近使用者,每次看「還剩哪段路最長」
新 SSR 把延遲請回來 → 邊緣函式;邊緣函式仍回中央 DB → 唯讀副本推到邊緣  ⇄  像 第 10 段的 CDN:把靜態資產複製到離使用者最近的節點
第 11 段 → 從白畫面到邊緣運算(scale level 3) · edge computing read replica

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

non-functional requirement:系統以什麼品質做到(多快、撐多少人、多安全),會產出架構;幾乎都可量化
non-functional requirement
第 2 段 → 功能與非功能需求(飛機比喻) · non-functional requirement functional requirement
state:為畫出目前畫面必須記住的最小資訊;來源只有 data fetching 與 user interaction 兩種
state
第 4 段 → 抽狀態與不可再簡測試 · state domain entity
lifting state:狀態被抬高不是因為很多元件要用,而是它成了某個副作用(API 請求)的輸入
lifting state
第 5 段 → 狀態的高度:lifting state · lifting state component tree global state
under-fetching:REST 資源切得越乾淨,一個畫面要組的資源越多;一次瀏覽打五支是 client-server 極限
under-fetching
第 6 段 → REST 建模與 under-fetching · under-fetching REST client-server architecture
Core Web Vitals 是前端的 SLI:LCP / CLS 管初次載入,INP 管互動;還要一條目標線才算 SLO
Core Web Vitals
第 9 段 → Core Web Vitals 與 UI 動靜光譜 · Core Web Vitals SLI LCP CLS INP
CDN:延遲 = 來回次數 × 地理距離,把靜態資產複製到 edge location 是唯一能同時砍每次來回的手段
CDN
第 10 段 → 網路延遲與 CDN(scale level 1) · CDN edge location network latency
server-side rendering:伺服器先取資料渲染好 HTML,解 CSR 白畫面;hydration 前看得到按不動
server-side rendering
第 11 段 → 從白畫面到邊緣運算(scale level 3) · server-side rendering client-side rendering hydration white screen of death
backend for frontend:按畫面形狀組資料的 facade,由前端團隊擁有,一個 client 一套
backend for frontend
第 13 段 → API Gateway 與 BFF · backend for frontend facade pattern
GraphQL:一次查詢取多個資源、只要需要的欄位;解的是「形狀」不是「速度」,後端服務一支都沒少
GraphQL
第 14 段 → GraphQL 與水平擴展(scale level 4) · GraphQL resolver over-fetching
Conway's law:系統會複製組織的溝通結構;溝通線隨人數平方成長 → 拆團隊就得先拆系統 → 微前端
Conway's law
第 15 段 → 康威定律與微前端(scale level 5) · Conway's law two-pizza team micro frontend shell application

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

最常見錯誤:把篩選後的商品陣列另存一份,它其實是商品陣列 + 選中品牌的推導結果
不可再簡測試擋的就是「把可以算出來的東西存成狀態」;篩選結果 = products.filter(selectedBrands),不該是獨立 state
證明 state
演練 這個例子證明了狀態「越少越好」的代價在哪(記憶體、re-render)?你專案裡有哪個 state 其實是推導值?
第 4 段 → 抽狀態與不可再簡測試
current page 與 brand filter 都要打同一支商品 API,所以被抬到與商品請求同層
current page 必須拿去打 API → 抬到與商品資料請求同層;品牌篩選影響同一個請求 → 三個狀態最後都是同層的 shared local state
證明 lifting state
演練 這證明了判斷狀態高度的依據是什麼?下次遇到「該不該放 global」的爭論,你會先找什麼?
第 5 段 → 狀態的高度:lifting state
美國月 21.1 億次造訪 → 尖峰每小時 1.36 億次頁面瀏覽;59% 購買在行動、70% 造訪在搜尋頁
21.1 億/月 → 5 億/週 → 週二至四 2.5 億 → 週四 1.25 億 → 尖峰小時 2100 萬次造訪 × 6.8 頁 = 1.36 億頁面瀏覽/小時;行動 > 70%
證明 non-functional requirement
演練 這組數字證明了什麼(單機不可能、該優先做哪個頁面)?行動佔七成同時決定了哪兩件相反的事(JS 要輕、圖片可以更小)?
第 8 段 → 流量估算:back of the envelope
SF→NY 光速單程 9ms,實測來回 140–160ms,再加 TCP + TLS 建連要好幾百 ms
光纖約 30 萬 km/s,2654 km 單程約 9 ms(人感覺慢要 > 100 ms);封包經一連串交換節點,一次來回 140–160 ms;建連再乘上來回次數。AI 更正:TLS 1.3 一次來回、續連可 0-RTT,現代 HTTPS 約兩次來回;HTTP/3 再合併
證明 CDN
演練 這組數字證明了「慢」的來源是物理距離而非程式寫得爛,你會怎麼用「先算物理下限、再看實際差多少」這個姿勢分析自己專案的載入時間?
第 10 段 → 網路延遲與 CDN(scale level 1)
3 人 3 條溝通線、14 人 91 條;某公司 45 人共用單體,每天 1.5 小時站立會議九成與你無關
溝通線 n(n-1)/2;45 人單體的站立會議案例;微前端各自獨立網域、各自發版,由 shell 在執行期組起來(Level 5)
證明 Conway's law
演練 這個數字證明了大團隊為何必然低效?倒過來用:你的系統邊界和團隊邊界對不上時,痛苦會出現在哪個環節?微前端的代價(重複相依、跨應用狀態、樣式失控)你會怎麼主動講?
第 15 段 → 康威定律與微前端(scale level 5)

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

別背「Spotify 怎麼做」:題目每次換、心智模型不換,要展示的是被需求逼出架構的過程
為什麼前端系統設計面試不該背範例答案?
題目每次不同但模型可遷移;面試要展示「同一個系統被非功能需求加壓、一層層長出來」的過程,scale level 編號是作者自創的教學工具
第 1 段 → 別背題目,要學心智模型 · mental model
三個刻度 local / shared local / global;原則「as low as possible, as high as necessary」
狀態位置的三個刻度是什麼?預設放哪?抬高的代價是什麼?
local → shared local → global(例如登入身分);從最低層開始只在必要時往上;抬高後每次改變讓整個子樹重算
第 5 段 → 狀態的高度:lifting state
拆實體判準:欄位的變動頻率、生命週期、計算來源跟主體不一樣就拆出去
什麼時候該把 price、rating、deliveryTime 從 product 拆成獨立資源?
價格會單獨頻繁改、評分是評論的聚合結果、到貨時間依使用者地址即時算——生命週期與主體不同就各自 endpoint;篩選分頁用 query parameter 不另開端點
第 6 段 → REST 建模與 under-fetching
六大非功能需求,順序即後半段章節目錄
作者列的六項非功能需求是哪些?為什麼這樣排?
web performance、client scalability、availability & fault tolerance、accessibility、maintainability、security;前兩項改架構圖、可維護性改團隊與 repo、後兩項靠實作紀律
第 7 段 → 六大非功能需求與該問的問題
UI 動靜光譜:靜態端靠複製資產(CDN),動態端靠移動運算(SSR / edge),電商在中間兩套都做
「你的 UI 有多靜態或多動態」這個問題決定了什麼?
靜態端(媒體、部落格)靠 CDN 複製資產;動態端(SaaS、Uber)靠 SSR 與邊緣運算;電商卡中間所以兩套都得做
第 9 段 → Core Web Vitals 與 UI 動靜光譜
CAP 的正確講法:網路分區發生時,一致性與可用性只能挑一個;唯讀副本選可用性、換來最終一致
CAP 定理正確的表述是什麼?邊緣唯讀副本選了哪一邊?
分區發生時一致性 vs 可用性擇一;唯讀副本選可用性,代價是最終一致性——社群可接受、金融不行
第 11 段 → 從白畫面到邊緣運算(scale level 3) · CAP theorem
判斷哪些請求可以消除只問一題:哪些資料讀遠多於寫?圖片標題可快取,價格到貨時間不行
最有效的擴展是消除請求,怎麼判斷哪些請求可以消除?
看讀寫比:讀百萬次很少變的(商品圖、標題)設積極快取;讀寫都頻繁的(價格、到貨時間)不行。實務上靠雜湊檔名+長期快取而不是縮短時間
第 12 段 → 用快取消除請求 · HTTP caching
API Gateway 解「重複實作」(HTTPS、快取、授權做一次),BFF 解「形狀不匹配」(領域切 vs 視圖切)
怎麼判斷需要 API Gateway 還是 BFF?
痛點是每個服務都在重做同一件事 → gateway(反向代理);痛點是前端要打很多支再拼 → BFF。BFF 是要部署監控的服務,畫面差異小時一個加參數比兩個划算
第 13 段 → API Gateway 與 BFF · API gateway reverse proxy
面試提 GraphQL 要主動說代價:查詢複雜度要限制、快取比 REST 難
GraphQL 引進哪些新問題?水平擴展 BFF 的前提是什麼?
惡意查詢可打爆後端要限制複雜度;不再一 URL 一資源,快取靠正規化客戶端快取或持久化查詢。水平擴展前提:實例無狀態,session 不能存在記憶體
第 14 段 → GraphQL 與水平擴展(scale level 4)
design system 統一使用者看到的東西,monorepo 統一工程師碰到的東西——把微前端拆散的「該一致的部分」收回
微前端之後為什麼要補 design system 與 monorepo?各自代價是什麼?
拆系統換自主,這兩樣把該一致的收回:design system(npm 元件庫)要專責維護否則變化石;monorepo 要投資受影響範圍偵測與遠端建置快取,否則 CI 線性惡化
第 16 段 → Design system 與 monorepo · design system monorepo
無障礙四條:語意化 HTML 優先、ARIA 是例外、tab 順序等於視覺順序、開發期就用 axe 測
拿下 WCAG AA 的最短路徑四條是什麼?最常見反例?
盡量語意化 HTML(免費得到行為);做不到才用 aria;tab 順序與視覺一致;開發階段用 axe 測。反例:React 開發者習慣多包一層 div / span
第 18 段 → 無障礙與資安速查 · semantic HTML ARIA
資安速查:MITM→HTTPS、XSS→不執行使用者內容+CSP、CSRF→SameSite+token、SQLi→ORM、DDoS→過濾+限流
五種主要攻擊與對策各是什麼?
中間人:全站 HTTPS;XSS:別用 dangerouslySetInnerHTML、加 CSP 當第二層;CSRF:SameSite cookie + CSRF token(第二因素只給高風險操作);SQL injection:ORM 參數化;DDoS:流量過濾、Cloudflare、rate limiting。共同邏輯:預設安全比事後檢查便宜
第 18 段 → 無障礙與資安速查 · XSS CSRF SQL injection DDoS man-in-the-middle attack
五類可觀測性工具各答一題:log 壞了嗎、RUM 多快、產品分析用什麼、網站分析從哪來、consent 有沒有權利收
可觀測性的五類工具各回答什麼問題?它們自己會拖慢什麼?
集中 log 與告警(Datadog)→ 壞了嗎;RUM(Sentry)→ 真實 CWV;產品分析(Amplitude)→ 用什麼、A/B;網站分析(GA)→ 漏斗轉換;consent manager → 能不能收。分析工具丟到 web worker 跑,否則塞主執行緒拖累 INP
第 19 段 → SEO 與前端可觀測性 · observability real user monitoring web worker consent manager

Top 15 Frontend Interview Questions for 2026 (wa/ Senior Engineer)

theSeniorDev 前端底層與系統設計 產生於 2026-09-21 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

資深答題法:先把開放問題切開分類,再給結構加取捨(什麼條件下用、代價、不用會怎樣)
  1. 聽到問題先不報工具名,先分類(例如 static / dynamic、載入慢 / 互動慢、CSR / SSR)
  2. 每一類給一個做法
  3. 每個做法附條件與代價:什麼時候用、不用會怎樣
  4. 最後用一句「為什麼」收尾
今天就做 挑影片裡任一題(例如「用 AI 時怎麼守住品質」),錄一段兩分鐘回答,第一句必須是分類而不是工具名
第 1 段 → 開場:被拒的理由是「不夠 senior」
資深 AI 工作流三層約束:素材(plan mode、MCP 取材)、結構(單一狀態來源、相對單位)、邊界(先定 props 形狀)
  1. 先 plan mode 規劃,讓 agent 透過 Figma MCP 與既有 design system 取材,用你的積木
  2. 規則寫進去:state 遵守 single record principle、CSS 用相對單位
  3. 自己先決定 component 的 props 與 state 形狀,再讓 AI 填實作
  4. 只逐行看爆炸半徑大的改動:全域設定、測試、linter、type checker、package.json
今天就做 下一個要交給 agent 做的元件,先自己寫好它的 props type 與 state 形狀(10 行以內)再下 prompt,比較產出跟以前差在哪
第 3 段 → Q2 資深的 AI 工作流長什麼樣 · plan mode MCP programming to interfaces
把 AAA(arrange / act / assert)格式直接交代給 AI,產出的測試人才讀得下去
  1. arrange:準備輸入與 test double
  2. act:只呼叫一次被測的東西
  3. assert:驗證行為,不只是「有跑到」
  4. 把這三段的規則寫進 agent 的專案規則檔
今天就做 打開你專案任一個既有測試,加上 // arrange // act // assert 三行註解重排,順手把這條規則寫進 CLAUDE.md 或 agent 規則檔
第 5 段 → Q4 測試:覆蓋率、金字塔、AAA · AAA pattern
全域 border-box reset,並把 AI 硬寫的 width: 300px 換成相對尺寸,別用 breakpoint 補洞
  1. 全域加 *, *::before, *::after { box-sizing: border-box }
  2. grep 出 AI 產的 width: NNNpx,判斷該跟容器走還是跟字級走
  3. 改成 %、rem、em 或 max-width
  4. 在 DevTools 用手機寬度驗證
今天就做 在你的專案 grep "width: [0-9]*px",挑三處 AI 硬寫的像素寬度改成相對單位,手機視窗確認不爆
第 8 段 → Q7 CSS box model 與 box-sizing
打字卡頓:先錄 Performance 分辨 scripting/rendering,再 state 下推、debounce、降級更新
  1. DevTools Performance 錄一段打字,看是 scripting 還是 layout 佔時間
  2. 輸入的 state 下推到最靠近的元件,不要放太高
  3. 昂貴的後續動作(搜尋、驗證)做 debounce
  4. React 18+ 用 useDeferredValue / startTransition 把非緊急更新降級
  5. 每次 re-render 用 memoization 變便宜
今天就做 找你專案裡一個受控 input,看它的 state 放在哪一層;如果高於直接父元件就把它下推一層
第 12 段 → event loop 在生產環境的真實 bug
vibe coded React 很慢:先量 LCP 還是 INP 差,載入慢走 bundle 分析,互動慢走 re-render
  1. Lighthouse / web-vitals 先量:LCP 差 = 載入、INP 差 = 互動
  2. 載入:bundle analyzer 看組成 → 砍相依 → dynamic import 做 code splitting → SSR / server components
  3. 互動:React.memo 避免重繪(必須搭 useCallback / useMemo 否則 props 永遠不相等)
  4. 不需要 state 的邏輯搬到元件外;state 不要無謂往上提
  5. 別忘了字型與圖片,它們常是 LCP 主因
今天就做 對你的專案跑一次 bundle analyzer(vite-bundle-visualizer 或 webpack-bundle-analyzer),記下最大的三個相依並判斷能不能延後載入
第 13 段 → Q11 vibe coded 的 React 很慢怎麼辦 · critical rendering path code splitting React.memo
依變動頻率切 bundle:vendor 長壽 chunk 配長 max-age、自己的碼短快取;靠內容雜湊檔名才安全
  1. 先分辨 CSR 還是 SSR;CSR 靜態資產上 CDN
  2. Webpack / Vite 把 React、React DOM 等穩定 vendor 切成獨立 chunk
  3. 確認輸出檔名帶內容雜湊(vendor.a3f9c2.js)
  4. 帶雜湊的資產設 max-age=31536000, immutable;HTML 不快取或短快取
  5. micro-frontend 只在同時改同一份碼的人多時才需要;SSR 推到 edge 要連資料庫一起,寫入貴且最終一致,多數不值得
今天就做 看你專案 build 輸出的檔名有沒有內容雜湊,有的話檢查靜態資產的 Cache-Control 是否為 max-age=31536000, immutable,沒有就補上
第 16 段 → Q14 從一千到十萬日活怎麼擴 · max-age CDN

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

壓 token 花費=壓「做一個功能要改的程式碼量」,也就是老問題:模組化與解耦
新 AI 成本的真正槓桿在改動量,不在讓模型少講話  ⇄  像 傳統軟體工程追求的模組化、hexagonal architecture、SOLID
第 6 段 → Q5 怎麼把 token 花費壓低
JavaScript 與 rendering 是兩條輸送帶共用同一個機器人,所以「別阻塞 main thread」是結構後果
新 main thread 同時負責跑 JS、算樣式、layout、paint  ⇄  像 工廠生產線:兩條輸送帶輪流用同一隻機械手臂
第 11 段 → Q10 event loop 的完整運作
用資料流的形狀選協定:真人聊天是雙向所以 WebSocket,LLM 是不對稱所以 SSE
新 LLM 聊天機器人用 SSE:client POST 一次、伺服器串流 token  ⇄  像 WebSocket 適合真人對真人的聊天(影片內先出現)
第 17 段 → Q15 LLM 聊天機器人用哪種即時協定

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

reflow:改 width 這類幾何屬性會讓瀏覽器重算 layout、重跑整條 render;能繞過 main thread 就繞過
reflow
第 2 段 → Q1 hover 放大:reflow 還是 compositor · reflow
static analysis:不執行程式就能檢查,便宜、快、結果確定,能加就先加滿
static analysis
第 4 段 → Q3 用 AI 時怎麼守住程式碼品質 · static analysis
testing pyramid:unit 多、e2e 少;判準是訊號定位精度 ÷ 維護成本
testing pyramid
第 5 段 → Q4 測試:覆蓋率、金字塔、AAA · testing pyramid
design tokens:把顏色、間距、字級抽成具名變數,是 design system 四層(token→元件→組合→工具)的底層
design tokens
第 7 段 → Q6 什麼樣的 design system 算好 · design tokens
box-sizing 決定 width 這個數字算到哪一層:content-box 只算內容、border-box 含 padding 與 border
box-sizing
第 8 段 → Q7 CSS box model 與 box-sizing · box-sizing
CSS specificity:A-B-C 三欄(ID / class 屬性 / 元素)從左往右逐位比大小,不是加總
CSS specificity
第 9 段 → Q8 CSS specificity 怎麼算出勝負 · CSS specificity
event loop:JS 單執行緒是因為 DOM 是唯一共享結構;一條 call stack 加 microtask / macrotask 兩個佇列
event loop
第 11 段 → Q10 event loop 的完整運作 · event loop
reachability:從根部能否經參照鏈走到,是 GC 唯一判準;Map 持強參照所以 key 活著,WeakMap 的參照不算
reachability
第 15 段 → Q13 Map、WeakMap 與垃圾回收 · reachability
Server-Sent Events:一條長開的 HTTP 回應單向推文字事件,瀏覽器原生;對上 LLM「送一次、收一長串」的不對稱形狀
Server-Sent Events
第 17 段 → Q15 LLM 聊天機器人用哪種即時協定 · Server-Sent Events

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

static 檢查成本固定(CI 幾十秒),dynamic 隨程式碼量線性成長;AI 日產數百行時後者會失控
投資順序:TypeScript 型別邊界 → linter(ESLint / Oxlint)→ cyclomatic complexity 與 code duplication;AI 重複常是結構相同字面不同,要用 token / AST 級工具(jscpd)才抓得到;靜態檢查不擋大檔案,靠 unit test 逼你拆
證明 static analysis
演練 這個成本結構證明了為什麼順序是先 static——如果你的專案現在只有 e2e 測試沒有 linter,你會先補哪一個、為什麼?
第 4 段 → Q3 用 AI 時怎麼守住程式碼品質
coverage 落在 65% 以上但別衝過 85%:AI 什麼都能寫測試,過高多半是沒人驗證的假覆蓋率
覆蓋率量的是「這行有被執行」不是「行為有被驗證」;AI 容易寫出跑過所有分支卻沒有意義斷言的測試。搭配看斷言品質或 mutation testing(故意改壞程式看測試會不會紅)
證明 testing pyramid
演練 這條門檻證明了數字本身不是目標——你的專案 coverage 是多少?你會怎麼驗證裡面有多少是真的?
第 5 段 → Q4 測試:覆蓋率、金字塔、AAA
Figma 與 CSS 兩份 token 靠人工同步遲早漂移;要讓一邊成唯一來源,用工具生成另一邊
例:Style Dictionary 把 token 產成 CSS 變數;design-to-code MCP 讓 Figma 成為 agent 的最佳化標的。分層清楚的系統把「該從哪一層開始」變成選擇題,這正是它對 AI 有效的原因
證明 design tokens
演練 這證明了「接上 MCP」不是重點、單一來源才是——你的專案裡顏色現在存在幾個地方?哪一份該當來源?
第 7 段 → Q6 什麼樣的 design system 算好
microtask queue 是一次清空不是一次一個:不斷產生 microtask 的 promise 迴圈會讓畫面凍住,setTimeout 版反而能更新
正確節奏:跑一個 macrotask → 把 microtask 排乾(含過程新產生的)→ 必要時 rendering(約 16.7 ms 對齊螢幕,requestAnimationFrame 在此)→ 下一個 macrotask
證明 event loop
演練 這證明「用 promise 還是 setTimeout 排隊」是有答案的——你手上哪段迴圈式的非同步程式該換成 macrotask 讓畫面能更新?
第 11 段 → Q10 event loop 的完整運作
WeakMap 沒 size、沒 iterator、key 必須是物件或 symbol——因為暴露這些會讓 GC 時機變成可觀察行為
用 WeakMap 以物件參數當 key 做 memoization:物件一被丟棄快取自動消失。但 weakMap.set(key, value 裡又參照 key) 仍會漏——問題在參照關係不在容器
證明 reachability
演練 這些限制證明了 WeakMap 的設計取捨——你會在哪個快取場景用它?什麼情況下即使用了 WeakMap 還是漏?
第 15 段 → Q13 Map、WeakMap 與垃圾回收

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

能繞過 layout 的只有可合成屬性(主要 transform、opacity);transition: width 照樣每影格 reflow
hover 放大用 CSS transition 就一定比 JS 便宜嗎?
不一定,看動的是哪個屬性:transform / opacity 走 compositor;transition: width 每影格照樣 layout + paint
第 2 段 → Q1 hover 放大:reflow 還是 compositor · CSS transform
single record principle:同一份事實只存一次,是資料庫正規化搬到前端 state;AI 逐段產碼特別容易違反
single record principle 是什麼?為什麼 AI 產的程式特別容易違反?
同一筆資料只存一處;模型逐段產碼、每段局部合理,於是同一筆資料被複製到三處各自更新,忘了同步就出 bug
第 3 段 → Q2 資深的 AI 工作流長什麼樣 · single record principle
blast radius:只值得逐行 review 的檔案是全域設定、測試、linter、type checker、package.json
AI 改動裡哪些路徑該逐行看?怎麼落成機制?
全域設定、測試、linter、type checker、package.json;列進 CODEOWNERS 或 review checklist
第 3 段 → Q2 資深的 AI 工作流長什麼樣 · blast radius
e2e 掛掉只說系統壞了不說壞在哪;unit test 可以一段段排除嫌疑,除錯便宜
為什麼「只寫 e2e 就夠」在 AI 時代是錯的?
e2e 描述使用者行為適合 AI 生成,但它是最貴的除錯入口——掛了得花時間或 token 去搜;unit test 定位精度高
第 5 段 → Q4 測試:覆蓋率、金字塔、AAA
box model 由外而內 margin、border、padding、content;outline 在 border 外
box model 四層由外而內是什麼?outline 畫在哪?
margin → border → padding → content;outline 與 box-shadow 在 border 外、margin 內
第 8 段 → Q7 CSS box model 與 box-sizing · CSS box model
margin collapsing:垂直相鄰 margin 取較大者不相加;父子間沒 padding / border / BFC 時也會
「明明加了 margin 卻沒變化」最可能是什麼?條件?
margin collapsing:只在垂直方向、父子間沒有 padding、border 或 BFC 時,合併成較大的那個
第 8 段 → Q7 CSS box model 與 box-sizing · margin collapsing
form .big-form 是 0-1-1,#my-form 是 1-0-0;ID 那欄先分勝負所以藍底贏
form .big-form 與 #my-form 同時命中,誰贏?分數各多少?
#my-form 贏:1-0-0 對 0-1-1,第一欄就分出勝負,後面不看
第 9 段 → Q8 CSS specificity 怎麼算出勝負
cascade 順序:importance → layers → specificity → 出現順序;:where() 算 0 分
為什麼 !important 贏過任何選擇器?@layer 裡低 specificity 為什麼可能贏?
順序是 importance → layers → specificity → order;前面的關卡先分勝負。:where() 內容一律 0 分、:is() 取最高
第 9 段 → Q8 CSS specificity 怎麼算出勝負 · cascade algorithm
rem 相對 root、em 相對父層;可重用元件用 em、要跟全站尺度一致用 rem;border 用 px 反而對
什麼時候用 rem、什麼時候用 em?什麼東西該保留 px?
rem:跟全站尺度一致;em:可重用元件跟父層走;px:border 寬度、某些圖示這種不該隨字級縮放的
第 10 段 → Q9 響應式設計的三根支柱 · rem em
影片少講 container queries:media query 問視窗多寬,元件真正在意的是容器多寬
同一張卡片在側欄與主欄要不同版面,media query 為什麼做不到?用什麼?
視窗寬度一樣所以 media query 分不出;用 @container(主流瀏覽器已支援)。grid 用 repeat(auto-fit, minmax()) 也能不靠 breakpoint
第 10 段 → Q9 響應式設計的三根支柱 · media query
object vs Map:key 型別、可迭代、順序保證、乾淨程度(無 prototype)、JSON 序列化
object 與 Map 有哪些差別?什麼時候才換 Map?
Map key 可任意值且保插入順序、本身可迭代、無 prototype 污染、頻繁增刪快;object 語法順手且可直接 JSON。key 超出 string / symbol 才換 Map
第 14 段 → Q12 object 與 map 的差別 · Map

15 Frontend Concepts Every Senior Dev Has Mastered

theSeniorDev 前端底層與系統設計 產生於 2026-09-21 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

看一份 HTML 找出哪些資源會擋住首次繪製:head 裡的 stylesheet 與同步 script 都是 render blocking
  1. 打開頁面的原始 HTML,從上往下讀 head
  2. 每個 <link rel=stylesheet> 與沒有 async/defer 的 <script> 記一筆:這些都擋 FCP
  3. 找到 body 裡第一個有內容的元素,那就是 FCP 的位置
  4. 數一數 FCP 之前有幾個 blocking 請求,這就是你的優化清單
今天就做 打開你專案的正式頁面 view-source,列出 head 裡所有會 render blocking 的 stylesheet 與 script,各標上「必要/可延後」
第 2 段 → #1 關鍵渲染路徑 · render blocking first contentful paint
把點了才需要的功能改成動態匯入:await import("./modal") 解構出具名匯出再呼叫
  1. 找一個只在使用者操作後才用到的模組(modal、圖表、編輯器)
  2. 把頂端的 static import 拿掉
  3. 在事件處理器裡寫 const { fn } = await import("./modal")
  4. build 後確認多出一個獨立 chunk、主 bundle 變小
  5. 若延遲影響互動,在 hover 或元件即將進入視窗時先預抓
今天就做 在你的專案找一個點擊才會開的 modal 或圖表元件,改成 await import() 動態載入,build 後比較主 bundle 大小
第 7 段 → #5 延遲載入與動態匯入 · dynamic import
critical CSS:用無頭瀏覽器預渲染首屏收集命中的規則,內聯進 HTML,其餘非阻擋載入
  1. build 時讓 bundler 呼叫 Puppeteer 在指定視窗尺寸預渲染一次
  2. 收集 above the fold 實際命中的選擇器,抽成 critical CSS(桌機、手機分開)
  3. 把 critical CSS 內聯進 HTML 的 <style>,控制在 14KB 以內
  4. 其餘 CSS 用 <link rel="preload" as="style" onload="this.rel='stylesheet'"> 非阻擋載入
  5. 做首屏跑版的回歸測試
今天就做 在 Chrome DevTools 開 Coverage 面板重載你的首頁,看首屏未使用的 CSS 佔幾 %,記下這個數字當 critical CSS 值不值得做的依據
第 9 段 → #7 Critical CSS · critical CSS above the fold headless browser inlining
盤點一個元件的狀態:逐項問「能不能從別的狀態算出來」,能的就刪掉改成計算
  1. 列出元件裡所有 useState / store 欄位
  2. 每一項問:拿掉它,能不能從其他欄位算出來?
  3. 能算的改成 render 時計算(貴的用 useMemo)
  4. 剩下的就是 essential state,通常只剩三四項
今天就做 打開你專案裡狀態最多的一個元件,把每個 state 欄位列出來標「essential/derived」,把至少一個 derived 改成計算值
第 10 段 → #8 Essential state · essential state derived state
windowing:長清單只掛載視窗內的項目,隨捲動掛載/卸載,用 Intersection Observer 判斷進出
  1. 找出畫面上超過幾百個節點的清單(動態牆、表格)
  2. 只渲染 viewport 內加少量緩衝的項目
  3. 卸載的項目留等高的佔位空間,捲軸長度才不會跳
  4. 用 Intersection Observer(非同步、不強制排版)判斷進出視窗
  5. 列高不一時先估算再量測校正——或直接用 react-window / TanStack Virtual
今天就做 在你的專案找最長的清單頁,DevTools Console 跑 document.querySelectorAll('*').length,超過一千就把它列為 windowing 候選
第 12 段 → #10 Windowing 與可視區觀察 · windowing Intersection Observer viewport

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

三個 Core Web Vitals 像 F1 賽車的極速/敏捷/下壓力:複雜系統不能用一個數字描述
新 LCP / INP / CLS 三個互補指標  ⇄  像 F1 賽車要同時看極速、敏捷、下壓力
第 3 段 → #2 Core Web Vitals
HTTP 快取就是 memoization 在檔案層的特例:同樣輸入重用上次輸出,只多了一個 TTL
新 HTTP 快取:同一個 URL 在 TTL 內直接用瀏覽器存的副本  ⇄  像 memoization:函式對同樣輸入重用上次計算結果
第 4 段 → #3 HTTP 快取與 TTL
資深前端看 UI 像汽車工程師看車:看到的不是外觀而是內部系統(元件結構與狀態)
新 看到購物車畫面時先問「essential state 是什麼」  ⇄  像 汽車工程師看車看的是引擎、傳動,不是外觀
第 10 段 → #8 Essential state
每個微前端像公司裡的小公司:自己的 PM、BFF、微服務,能獨立部署
新 micro frontends:shell 注入全域狀態,各區塊獨立開發部署  ⇄  像 大公司拆成獨立損益的事業單位
第 17 段 → #15 微前端與收尾

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

critical rendering path:DOM+CSSOM 合成 render tree,再 Style→Layout→Paint→Composite
critical rendering path
第 2 段 → #1 關鍵渲染路徑 · critical rendering path DOM CSSOM render tree
Core Web Vitals:LCP、INP、CLS 三個互補指標,各自量到渲染路徑的不同段落
Core Web Vitals
第 3 段 → #2 Core Web Vitals · Core Web Vitals LCP INP CLS
TTL:快取資料被視為新鮮、可直接重用的時間;過期後拿 ETag 問伺服器「還新鮮嗎」
TTL
第 4 段 → #3 HTTP 快取與 TTL · TTL Cache-Control ETag
CDN:把靜態資源複製到全球邊緣節點,使用者連最近的點;同時帶來距離、快取政策、壓縮三件事
CDN
第 5 段 → CDN:把快取推到邊緣 · CDN edge location origin server
content negotiation:瀏覽器用 Accept-* 標頭說自己接受什麼,伺服器挑一個手上有的版本回並標明
content negotiation
第 6 段 → #4 內容協商與壓縮 · content negotiation Accept-Encoding Content-Encoding
dynamic import:import() 在執行期才抓模組、回傳 Promise,打包工具自動切成獨立 chunk
dynamic import
第 7 段 → #5 延遲載入與動態匯入 · dynamic import static import chunk lazy loading
bundle splitting:相依套件抽成 vendor,其餘依路由切開,每頁只下載自己要的 JS
bundle splitting
第 8 段 → #6 Bundle splitting · bundle splitting vendor bundle
essential state:渲染 UI 所需、且無法從其他資料推導的最小集合;其餘全部算出來
essential state
第 10 段 → #8 Essential state · essential state derived state single source of truth
reducer pattern:所有變更集中到 (舊狀態, action) → 新狀態 的純函式,元件各自訂閱結果
reducer pattern
第 11 段 → #9 Reducer 模式 · reducer pattern pure function immutability action
server-side rendering:伺服器跑元件、直接取資料、產出完整 HTML;改善「看得到」不改善「能互動」
server-side rendering
第 13 段 → #11 伺服器端渲染 · server-side rendering client-side rendering first meaningful paint

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

門檻值:LCP 好 ≤2.5s 差 >4s;INP 好 ≤200ms 差 >500ms;CLS 好 ≤0.1 差 >0.25
LCP ≤ 2.5 秒(> 4 秒差)、INP ≤ 200 毫秒(> 500 毫秒差)、CLS ≤ 0.1(> 0.25 差);作者自己對 INP 要求 100 毫秒
證明 Core Web Vitals
演練 你專案的 Lighthouse 報告三個數字各落在哪一區?哪一個最先該動手、對應本片哪個技巧?
第 3 段 → #2 Core Web Vitals
Walmart 回應:Cache-Control: public, max-age=30541039(秒,約 353 天),對得上 Expires 日期
Cache-Control: public, max-age=30541039;Content-Encoding: br;Expires: Tue, 29 Sep 2026。作者口誤說單位是毫秒,實際是秒(30541039 秒 ≈ 353 天)
證明 TTL
演練 這個 max-age 證明了什麼前提(改版時檔名會變)?你專案的靜態資源 max-age 是多少、檔名有沒有雜湊?
第 4 段 → #3 HTTP 快取與 TTL
跨洲來回延遲通常 150–300 毫秒,同城是個位數毫秒;頁面串十幾個請求時差距是決定性的
跨洲 RTT 150–300ms vs 同城個位數 ms;Walmart 回應裡的 Server-Timing: cdn-cache;desc=HIT 就是 CDN 命中的證據;CDN 只取代靜態資源分發,動態 API 仍回原伺服器
證明 CDN
演練 為什麼作者說 CDN 是投資報酬率最高的第一步?你的專案哪些請求還在跨洲跑原伺服器?
第 5 段 → CDN:把快取推到邊緣
Chrome 送 Accept-Encoding: gzip, deflate, br, zstd;伺服器備三個版本挑一個回
截圖:Accept-Encoding: gzip, deflate, br, zstd;Accept-Language: en-GB,en-US;q=0.9;Accept: text/css——同一機制協商壓縮、語言、格式三件事。zstd 自 2024 起 Chrome/Firefox 支援
證明 content negotiation
演練 這張截圖證明內容協商不只協商壓縮,還協商什麼?解壓縮花 CPU 為什麼還划算?
第 6 段 → #4 內容協商與壓縮
vendor 幾乎不變,所以它的長 TTL 能跨版本存活;發新版使用者只重抓小小的路由 chunk
Build 產出 common.js + /login、/home、/dashboard、/settings 各自的檔案;React 實作上每個路由元件套 React.lazy + <Suspense>;切太細會產生瀑布式請求
證明 bundle splitting
演練 把 vendor 單獨抽出來真正的動機是什麼(不只是體積)?切太細會出什麼問題?
第 8 段 → #6 Bundle splitting
購物車畫面七八個數字,essential 只有四項:商品資訊、折扣率、運費、稅率,其餘都是函式
原價 $20.39、劃掉的 $23.99/$59.99、折扣文字、小計、運費 $4.99、稅 $1.83、總計 $27.21——收斂後只剩商品(含原價)、折扣率、運費、稅率;多餘狀態的危險不是效能而是可以彼此矛盾
證明 essential state
演練 折扣價與總價各存一份會出什麼 bug?型別檢查抓得到嗎?你手上哪個元件有這種可推導卻另外存的值?
第 10 段 → #8 Essential state
SPA 的 HTML 只有 <div id="root"></div>:FCP 畫的是空容器,FMP 要等 bundle 執行完
截圖:<div id="root"> 標成 First Contentful Paint,載入 React 並 root.render(<App/>) 的 script 才是 First Meaningful Paint;兩者距離就是白畫面時間。SSR 把成本轉嫁到伺服器(TTFB 上升),常搭配快取或 SSG
證明 server-side rendering
演練 這證明了 FCP 好看為什麼不代表使用者體驗好?你的專案 root div 之後第一個有意義的內容要等多久?
第 13 段 → #11 伺服器端渲染

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

四步渲染的成本排序:Layout 重算最貴、Paint 次之、只動 Composite(transform、opacity)最便宜
Style→Layout→Paint→Composite 四步中哪一步最貴?動畫該只動哪一步?
Layout 最貴、Paint 次之;動畫只動 transform / opacity 就只觸發 Composite
第 2 段 → #1 關鍵渲染路徑
Lighthouse 是 lab data(同一台機器模擬網路),Google 排名看的是 CrUX 的 field data,兩者常對不上
Lighthouse 分數跟 Google 排名用的 Core Web Vitals 數據差在哪?
Lighthouse 是實驗室數據;排名看 CrUX 蒐集的真實使用者(field)數據
第 3 段 → #2 Core Web Vitals
TTL 設很長的前提是檔名帶雜湊(app.4f2a9c.js),否則使用者會卡在舊版本
靜態資源要設一年 TTL 之前必須先滿足什麼?
檔名帶內容雜湊,改版檔名跟著變,舊快取自然不會被用到
第 4 段 → #3 HTTP 快取與 TTL · cache invalidation
回應要附 Vary: Accept-Encoding,否則中間快取可能把 Brotli 版本餵給不支援的客戶端
伺服器做壓縮協商時回應還要加哪個標頭?為什麼?
Vary: Accept-Encoding;讓快取層依編碼分開存,不會把 br 版給不支援的客戶端
第 6 段 → #4 內容協商與壓縮
圖片延遲載入有原生解法 <img loading="lazy">,不必寫 JavaScript
圖片要捲到才載,最簡單的寫法是什麼?延遲載入的代價是什麼?
<img loading="lazy">;代價是互動當下多一次網路來回,會影響 INP
第 7 段 → #5 延遲載入與動態匯入 · lazy loading eager loading
reducer 不是免費的:簡單區域狀態硬套只是多寫樣板碼,價值在「多個元件受同一次變更影響」時才出現
什麼情況值得用 reducer?什麼情況不值得?
一個動作同時影響多個元件(全域開關)值得;單一元件的區域狀態不值得
第 11 段 → #9 Reducer 模式 · prop drilling lifting state up
hydration error 成因:伺服器 HTML 與客戶端首次渲染不一致——隨機值、Date.now()、window 判斷、語系
hydration error 是什麼造成的?怎麼避?
兩邊渲染結果不同(隨機值、Date.now()、window、只在客戶端才有的資料);把這類差異延到 hydrate 之後(effect)再處理。React 18 用 hydrateRoot
第 14 段 → #12 Rehydration · rehydration hydration error
partial pre-rendering 的分界判準:對所有人一樣的靜態先送,跟這個使用者有關的(購物車、推薦)串流補上
同一頁哪些區塊該靜態、哪些該動態?機制上怎麼補動態區塊?
對所有人相同→靜態(可 revalidate);跟使用者有關→動態;伺服器先送靜態外殼與佔位,再用串流 + Suspense 邊界補內容
第 15 段 → #13 Partial pre-rendering · partial pre-rendering static rendering
"use client" 會沿樹往下傳染:標在根附近整棵樹都變客戶端元件,要盡量往葉節點放
哪些元件可以是 server component?"use client" 該放哪裡?
不監聽事件、不改狀態的元件;標記盡量往葉節點放,因為客戶端範圍會沿子樹傳染
第 16 段 → #14 伺服器元件 · server components client component zero JavaScript

Every Frontend Architecture Pattern Explained in 23 Minutes

theSeniorDev 前端底層與系統設計 產生於 2026-09-21 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

判斷該不該搬到 SSR/Next.js:先問載入速度是否真的在傷轉換率或排名,有沒有數據
  1. 找出目前的 LCP / TTI 數字,以及它跟轉換率或搜尋排名的關係
  2. 列出搬過去要多養的東西:Node 執行環境、伺服器端錯誤類型、跟框架版本綁死的部署
  3. 只有效能是產品瓶頸(例如電商)才走;否則留在 CSR / SSG
  4. 記住作者的觀察:不少公司過早搬到 Next.js,現在正在往回退
今天就做 打開你專案的 Lighthouse 或 Web Vitals 報告,寫下 LCP 與 TTI,再用一句話回答「這數字有沒有傷到任何商業指標」——沒有就寫下「不搬 SSR 的理由」
第 8 段 → 渲染策略的代價與過早遷移的警告
替大型單體放進秩序:共用 UI/服務歸平台團隊,功能依領域切目錄與團隊
  1. 列出共用 UI 與邏輯:design system、全域 CSS、hooks、驗證
  2. 列出共用服務:auth、資料抓取、logging、analytics——交給平台團隊擁有
  3. 其餘依領域切:user、payments、billing 各一個目錄,功能團隊對應
  4. 把共用層當產品經營:有版本、有文件、有淘汰策略
  5. 用 lint 或 build 設定擋掉跨領域匯入,讓違規在 CI 失敗
今天就做 打開你專案的 src/,把每個頂層資料夾標成「共用」或「領域」,找出一個混在一起的(例如 utils 裡有 payments 邏輯),寫下該搬去哪
第 9 段 → 模組化前端單體:先畫出邊界
架構稽核:對自己的 code base 回答「現在是什麼、為什麼走到這、下一步是什麼、還用了哪些模式」
  1. 寫下目前的架構風格(對照本片六站)
  2. 寫下為什麼會走到這裡(解決了什麼問題)
  3. 寫下自然的下一步是什麼
  4. 列出還用到的其他模式:BFF?MVC?SSR?
  5. 把答案壓成一句三層敘述:渲染策略+程式碼組織+演化方向,每層附理由
今天就做 拿你現在的專案,用 10 分鐘寫出那句話:「我們用 ___ 處理 ___,因為 ___;repo 是 ___;規模成長時評估過 ___」——寫不出「因為」的那一格就是你要補的洞
第 12 段 → 架構稽核與資深的回答長什麼樣

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

「渲染發生在哪裡,決定了誰快、誰慢、誰耦合」是解釋整條演化的同一把鑰匙
新 MVC 的所有優缺點(SEO 好、首屏快、整頁重載)都來自「view 在伺服器」這一件事  ⇄  像 資料放在哪裡決定誰要跑遠路:離資料近的快,離資料遠的要來回
第 2 段 → 從靜態頁面到 MVC
BFF 與 GraphQL 常被混講,其實一個是「誰擁有 API 層」的組織決策,一個是「查詢長什麼樣」的技術決策
新 BFF(可用 REST 寫)與 GraphQL(可不經 BFF 直接給 client)是兩個獨立決定  ⇄  像 「誰負責這條 API」跟「這條 API 用什麼協定」在後端本來就是兩件事
第 5 段 → BFF 與 GraphQL:後端被切開
「效能最好但擴展性最差」不矛盾:效能量單一使用者速度,擴展性量流量成長時的成本曲線
新 SSR/RSC 效能最佳、擴展性反比純 CSR 差,因為預渲染需要伺服器基礎設施  ⇄  像 靜態檔在 CDN 的成本曲線幾乎水平;每請求跑一次 React 的伺服器曲線是斜的
第 8 段 → 渲染策略的代價與過早遷移的警告
「可以整段換掉 React」這個賣點在前端要打折:框架相關程式碼本身就是八成
新 clean architecture 在前端保住的純邏輯往往只是幾個計算函式,換框架仍要重寫八成  ⇄  像 後端的 clean architecture:UI 只是眾多入口之一,抽掉框架後核心邏輯仍佔大部分
第 10 段 → clean architecture 在前端值不值得

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

MVC:model 與 controller 在伺服器把資料混進模板吐出 HTML,互動要整頁重載
MVC
第 2 段 → 從靜態頁面到 MVC · MVC
SPA:畫面在瀏覽器建,後端退化成資料 API;前端工程師這個職位從此誕生
SPA
第 3 段 → SPA:邏輯搬到瀏覽器那一刻 · SPA client-side routing
BFF:每種前端各配一個專屬後端,把下游資料整成該畫面要的形狀,通常由前端團隊擁有
BFF
第 5 段 → BFF 與 GraphQL:後端被切開 · BFF
SSG:build 時把頁面產成靜態檔丟 CDN,極快極便宜;但零互動、內容改了要重 build
SSG
第 6 段 → SSG 與 ISR:回到伺服器產頁面 · SSG CDN
ISR:靜態頁帶 TTL,過期後由請求觸發重新產生並換掉 CDN 快取;仍撐不起真互動
ISR
第 6 段 → SSG 與 ISR:回到伺服器產頁面 · ISR TTL
SSR:每次請求在伺服器跑前端框架、產帶初始狀態的 HTML;看得到很快,能點還要等 hydration
SSR
第 7 段 → SSR、hydration 與 React Server Components · SSR
hydration:瀏覽器重跑一次元件邏輯,把事件與狀態接回伺服器送來的死 HTML
hydration
第 7 段 → SSR、hydration 與 React Server Components · hydration virtual DOM
RSC:沒互動的元件留在伺服器,只送需要互動的子集到瀏覽器;JS 少了,可互動時間提前
React Server Components
第 7 段 → SSR、hydration 與 React Server Components · React Server Components island architecture
modular monolith:仍單一部署,內部橫切共用層(平台團隊)、直切領域(領域團隊)
modular monolith
第 9 段 → 模組化前端單體:先畫出邊界 · modular monolith platform team domain-driven design
micro-frontend:領域真的拆成獨立部署的應用,shell 在使用者落地時把它們組起來
micro-frontend
第 11 段 → micro-frontend:企業級的最後一站 · micro-frontend shell application module federation vertical slice

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

MVC 六軸:擴展性最低、效能不佳、複雜度低、團隊小;適合 CRUD、小團隊、原型
六軸卡片:scalability 最低、performance 不佳、complexity 低、team size 小;適用 CRUD 應用、小團隊、快速原型;代價:整頁重載、前後端緊耦合、framework lock-in(Django / Rails / ASP.NET 換不掉)
證明 MVC
演練 這張卡證明了「渲染在伺服器」同時帶來哪些好處與代價?你現在的專案若是 CRUD 加小團隊,你會用什麼理由替 MVC 辯護?
第 2 段 → 從靜態頁面到 MVC
搬走的不只渲染,還有狀態所有權:瀏覽器養一份資料副本,快取/失效/樂觀更新變前端日常
AI 補充:伺服器渲染時畫面=資料當下樣子,沒有同步問題;SPA 在瀏覽器裡有副本,於是快取、失效、樂觀更新、race condition 全成前端工作——這是狀態管理工具大量出現的原因
證明 SPA
演練 這解釋了為什麼 SPA 時代冒出那麼多狀態管理工具?你的專案裡哪些 bug 其實是「副本跟伺服器不同步」造成的?
第 3 段 → SPA:邏輯搬到瀏覽器那一刻
ISR 通常「先給舊的、背景更新」,踩到過期的人拿到的仍是舊頁,下一位才看到新的
AI 補充:SSG 產生時機=build 時(一萬篇文章=一萬次渲染);ISR=第一個過期後的請求,且多為 stale-while-revalidate——這解釋「我改了資料庫但頁面沒變」的常見困惑
證明 ISR
演練 這證明了 ISR 的「動態」是哪一種動態?下次有人回報「改了資料庫頁面沒變」,你會怎麼用這點解釋?
第 6 段 → SSG 與 ISR:回到伺服器產頁面
SSR 時間軸:伺服器 HTML → 首次內容繪製 → 下載執行 JS → hydrate → 才能點;中間按了沒反應
投影片時間軸:render server HTML → FCP → download & execute JS → hydrate → interactive;空窗期使用者按按鈕沒反應
證明 hydration
演練 這條時間軸證明了「快」有兩種(看得到 vs 能點)?你會用它說服誰 SSR 不等於互動變快?
第 7 段 → SSR、hydration 與 React Server Components
門檻:上千名工程師在同一個 client 上工作才需要;小公司走這條路是自己搬石頭砸腳
六軸:擴展性、團隊規模、產品價值接近滿格;效能明顯掉(落地時才組合、可能各帶一份 React);複雜度與成本接近滿格。門檻=上千工程師同一 client
證明 micro-frontend
演練 這個門檻證明了 micro-frontend 解的是組織問題不是技術問題?面試被小公司問到時你會怎麼回答「什麼時候該用」?
第 11 段 → micro-frontend:企業級的最後一站

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

資深前端面試考的是架構取捨,不是寫程式;答不出取捨最多算中階
資深前端面試在架構題上真正評的是什麼?
能不能說出「為什麼選、代價是什麼、什麼條件下不選」——取捨,不是框架名
第 1 段 → 開場:面試考的是架構取捨
「SPA SEO 很差」今天要打折:搜尋引擎會跑 JS,但有延遲;社群預覽爬蟲多半不跑 JS
今天說「純 SPA 對 SEO 不好」的理由是什麼?
不是搜不到,是慢且不可靠:搜尋引擎執行 JS 有排程延遲;社群分享的預覽卡片爬蟲多半不執行 JS
第 3 段 → SPA:邏輯搬到瀏覽器那一刻
thin ↔ thick client 是一條軸:邏輯往前端搬越多越 thick,後端越像儲存空間
thin client 跟 thick client 差在哪?這條軸在講什麼?
thin:邏輯多在後端(靜態頁);thick:自己扛商業邏輯、後端當儲存。演化就是不斷把邏輯往前端搬,要問「該厚到什麼程度」
第 4 段 → thick client 與 thin client · thin client thick client
client 越厚的三個代價:邏輯不可信任、版本管理痛、但同一應用可逐畫面選厚度
決定 client 該多厚時要記住哪三件事?
1) 前端的驗證/權限只是體驗優化,後端一定要再做;2) 使用者可能跑三週前的舊版;3) 不是全有全無,可逐畫面選
第 4 段 → thick client 與 thin client
GraphQL:client 在查詢裡指定要哪些欄位,解 REST 的 over/underfetching,但帶來 N+1
GraphQL 解決什麼、又帶來什麼?
解 REST 的 overfetching / underfetching(client 指定欄位);帶來 N+1 與用圖思考的複雜度
第 5 段 → BFF 與 GraphQL:後端被切開 · GraphQL overfetching N+1 problem
N+1 是 GraphQL 每個欄位各自解析造成的;標準解法是 DataLoader 批次合併
GraphQL 的 N+1 問題怎麼來、怎麼解?
每個欄位各自 resolve,一百筆清單的關聯欄位就打一百次;用 DataLoader 把同一輪請求批次合併
第 5 段 → BFF 與 GraphQL:後端被切開 · N+1 problem
SSG 與 ISR 的共同前提:頁面對所有人都一樣;一出現「你好,某某」快取就沒法共用
什麼情況下 SSG/ISR 就失去意義?
內容因人而異(個人化)時,快取無法共用,必須走 SSR
第 6 段 → SSG 與 ISR:回到伺服器產頁面
SSR/RSC 兩個實務痛點:hydration mismatch(時間/隨機/window)與 use client 邊界只能傳可序列化資料
SSR 與 RSC 各有什麼作者沒講、實務會馬上遇到的痛?
SSR:hydration mismatch——依賴時間、隨機值、window 的程式碼兩端對不上;RSC:use client 邊界,跨界只能傳可序列化資料
第 7 段 → SSR、hydration 與 React Server Components
clean architecture:同心圓、依賴只能由外向內;前端多半用不上,值得留的是依賴方向這條規則
clean / hexagonal / onion architecture 的共同核心是什麼?前端該留下哪一條?
都是同心圓、依賴只能由外向內、最穩定的商業領域在中心;前端該留的是「讓不穩定的依賴穩定的」這條方向規則
第 10 段 → clean architecture 在前端值不值得 · clean architecture hexagonal architecture onion architecture dependency inversion
前端用不上 clean architecture 的四個理由(投影片)
投影片列的「前端通常不需要 clean architecture」四個理由?
框架本身很有意見、自帶結構;前端通常很薄;只有要換框架/需穩定商業邏輯才有用;最大價值其實是幫你思考解耦
第 10 段 → clean architecture 在前端值不值得
模組化單體的三個代價:全域狀態、極長 build 時間、邊界畫在圖上卻難遵守
模組化前端單體最常見的三個代價?
全域狀態、極長的 build 時間、邊界容易畫難遵守(相依到處長)
第 10 段 → clean architecture 在前端值不值得
共用得越多,獨立性越假:活得下來的 micro-frontend 只共用設計代幣與少數版本化元件
micro-frontend 的 shell 共用 CSS 與狀態藏著什麼矛盾?實務怎麼做?
共用越多越耦合、越要協調所有團隊;實務上只共用設計代幣與少數版本化元件,其餘寧可重複也不共用
第 11 段 → micro-frontend:企業級的最後一站
back-end rejection:面試當下都好,幾天後收到「找到更有經驗的人」的罐頭信
back-end rejection 是什麼?作者怎麼解讀它?
面試順利、事後沒理由被拒;不是做錯什麼,而是沒做夠對的事——所以架構故事要事先準備好
第 12 段 → 架構稽核與資深的回答長什麼樣 · back-end rejection

JavaScript Visualized - Promise Execution

Lydia Hallie JavaScript 執行模型 產生於 2026-09-21 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

promisify:把 callback 式 API 包進 new Promise,在 callback 裡 resolve/reject
  1. 找一個 callback 式的 API(setTimeout、fs.readFile、XMLHttpRequest)
  2. 寫 new Promise((resolve, reject) => { ... }),在 executor 裡呼叫那個 API
  3. 在它的 callback 裡:有資料就 resolve(data),出錯就 reject(err)
  4. 回傳這顆 promise,呼叫端改用 .then/await 接結果
  5. 檢查:executor 裡真的有一件離開主執行緒的工作嗎?沒有的話這顆 promise 只是繞一圈的同步值
今天就做 在你手上的專案裡找一個還在用 callback 的呼叫(或用 setTimeout 當替身),寫一個 sleep(ms) 或 readFileAsync(path) 的 promisify 版,並用 .then 印出結果
第 5 段 → executor 裡真正該放的東西 · promisify
逐格推演 setTimeout + then 的時間線:用四個框追蹤每一步誰進 call stack、誰排進哪個 queue
  1. 畫四個框:call stack、Web APIs、task queue、microtask queue
  2. new Promise 進 call stack 建物件;executor 同步跑,setTimeout 把 timer + callback 交給 Web APIs
  3. then 進 call stack 建 reaction record 後彈出;script 其餘同步碼繼續跑
  4. timer 到期,callback 落進 task queue;等 call stack 空(第一次)才進來
  5. callback 呼叫 resolve:改 state/result,handler 排進 microtask queue
  6. call stack 再空(第二次),handler 才被取出執行
今天就做 拿你專案裡任一段同時有 setTimeout 與 .then 的程式(沒有就寫 5 行),在紙上畫四個框,逐行標出每一步進出哪個框,最後對照實際 console 輸出
第 6 段 → setTimeout + then 的逐格執行 · setTimeout Web API call stack
推輸出順序的規則:同步 console.log 一定先印完,promise 的 then 才開始跑,跟 resolve 得多早無關
  1. 從上到下先只跑同步碼:console.log、new Promise 的 executor、resolve() 本身都是同步的
  2. 遇到 then:只登記(或已 settle 時直接排 job),絕不當場執行
  3. script 跑到底、call stack 空了,再清 microtask queue(promise handler,含鏈上每一環各一輪)
  4. microtask 清空後才拿 task queue 的第一個(setTimeout callback),跑完再回步驟 3
今天就做 自己寫一段 10 行內混合 console.log、同步 resolve、then 與 setTimeout(fn, 0) 的程式,先在紙上寫下預測順序,再跑一次比對;錯了就標出是哪一步漏算
第 8 段 → 小測驗:為什麼印出 1、3、2 · synchronous PromiseReactionJob

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

then 只負責「登記」,觸發由 resolve 負責,執行時機由 event loop 決定——三件事分開看
新 then 存 reaction record;resolve 把 handler 排進 microtask queue;event loop 決定何時取出執行  ⇄  像 餐廳取號:登記(拿號碼牌)、叫號(廚房出餐)、真的入座(輪到你)是三個不同時刻
第 3 段 → then 建立 promise reaction record

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

Promise 難的不是 then/catch 語法,而是三件看不見的事:物件狀態、callback 存哪、何時被拿出來跑
Promise
第 1 段 → Promise 不可怕,只是看不見底層 · Promise
executor 是傳給 new Promise 的 (resolve, reject) => {},同步執行,負責啟動工作並決定結果
executor function
第 2 段 → new Promise 建了什麼物件 · executor function resolve reject
then 的職責是建立一筆 reaction record 放進 fulfill reactions,handler 包著我們的 callback
Promise Reaction Record
第 3 段 → then 建立 promise reaction record · Promise Reaction Record then handler
microtask queue 放 promise handler,call stack 一空就先清它,清空才輪到 task queue
microtask queue
第 4 段 → 複習:microtask queue 優先於 task queue · microtask queue microtask
event loop:不斷檢查 call stack 空了沒、哪個 queue 有東西,把工作搬上 call stack
event loop
第 4 段 → 複習:microtask queue 優先於 task queue · event loop call stack task queue
asynchronous task:任何離開主執行緒、交給執行環境去做的事——讀檔、網路請求、timer
asynchronous task
第 5 段 → executor 裡真正該放的東西 · asynchronous task main thread
non-blocking 分兩層:等待本身由環境等、不佔 JS 執行緒;結果處理排進 microtask queue、不插隊打斷正在跑的程式
non-blocking
第 6 段 → setTimeout + then 的逐格執行 · non-blocking
then 回傳一顆新 promise,handler 的回傳值就是那顆 promise 的 result,所以能串成鏈逐步加工
promise chaining
第 7 段 → then 也回傳 promise:鏈式處理 · promise chaining then

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

勘誤:resolve/reject 是 constructor 造出來傳進 executor 的,不是 executor 提供的
作者說 resolve 是「executor function 提供給我們的」,方向相反:Promise constructor 造出 resolve/reject 兩個函式當參數傳進 executor;規格用 Promise Capability Record 把一顆 promise 和它專屬的兩個函式綁成一組,所以 resolve 只能改到自己那顆 promise
證明 executor function
演練 這個方向差證明了什麼——為什麼你不能在 executor 外面拿到某顆 promise 的 resolve?如果真的需要在外面 resolve(例如 deferred 模式),你會怎麼寫?
第 2 段 → new Promise 建了什麼物件
更精確的模型:每跑完一個 task 就把 microtask queue 整個清空,清空中新產生的 microtask 同輪繼續跑
AI 補充:不是「microtask 全清完才開始跑 task」而是「每個 task 之後清一次」;不斷產生 microtask 會餓死畫面更新。瀏覽器渲染排在清空 microtask 之後;Node.js 另有 process.nextTick 佇列,比 promise microtask 更優先
證明 microtask queue
演練 這個模型證明了為什麼一個無窮遞迴的 then 鏈會讓頁面卡死、而無窮遞迴的 setTimeout 不會?在 Node 裡你會什麼時候刻意選 process.nextTick 而不是 queueMicrotask?
第 4 段 → 複習:microtask queue 優先於 task queue
時間線裡有兩次「等 call stack 空」:一次讓 setTimeout callback 進來,一次讓 handler 進來,少算一次就算錯順序
第一次:等 script 跑完,callback 從 task queue 進 call stack;第二次:等 callback(含 resolve)跑完,handler 從 microtask queue 進來。Web APIs 那格放的是 Delay 100 + callback,持有 resolve 的是 callback,promise 自己不知道 timer 存在
證明 event loop
演練 這兩次等待證明了 promise 與 timer 之間是誰依賴誰?如果 clearTimeout 取消了 timer,那顆 promise 會怎樣?你會怎麼加 timeout 保護?
第 6 段 → setTimeout + then 的逐格執行
1 → 2 → 4 看起來一瞬間算完,其實三個 then 花三輪 microtask,鏈越長結果越晚出現
promise 用 1 resolve → 第一個 handler 算 result * 2 = 2 → 第二個拿 2 算出 4 → 第三個只 console.log 沒 return,它那顆 promise 的 result 是 undefined。截圖裡兩顆 Promise Object 並排:鏈上每個 then 都有自己的一顆 promise、自己的 state 與 result
證明 promise chaining
演練 「同一顆 promise」其實是一串——這證明了 then 鏈的每一環各自獨立 settle。你會怎麼用這點解釋為什麼 catch 放鏈尾能接住上游任一環丟出的錯?
第 7 段 → then 也回傳 promise:鏈式處理
小測驗印出 1、3、2:resolve(2) 同步改狀態,then 立刻排進 microtask queue,但 3 先印,script 結束後 2 才印
console.log(1) → resolve(2)(fulfill reactions 還是空的)→ then 建 record,promise 已 resolve 所以直接排進 microtask queue → console.log(3) → call stack 空,handler 取出印 2。截圖裡主控台只有 1、microtask queue 躺著 handler、then() 還在 call stack 上:「已排程、未執行」的畫面
證明 microtask queue
演練 這道題證明了「排程不等於執行」——你會怎麼用它解釋同事那句「我的 promise 明明已經 resolve 了,為什麼 then 裡的 log 還是最後才印」?
第 8 段 → 小測驗:為什麼印出 1、3、2

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

new Promise 建出的物件有五個 internal slot:state、result、兩種 reactions、is handled
promise 物件的五個 internal slot 是哪些?
[[PromiseState]]、[[PromiseResult]]、[[PromiseFulfillReactions]]、[[PromiseRejectReactions]]、[[PromiseIsHandled]]
第 2 段 → new Promise 建了什麼物件 · internal slot [[PromiseState]] [[PromiseResult]]
狀態只能單向轉一次:pending → fulfilled 或 rejected,之後再呼叫 resolve/reject 都沒效
已經 fulfilled 的 promise 再呼叫 reject 會怎樣?
完全沒效果:不報錯、不改 state、不改 result(狀態是單行道)
第 2 段 → new Promise 建了什麼物件 · [[PromiseState]]
[[PromiseIsHandled]]:串上 then/catch 才變 true;rejected 沒人接手就靠它判 unhandled
[[PromiseIsHandled]] 什麼時候從 false 變 true?它拿來判定什麼?
串上 then/catch 那一刻;用來判定 unhandled rejection(rejected 又沒人接手)
第 3 段 → then 建立 promise reaction record · [[PromiseIsHandled]]
fulfill reactions 是列表不是單格:同一顆 promise 可以串好幾個 then,每個放一筆
為什麼 [[PromiseFulfillReactions]] 是一個列表?
同一顆 promise 上可以串多個 then/catch,每個各放一筆 reaction record
第 3 段 → then 建立 promise reaction record · [[PromiseFulfillReactions]]
四個框:call stack 現在跑的、Web APIs 環境在等的、task queue 放 setTimeout、microtask 放 handler
setTimeout 的 callback 與 promise handler 各排進哪個 queue?
setTimeout → task queue(又叫 callback/macrotask queue);promise handler → microtask queue
第 4 段 → 複習:microtask queue 優先於 task queue · call stack task queue microtask queue
Promise 不會讓程式變多執行緒、不會讓同步重運算變快;只是把「等的工作何時完成」包成可掛 handler 的物件
Promise 能讓一段同步的重運算變快或不卡畫面嗎?
不能。等待是環境在等,JS 引擎自己算的東西照樣佔主執行緒;要用 Web Worker
第 5 段 → executor 裡真正該放的東西
setTimeout 的毫秒數是「最早何時可以進 task queue」,不是「何時執行」,延遲永遠只是下限
setTimeout(fn, 100) 保證 fn 在 100ms 時執行嗎?
不保證。100ms 只是最早進 task queue 的時間;call stack 還在忙就得繼續排隊,延遲只是下限
第 6 段 → setTimeout + then 的逐格執行 · setTimeout
忘記 return 是鏈式最常見的 bug:下游 then 拿到 undefined
then 的 handler 沒有 return,下一個 then 拿到什麼?
undefined——那顆新 promise 的 result 就是 handler 的回傳值
第 7 段 → then 也回傳 promise:鏈式處理 · undefined
handler 回傳一顆 promise(或 thenable)時,下游不會拿到「一顆 promise」,而是等它 settle 後取值
then 的 handler 回傳一顆 promise,下一個 then 拿到什麼?
不是那顆 promise,而是它 settle 後的值(吸收行為,讓 then 鏈能串接非同步步驟)
第 7 段 → then 也回傳 promise:鏈式處理 · thenable
在已經 settle 的 promise 上串 then:跳過存進列表,直接排一份 job,但仍至少延後一輪 microtask
對已經 resolve 的 promise 呼叫 then,handler 會同步執行嗎?
不會。跳過存列表那一步,直接排一份 PromiseReactionJob 進 microtask queue,仍延後至少一輪
第 8 段 → 小測驗:為什麼印出 1、3、2 · PromiseReactionJob
影片裡 [[PromiseState]]、Promise Reaction Record、job 這些名字直接取自 ECMAScript 規格
想確認 then 遇到已 settle 的 promise 究竟怎麼處理,該讀規格哪一節?
ECMAScript 規格的 PerformPromiseThen
第 8 段 → 小測驗:為什麼印出 1、3、2 · ECMAScript specification

Reactivity in Vue 3 - How does it work?

Vue Mastery 歷史參考(已過時) 產生於 2026-09-21 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

把 depsMap 圖翻成 track(key) / trigger(key):get → 沒有就建 → set 回去 → add
  1. const depsMap = new Map()
  2. track(key):let dep = depsMap.get(key);沒有就 depsMap.set(key, (dep = new Set()))
  3. dep.add(effect)
  4. trigger(key):const dep = depsMap.get(key);if (dep) dep.forEach(e => e())
  5. 主程式:track('quantity') → effect() → 改 product.quantity → trigger('quantity') 驗證 total 變化
今天就做 在你自己的專案開一個 scratch.js,不看影片、20 分鐘內手寫 depsMap 版的 track / trigger,用一個有兩個屬性的物件驗證:只 trigger 其中一個屬性時,另一個屬性的 effect 不會跑
第 8 段 → 把 depsMap 寫成程式碼 · lazy initialization
track / trigger 改收 (target, key):track 兩層「沒有就建」,trigger 兩層檢查加 early return
  1. const targetMap = new WeakMap()
  2. track(target, key):depsMap = targetMap.get(target);沒有就 targetMap.set(target, (depsMap = new Map()))
  3. dep = depsMap.get(key);沒有就 depsMap.set(key, (dep = new Set()));dep.add(effect)
  4. trigger(target, key):const depsMap = targetMap.get(target);if (!depsMap) return
  5. const dep = depsMap.get(key);if (dep) dep.forEach(e => e())
  6. 實測:track(product,'quantity') → effect() → product.quantity = 3 → trigger(product,'quantity')
今天就做 承上一筆的 scratch.js,加上 targetMap 那層並多建一個 user 物件;先在紙上畫出 targetMap → depsMap → dep 三層與各層 key,再照圖改程式,驗證 trigger(user, 'name') 不會動到 product 的 effect
第 10 段 → 把 targetMap 寫成程式碼 · early return

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

函式是 JavaScript 裡唯一能把「還沒發生的計算」當資料保存的東西
新 () => { total = price * quantity } 是一個值,可以存、可以之後再呼叫;敘述寫下去就執行完了  ⇄  像 食譜(可以收藏、之後照做)vs 做好的菜(吃完就沒了)
第 5 段 → effect、track、trigger 三個名字
closure 補上 eager evaluation 缺的另一半:effect 抓的是變數本身,重跑自然讀到新值
新 effect 是閉包,抓外層 price / quantity 變數本身,所以 trigger 後 total 變 15  ⇄  像 第 3 段的 let total = price * quantity 只存了當下的快照 10
第 6 段 → 用 Set 當儲存空間 dep · closure
track 遇到沒有就建立、trigger 遇到沒有就走人:登記與通知的預設立場天生相反
新 track 兩層都是「拿不到就現場建一個存回去」;trigger 兩層都是防禦性檢查、第一層直接 early return  ⇄  像 訂閱電子報:訂閱時沒名單就開一份;發送時名單不存在就代表沒人要收,直接不發
第 10 段 → 把 targetMap 寫成程式碼

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

Proxy 包住整個物件攔截所有讀寫,是 Vue 3 讓 track / trigger 自動發生的攔截器
Proxy
第 1 段 → 為什麼要懂 Vue 3 響應式 · Proxy
dependency:某段程式碼執行時讀到某值,值變時該程式碼必須重跑;依賴會串成鏈
dependency
第 2 段 → Vue 怎麼知道要更新畫面 · dependency computed property
eager evaluation:let total = price * quantity 當下算完只存結果,「關係」在賦值瞬間就用完
eager evaluation
第 3 段 → 純 JavaScript 沒有響應式 · eager evaluation
effect:被包成函式、要在它讀到的值改變時重新執行的那段計算
effect
第 5 段 → effect、track、trigger 三個名字 · effect anonymous function
track:把目前這個 effect 登記進某個值的 dep,表示值變時要重跑它
track
第 5 段 → effect、track、trigger 三個名字 · track
trigger:把某個值 dep 裡登記過的 effect 全部重新執行一次
trigger
第 5 段 → effect、track、trigger 三個名字 · trigger
dep:一個 Set,裝「這個值改變時要重跑的所有 effect」,名字是 dependency 縮寫
dep
第 6 段 → 用 Set 當儲存空間 dep · dep Set
depsMap:一個 Map,key 是屬性名稱、value 是該屬性的 dep,讓同物件的各屬性各自追蹤
depsMap
第 7 段 → 每個屬性各自一個 dep · depsMap Map
targetMap:最外層 WeakMap,key 是響應式物件本身、value 是它的 depsMap
targetMap
第 9 段 → 多個物件與 targetMap · targetMap target
WeakMap:key 只能是物件,且對 key 只持弱引用——沒人引用時可連同整張 depsMap 被回收
WeakMap
第 9 段 → 多個物件與 targetMap · WeakMap garbage collection

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

最小 app:改一次 price,模板兩處直接依賴 + 一處經 computed 間接依賴一起更新
data 只有 price、quantity;模板用了 price、price * quantity、以及依賴兩者的 computed totalPriceWithTax。改 price 後三個位置更新,第三處是先依賴 computed 再依賴 price 的鏈
證明 dependency
演練 這個例子證明了 Vue 的更新粒度是什麼(整頁還是用到的地方)?如果 Vue 不把 computed 當中間節點追蹤,第三處會發生什麼?
第 2 段 → Vue 怎麼知道要更新畫面
純 JS:price=5、quantity=2,total=10;改 price=20 再印 total 還是 10
let price = 5; let quantity = 2; let total = price * quantity → 10;price = 20 之後 total 仍是 10,語言本身沒有任何響應式
證明 eager evaluation
演練 這十行證明了響應式是誰的責任(語言還是框架)?從 total 存的是「結果」而非「關係」,你會怎麼推出「必須把計算包成函式」?
第 3 段 → 純 JavaScript 沒有響應式
模板三處讀 price,同一個渲染 effect 會被 track 三次;用陣列會重繪三次,Set 只留一份
重複登記在真實情境是常態:同一渲染 effect 被 track 多次。陣列會讓 price 改一次重繪三次,若 effect 又寫回自己讀的值可能滾成無窮迴圈;Set 把去重變成資料結構的天然性質
證明 dep
演練 這證明了為什麼 dep 選 Set 而不是 Array?如果你的專案有一個訂閱者清單常被重複註冊,你會怎麼套用同一個理由?
第 6 段 → 用 Set 當儲存空間 dep
console 實測:total 10 → quantity 改 3 仍 10 → 呼叫 trigger() → 15
let dep = new Set(); track = dep.add(effect); trigger = dep.forEach(e => e());改 quantity 後 total 不動,手動 trigger 後才變 15
證明 trigger
演練 這個實測證明了 trigger 做了什麼、沒做什麼?為什麼改 quantity 後不會自動變成 15,缺的是哪一半?
第 6 段 → 用 Set 當儲存空間 dep
targetMap 若用普通 Map:全域永遠活著,元件卸載一千次就洩漏一千份
targetMap 是全域、永遠存在的變數;用普通 Map 會把每個曾變成響應式的物件永久扣住,是教科書等級的 memory leak。作者只講了「key 必須是物件」,漏掉 weak 這個關鍵字
證明 WeakMap
演練 這證明了 WeakMap 名字裡的 weak 為什麼比「key 是物件」更重要?你專案裡有沒有以物件為 key 的全域快取,該不該換成 WeakMap?
第 9 段 → 多個物件與 targetMap

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

Vue 2 用 Object.defineProperty 逐屬性改寫,Vue 3 改用 Proxy 攔整個物件
Vue 2 與 Vue 3 各用什麼攔截屬性讀寫?差別在哪?
Vue 2:Object.defineProperty,初始化時逐屬性裝 getter/setter,事後新增的屬性偵測不到;Vue 3:Proxy 攔整個物件,動態屬性也攔得到
第 1 段 → 為什麼要懂 Vue 3 響應式 · Object.defineProperty Proxy
Vue 3 的響應式是獨立套件 @vue/reactivity,不裝 Vue 也能單獨用
Vue 3 響應式系統的獨立套件叫什麼?能不搭 Vue 用嗎?
@vue/reactivity;可以,單獨 npm 安裝即可
第 1 段 → 為什麼要懂 Vue 3 響應式 · @vue/reactivity
投影片的 new Vue({ el, data, computed }) 是 Vue 2 語法,機制講的是 Vue 3
投影片上 var vm = new Vue({ el: '#app', data, computed }) 是哪一版的寫法?
Vue 2;影片講的機制是 Vue 3,範例語法沒跟著換,不要照抄初始化程式碼
第 2 段 → Vue 怎麼知道要更新畫面
「自動更新」拆成兩個獨立能力:儲存該重跑的程式碼、值變時自動去跑;本課只做前者
作者把「自動更新」切成哪兩塊拼圖?本課做哪一塊?
儲存端(把該重跑的程式碼存下來,本課)與觸發端(值變時自動跑,下一課);所以 track / trigger 都還手動呼叫
第 4 段 → 從零打造響應式引擎
教學版是先 track() 再 effect(),真實 Vue 反過來:執行 effect 讀到值時順手 track
教學版與真實 Vue 的 track 時機差在哪?
教學版先登記再執行(手動);真實 Vue 在執行 effect 過程中,因為讀到 price 才順手登記
第 5 段 → effect、track、trigger 三個名字
Set 判重複用 ===:只有同一個函式參考才算重複
Set 判定重複的依據是什麼?兩個內容一模一樣、分別建立的箭頭函式算幾個 effect?
同一性(===);算兩個,Set 不會把它們合併
第 6 段 → 用 Set 當儲存空間 dep · Set
depsMap 的 key 用屬性「名稱」不用值:值會變,名稱在變前變後都指向同一位置
depsMap 為什麼用屬性名稱當 key 而不是屬性值?
值會變、名稱不會;改 quantity 時才找得回當初登記在 quantity 底下的 effect。這也是 Vue 追蹤粒度是「物件的某個 key」的原因
第 7 段 → 每個屬性各自一個 dep
用 Map 不用普通物件:key 任意型別、有 size、順序穩定、不撞 Object.prototype 名字
depsMap 為什麼用 Map 而不是普通物件?
key 可以是任意型別(下一層要用物件當 key)、有 size、迭代順序穩定、屬性叫 constructor / toString 時不會出事
第 7 段 → 每個屬性各自一個 dep · Map
depsMap.set(key, (dep = new Set())) 一行做三件事:建 Set、dep 指向它、存進 map
depsMap.set(key, (dep = new Set())) 這一行為什麼能同時完成三件事?
賦值本身是運算式、會回傳被賦的值:建立新 Set、讓區域變數 dep 指向它、把它存進 map
第 8 段 → 把 depsMap 寫成程式碼 · lazy initialization
trigger 裡的 if (dep):從沒被 track 的屬性 get 回 undefined,「沒人在等」是正常狀態不是錯誤
trigger 裡為什麼要先 if (dep) 才 forEach?
沒被 track 過的屬性 depsMap.get 回 undefined,直接 forEach 會爆錯;沒有任何人在等這個屬性是正常情況
第 8 段 → 把 depsMap 寫成程式碼
target 這個名字來自 Proxy 攔截函式的第一個參數:被代理的原始物件
targetMap 的 target 指什麼?為什麼叫這個名字?
被追蹤的原始響應式物件(product、user);Proxy handler 的 get / set 第一個參數就叫 target
第 9 段 → 多個物件與 targetMap · target Proxy
本課成品最大的人工成分:key 是手打字串,effect 讀了 product.price 卻沒人替 price 登記
做完三層結構後,為什麼改 product.price 還是沒反應?
track(product, 'quantity') 的 key 是手打的,effect 裡讀了 product.price 但沒人替 price 呼叫 track;而且每改一個值都要自己呼叫 trigger
第 10 段 → 把 targetMap 寫成程式碼
自動化還需要 activeEffect:模組層級變數記錄「此刻正在跑哪個 effect」,track 才知道登記誰
track 被 Proxy 自動呼叫時,怎麼知道要登記哪一個 effect?
靠模組層級的 activeEffect 變數:執行 effect 前設定、執行後還原,track 讀它
第 11 段 → 三層結構回顧與下一課 · activeEffect
教學版沒處理的三件事:巢狀 effect、重跑前清掉舊依賴、觸發丟進排程佇列去重
真實的 @vue/reactivity 比本課十幾行多處理了哪三件事?
巢狀 effect、每次重跑前清掉舊依賴、把 trigger 丟進排程佇列去重(不同步立即跑)
第 11 段 → 三層結構回顧與下一課

The ultimate guide to web performance

Beyond Fireship 歷史參考(已過時) 產生於 2026-09-21 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

最基本的 LCP 診斷:開 DevTools Network 看瀑布圖,找特別寬的那一條
  1. DevTools → Network,切成行動裝置模擬(375×667)再重新整理
  2. 看 Waterfall 欄,找特別寬的橫條
  3. 分辨寬的是哪一種:深色段長 = 檔案本身大;淺色段長 = 前面排太多東西在等
  4. 檔案大 → 壓縮/換格式;等太久 → 減少請求數或提早發現
今天就做 打開你自己專案的首頁,Network 面板用行動裝置模擬重新整理,寫下最寬的三條資源與它們是「檔案大」還是「排隊久」
第 2 段 → LCP:載入效能怎麼量
優化 LCP 三層:資源小一點、放近一點(CDN)、重要的早一點(preload / fetchpriority)
  1. 小:圖片壓縮、改 WebP,字型只留最低限度
  2. 近:靜態資源放 CDN(Firebase / Vercel 部署會自動做)
  3. 早:找出 LCP 元素,主圖加 fetchpriority="high",關鍵 CSS 用 <link rel=preload as=style>
  4. 拆掉擋在前面的 render blocking JS / CSS:關鍵 CSS 內嵌、其餘非同步
今天就做 在你的專案首頁找出 LCP 元素(通常是首屏主圖或大標題),對它做一件事:加 fetchpriority="high" 或轉成 WebP,再量一次 LCP
第 3 段 → 優化 LCP 的三層做法
減少主執行緒佔用:找出長任務,切小、讓出、搬走或延後
  1. DevTools Performance 錄一次載入,找紅色角標的 long task(> 50 ms)
  2. 大工作切小,中間 await scheduler.yield()(退而求其次 setTimeout 切片)
  3. 第三方腳本搬進 web worker(Partytown)
  4. 非首屏程式碼用 React.lazy 之類延後載入
今天就做 用 Performance 面板錄你專案首頁載入 5 秒,列出所有超過 50 ms 的 long task 和它們的來源檔案
第 4 段 → FID:畫得出來還要按得動
修 CLS:每張 img 給 width / height 或 aspect-ratio;晚到的區塊先佔位
  1. 找出沒有 width / height 的 <img>,補上(它只拿來算比例,搭 width:100%; height:auto 不衝突)
  2. 比例會隨視窗變時才用 srcset
  3. 廣告位、嵌入影片、非同步橫幅用固定高度或 aspect-ratio 佔位
  4. 動畫只動 transform 與 opacity,不動會重排的屬性
今天就做 在你的專案裡 grep 出所有沒寫 width / height 的 <img>,挑首屏那幾張補上尺寸屬性
第 5 段 → CLS:畫面不能亂跳
Web Vitals 擴充套件三步:安裝、設定開 console logging、開一頁看 console
  1. Chrome 安裝官方 Web Vitals 擴充套件
  2. 擴充套件設定打開 console logging
  3. 開你要看的頁面,console 印 [Web Vitals Extension] LCP 136 ms (good) 這類一行
  4. 展開 LCP 看肇因元素與四段拆解;展開 CLS 看造成位移的 DOM 元素(hover 會框起來)
今天就做 裝上 Web Vitals 擴充套件、打開 console logging,開你自己專案的首頁,抄下 LCP 元素是哪個 tag 和四段拆解各幾 ms
第 6 段 → Web Vitals 擴充套件:單頁診斷
Unlighthouse:npx unlighthouse --site <網址>,自動爬路由平行跑 Lighthouse 掃整站
  1. 開一個新資料夾
  2. npx unlighthouse --site https://你的站(網址一定要跟在 --site 後面)
  3. 它會自動爬出路由並依樣板分組,看即時網頁介面的四欄分數
  4. 找出「不掃根本不會發現」的爛頁面,先修最差的
  5. SDK 接進 CI:設分數門檻,PR 掉到門檻下就擋
今天就做 對你自己有公開網址的站跑一次 npx unlighthouse --site,把 Performance 最低的三個路由記下來
第 7 段 → Unlighthouse:整站掃描與實測

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

「小一點、近一點、早一點」就像物流:壓小包裹、就近倉儲、重要件優先出貨
新 LCP 優化三層:壓縮資源、CDN、preload / fetch priority  ⇄  像 電商物流:包裹壓小、把貨放在離客戶近的倉庫、急件先出
第 3 段 → 優化 LCP 的三層做法
aspect-ratio 修 CLS 修的不是圖片是版面預留——像餐廳先留座位,人到了不用擠別人
新 aspect-ratio / width+height 讓瀏覽器在內容還沒到時就算出高度佔位  ⇄  像 餐廳訂位:先把桌子留下,客人到了直接坐,不用把已經坐下的人趕走
第 5 段 → CLS:畫面不能亂跳

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

Core Web Vitals 把使用者感受拆成三格:看得到(LCP)、按得動(FID)、不亂跳(CLS)
Core Web Vitals
第 1 段 → 為什麼要在意初次載入效能 · Core Web Vitals
LCP:從開始載入到視窗內最大那塊內容畫出來的時間,代表「我看到這一頁了」
Largest Contentful Paint (LCP)
第 2 段 → LCP:載入效能怎麼量 · Largest Contentful Paint (LCP)
render blocking:必須先下載並執行完瀏覽器才肯繼續畫,預設 CSS 比 JS 更典型
render blocking JavaScript
第 3 段 → 優化 LCP 的三層做法 · render blocking JavaScript
FID:使用者第一次互動後,瀏覽器隔多久才開始跑對應的事件處理器;元兇是主執行緒被 JS 佔住
First Input Delay (FID)
第 4 段 → FID:畫得出來還要按得動 · First Input Delay (FID)
CLS:把頁面上所有非預期的位移量累加的分數;最大成因是沒指定尺寸的圖片
Cumulative Layout Shift (CLS)
第 5 段 → CLS:畫面不能亂跳 · Cumulative Layout Shift (CLS)
TTFB:從發請求到收到第一個位元組,是 LCP 四段拆解的第一段,佔大宗代表問題在伺服器或網路
Time to First Byte (TTFB)
第 6 段 → Web Vitals 擴充套件:單頁診斷 · Time to First Byte (TTFB)

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

Google 挑三個指標的標準:與跳出率相關性最高、而且開發者改得動
AI 補充:Core Web Vitals 訂成三個而不是十個,是挑「與 bounce 相關最高且前端改得動」的三件事;公開研究量級約「每多一秒轉換率掉個位數百分比」
證明 Core Web Vitals
演練 這條選擇標準證明了指標不是隨機湊的——如果你的老闆要求再加一個「總請求數」當 KPI,你會怎麼用這個標準回應?
第 1 段 → 為什麼要在意初次載入效能
Amazon 商品頁:333 個請求、28.7 MB,大多是 479 B 的 gif/ping beacon 靠數量吃光連線
行動裝置模擬下 DOMContentLoaded 2.83 秒、整頁 Finish 12.86 秒;瀑布圖上大多數條目是廣告與追蹤的 479 B gif / ping,每個都快但排隊把連線吃光
證明 Largest Contentful Paint (LCP)
演練 這張圖證明「寬」有兩種——你的專案裡哪一種比較可能?如果是排隊型,壓縮圖片會有幫助嗎?
第 2 段 → LCP:載入效能怎麼量
Next.js 這類 SSR 框架比純 React SPA 受青睞:SPA 初載入得先跑一堆 JS 才畫得出主要內容
SPA 只送一份幾乎空白的 HTML,主要內容靠瀏覽器端 JS 產生,LCP 被 JS 綁架;伺服器端渲染先送 HTML
證明 render blocking JavaScript
演練 這個例子證明架構選擇本身就是效能決策——你現在的專案是 SPA 還是 SSR?如果是 SPA,LCP 元素要等哪些 JS 跑完才會出現?
第 3 段 → 優化 LCP 的三層做法
FID 已於 2024 年被 INP 取代,且 Google 判定用真實使用者的第 75 百分位
INP 量整個頁面生命週期所有互動「從按下到畫面更新」取近乎最差的一次;判定不是你自己點一次快就算綠
證明 First Input Delay (FID)
演練 這條勘誤證明了指標本身會演進——你會怎麼跟同事解釋「FID 綠但 INP 紅」是怎麼發生的?
第 4 段 → FID:畫得出來還要按得動
fireship.io LCP 136 ms 拆成 TTFB 24、load delay 7、load time 0、render delay 105
LCP element 是 <img src="/courses/supabase/img/featured.webp">;105 ms 全壓在 element render delay,代表圖片早下載完、卡的是渲染,壓縮圖片或換 CDN 都沒用
證明 Largest Contentful Paint (LCP)
演練 這四段分佈就是診斷書——如果你的頁面是 TTFB 800 ms、render delay 30 ms,該改前端還是後端?
第 6 段 → Web Vitals 擴充套件:單頁診斷
實測:Google 極好、Amazon 不錯但 CLS 明顯、Reddit/GitHub 約 90、astro.build 99 分
astro.build SITE SCORE 99、36 個路由,Performance 100 / Accessibility 99 / Best Practices 98 / SEO 100;Amazon 147 個路由,範圍差四倍,都在模擬行動裝置與節流下跑
證明 Core Web Vitals
演練 這串排名證明了前面的原則在真實世界會產生可見分差——但 Amazon 147 路由對 astro 36 路由,這個比較哪裡不公平?
第 7 段 → Unlighthouse:整站掃描與實測

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

bounce:使用者進站沒任何互動就離開,效能變差時最先惡化的商業數字
bounce 指的是什麼?為什麼它跟效能最直接相關?
使用者一進站沒有任何進一步互動就離開;網站慢的第一個後果就是它上升
第 1 段 → 為什麼要在意初次載入效能 · bounce
LCP 門檻:2.5 秒內算好、超過 4 秒算差
LCP 的 good / poor 門檻各是幾秒?
2.5 秒內 good;超過 4 秒 poor
第 2 段 → LCP:載入效能怎麼量
瀑布圖顏色:淺色段是排隊與等待,深色段才是實際傳輸
DevTools 瀑布圖的淺色段與深色段各代表什麼?
淺色 = 排隊/等待;深色 = 實際傳輸。決定你該壓檔案還是減請求數
第 2 段 → LCP:載入效能怎麼量
preload 的 as 屬性不能省:少了瀏覽器不知道優先權,還可能重複下載
<link rel="preload"> 為什麼一定要寫 as?
沒有 as 瀏覽器不知道用什麼優先權抓、可能重複下載一次,等於白做;例:as="style"、as="script"、as="video" type="video/mp4"
第 3 段 → 優化 LCP 的三層做法 · preload
fetchpriority 寫在 img / script / link 上,值是 high | low | auto
LCP 主圖要提早,該用 preload 還是 fetchpriority?
直接在 <img> 上寫 fetchpriority="high";preload 濫用會讓所有東西都高優先權等於沒有
第 3 段 → 優化 LCP 的三層做法 · fetch priority
FID 門檻:100 毫秒內算好、300 毫秒算差
FID 的 good / poor 門檻是多少?
100 ms 內 good;超過 300 ms poor
第 4 段 → FID:畫得出來還要按得動
long task:單次佔用主執行緒超過 50 ms 的工作,才是互動延遲的真正兇手
long task 的定義是幾毫秒?
單次佔用主執行緒 > 50 ms
第 4 段 → FID:畫得出來還要按得動
Partytown 把第三方腳本搬進 web worker;Qwik 靠 resumability 連 hydration 都省
Partytown 和 Qwik 各解決 FID 的哪個時機?
Partytown:搬走(第三方腳本進 web worker);Qwik:初載入幾乎不執行 JS,互動時才按需取回
第 4 段 → FID:畫得出來還要按得動 · Partytown Qwik
CLS 門檻 0.1 以內好、超過 0.25 差;使用者互動後 500 ms 內的位移不計分
CLS 的門檻是多少?點按鈕展開選單會扣分嗎?
0.1 內 good、> 0.25 poor;互動後 500 ms 內的位移豁免,所以展開選單不扣、廣告自己插進來會扣
第 5 段 → CLS:畫面不能亂跳
動畫安全屬性:transform 與 opacity 在合成階段處理,不觸發重排
動畫要動哪兩個屬性才不會產生 CLS?
transform 與 opacity——合成階段處理、不重排
第 5 段 → CLS:畫面不能亂跳
擴充套件量到的是你這一台、這一次的單次觀測;Google 判定用真實使用者 p75
Web Vitals 擴充套件的數字能當驗收成績嗎?
不能,它是單機單次觀測,用來定位問題;Google 用真實使用者第 75 百分位判定
第 6 段 → Web Vitals 擴充套件:單頁診斷
Astro:預設不送 JavaScript,建置時渲染成 HTML,只有需互動的元件才單獨載入
Astro 為什麼幾乎滿分?它的核心原則是什麼?
預設不送 JS:建置時就渲染成 HTML,只有需要互動的元件才載入
第 7 段 → Unlighthouse:整站掃描與實測 · Astro
把效能變成流程:Unlighthouse SDK 接進 CI,設分數門檻擋 PR
怎麼讓效能從一次性檢查變成持續防線?
Unlighthouse SDK 接進 CI,設一條分數門檻,PR 掉到門檻以下就擋
第 7 段 → Unlighthouse:整站掃描與實測 · Continuous Integration (CI)

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

加 CSP:回應時多一個 Content-Security-Policy header,或 HTML 裡 meta http-equiv
  1. server 回應加 header:Content-Security-Policy: <directive> <value>; <directive> <value>
  2. 語法是「directive 空格 value」,分號分隔下一組——不是 JS 的 key: value
  3. 沒法改 server 時用 <meta http-equiv="Content-Security-Policy" content="…">(frame-ancestors / report-uri / sandbox 在 meta 裡無效)
  4. 先用 Content-Security-Policy-Report-Only 觀察,再切成強制
今天就做 在你手上任一個會回 HTML 的專案(或 python -m http.server 加一頁)加上 Content-Security-Policy-Report-Only: default-src 'self',開 DevTools Console 看哪些資源被回報
第 5 段 → 怎麼設:HTTP header 或 meta 標籤
讀 MDN 範例逐步建立自己的 policy,不用背 directive
  1. 從 default-src 'self' 開始
  2. 看 Console 的違規回報,每一條決定:搬回自己的 origin,或加該類型的 directive 白名單
  3. 只加真的需要的 origin,不用 *
  4. frame-ancestors / form-action / base-uri 另外寫,它們不從 default-src 繼承
今天就做 承上一筆的 Report-Only 結果,把每條違規改成一個具體 directive,寫出你那頁最小可行的 policy 字串
第 6 段 → MDN 文件:default-src 與 'self'

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

「來自自己 server 的東西都安全」這個信念,就像家裡人帶進門的東西不檢查
新 瀏覽器不區分「你寫的 script」與「別人塞進來的 script」,只要從你的頁面出來就執行  ⇄  像 只認門禁卡不認人:卡是你的就放行,不管是誰拿著
第 3 段 → 為什麼會執行:文字被當成 HTML
CSP 是「就算進來了也跑不了」,跟過濾輸入是兩層
新 CSP:限制瀏覽器能從哪載入、能執行什麼  ⇄  像 縱深防禦:門鎖(過濾輸入)之外還有保險箱(限制執行)
第 4 段 → CSP 的核心想法:白名單 origin
default-src 像 switch 的 default 子句
新 default-src:找不到該資源類型的 directive 時就用它  ⇄  像 switch 語句的 default 分支
第 6 段 → MDN 文件:default-src 與 'self'

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

CSP 是 server 透過 header 告訴瀏覽器「哪類資源只能從哪些 origin 載入」的規則
Content Security Policy (CSP)
第 1 段 → 本片議程:CSP 為了防 XSS · Content Security Policy (CSP)
XSS:攻擊者的 script 在別人的瀏覽器裡執行,利用的是瀏覽器信任「來自該網站的內容」
Cross-site scripting (XSS)
第 1 段 → 本片議程:CSP 為了防 XSS · Cross-site scripting (XSS)
innerHTML 把字串解析成 HTML 節點——用它顯示使用者輸入就是把資料當程式
innerHTML
第 3 段 → 為什麼會執行:文字被當成 HTML · innerHTML
Stored XSS:payload 存在 server 的 DB,之後每個瀏覽該內容的人都中
Stored XSS
第 3 段 → 為什麼會執行:文字被當成 HTML · Stored XSS
Allowlist:明確列出可載入資源的 origin,其餘一律拒絕;防禦從「別讓壞東西進來」變成「進來了也跑不了」
Allowlist
第 4 段 → CSP 的核心想法:白名單 origin · Allowlist
Directive:CSP 的組成單位,一個資源類型(或行為)加允許的來源清單
Directive
第 5 段 → 怎麼設:HTTP header 或 meta 標籤 · Directive
default-src:某類資源沒專屬 directive 時用的預設來源清單
default-src
第 6 段 → MDN 文件:default-src 與 'self' · default-src
script-src:限制 JS 可從哪些來源載入與執行,防 XSS 最關鍵的 directive
script-src
第 6 段 → MDN 文件:default-src 與 'self' · script-src
Origin:scheme://host:port 三者都相同才算同一個,瀏覽器安全模型的基本單位
Origin
第 6 段 → MDN 文件:default-src 與 'self' · Origin
HSTS:告訴瀏覽器在指定期間內連這個網域一律用 https;跟 CSP 分工不同
HSTS
第 8 段 → 附帶:只允許 https、搭配 HSTS · HSTS

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

留言 DB 裡並排兩則:{text:"nice"} 與 {text:"<script src=evil.js>"}
資料庫內容 [{text: "nice", userId: 5}, {text: "<script type='text/javascript' src='https://myevilwebsite.com/evilscript.js'/>", userId: 3}];攻擊者留的是「外部 script」而非內嵌碼
證明 Stored XSS
演練 這則留言證明了 stored XSS 的什麼特性(一次性還是持續性)?攻擊者選「外部 script」而不是內嵌碼,對後面 CSP 擋不擋有什麼影響?
第 2 段 → 情境:YouTube 留言裡放一個 script 標籤
CSP 預設也擋 inline script 與 eval,所以嚴格政策不只是白名單 origin
AI 補充:真正嚴格的 CSP 還包括「不允許 inline」;頁面有大量 inline script 時,導入 CSP 要先搬到外部檔或加 nonce
證明 Allowlist
演練 為什麼「只白名單 origin」不夠?inline script 跟 origin 白名單的關係是什麼?你的專案有哪些 inline script 會先被擋?
第 4 段 → CSP 的核心想法:白名單 origin
MDN Example 3:default-src 當後盾,img/media/script 各自覆蓋
圖片任意、媒體限兩站、script 限一站,其餘退回 'self'——「default 當後盾、個別 directive 覆蓋」的示範
證明 default-src
演練 在 Example 3 裡,字型會從哪裡被允許載入?為什麼?如果要加一個 CDN 的字型你會改哪一個 directive?
第 6 段 → MDN 文件:default-src 與 'self'
SPA 導入 CSP 的主要阻力是打包工具產生的 inline script / style
AI 補充:webpack runtime chunk、CSS-in-JS 注入的 style 會被擋,需要 nonce 或抽成外部檔;Next.js、Angular 有內建 nonce 支援
證明 Content Security Policy (CSP)
演練 為什麼「有回傳 HTML 就該加 CSP」在 SPA 上實作起來比 SSR 麻煩?你的框架怎麼處理 nonce?
第 7 段 → 實務:任何前端都該加

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

User-generated content(留言、暱稱)是「任何人都能寫入」的地方,XSS 最常見的注入入口
為什麼留言、暱稱這類 user-generated content 是 XSS 最常見的入口?
任何人都能寫入,且會被 server 存起來顯示給所有人
第 2 段 → 情境:YouTube 留言裡放一個 script 標籤 · User-generated content
Reflected XSS:payload 放在 URL/參數,server 未跳脫地回顯;受害者要點惡意連結
reflected XSS 的 payload 在哪?受害者要做什麼才會中?
在 URL/請求參數裡,server 未跳脫地回顯;受害者要點惡意連結
第 3 段 → 為什麼會執行:文字被當成 HTML · Reflected XSS
DOM-based XSS:前端 JS 自己把 URL 片段寫進 DOM,server 從頭到尾沒碰過 payload
DOM-based XSS 跟 server 的關係是什麼?
server 從頭到尾沒碰過 payload,是前端 JS 自己把 URL 片段(如 location.hash)寫進 DOM
第 3 段 → 為什麼會執行:文字被當成 HTML · DOM-based XSS
三種 XSS 的注入路徑
stored / reflected / DOM-based XSS 的 payload 各在哪裡?
stored:server DB;reflected:URL/請求參數,server 原樣回顯;DOM-based:前端 JS 自己寫進 DOM,server 沒碰過
第 3 段 → 為什麼會執行:文字被當成 HTML
innerHTML 與 textContent 的差別
顯示使用者輸入時 innerHTML 和 textContent 差在哪?
innerHTML 會把字串解析成 HTML 節點(會執行);textContent 只當純文字
第 3 段 → 為什麼會執行:文字被當成 HTML
origin 的定義
瀏覽器判斷「同一 origin」要哪三樣都相同?
scheme、host、port
第 3 段 → 為什麼會執行:文字被當成 HTML
CSP header 語法
Content-Security-Policy header 的值怎麼寫?
<directive> <value>; <directive> <value>——directive 空格 value,分號分隔
第 5 段 → 怎麼設:HTTP header 或 meta 標籤
meta 版 CSP 無效的 directive
哪些 directive 放在 meta http-equiv 裡無效?
frame-ancestors、report-uri、sandbox
第 5 段 → 怎麼設:HTTP header 或 meta 標籤
'self':CSP 關鍵字,代表當前文件的 origin,要加單引號
CSP 裡的 'self' 代表什麼?寫法要注意什麼?
當前文件的 origin(同 scheme、host、port);要加單引號
第 6 段 → MDN 文件:default-src 與 'self' · 'self'
default-src 不涵蓋的 directive
哪些 directive 不從 default-src 繼承?
frame-ancestors、form-action、base-uri(default-src 只是 fetch 類的預設)
第 6 段 → MDN 文件:default-src 與 'self'
upgrade-insecure-requests:CSP directive,把頁面內所有 http:// 子資源自動改成 https://
upgrade-insecure-requests 這個 directive 做什麼?
把頁面內所有 http:// 子資源請求自動改成 https://
第 8 段 → 附帶:只允許 https、搭配 HSTS · upgrade-insecure-requests
CSP 與 HSTS 的分工
CSP 和 HSTS 各管什麼?頁面第一次以 http 被載入誰擋?
CSP 管頁面內子資源用什麼協定載入;HSTS 管瀏覽器以後連這個網域一律 https。第一次 http 載入 CSP 擋不了,靠 HSTS + server 轉址
第 8 段 → 附帶:只允許 https、搭配 HSTS
本片沒講、實務會馬上遇到的三件事
導入 CSP 時本片沒講但會馬上遇到的三個東西?
inline script/style 被擋(nonce 或 hash)、Report-Only 模式與 report-to、'strict-dynamic'

How to Remember Everything You Read

Justin Sung 學習方法 產生於 2026-09-21 開啟文字解析 🎧 語音解析 🔗 在 YouTube 看

P 程序→ 練習怎麼做的資訊:盡早在真實情境用,硬背會白費。

消費期邊讀邊辨識類別,消化期用該類流程處理
  1. 讀的時候每遇到一筆資訊先問:這是 P/A/C/E/R 哪一類?
  2. 當下只做該類的「消費期動作」(P 記下要練什麼、A 找已知、C 加到地圖、E/R 存起來)
  3. 讀完另撥時間做該類的消化動作
今天就做 拿你手上正在讀的下一篇文章(或這站的 plan.html 第 9 段),在邊緣寫每一段的 PACER 字母,讀完數一數各幾筆
第 4 段 → PACER:五種資訊類別
程序性資訊一讀進來就盡早在真實情境練,不要當場硬背
  1. 辨識出這是「怎麼做」的資訊
  2. 當天或最近一次真實情境就用一次
  3. 現在沒時間練 → 換去讀別的,或停止吸收,等有時間再練
  4. 絕不為了記它反覆重讀、狂抄筆記
今天就做 今天挑一件你剛從影片或文件學到的「怎麼做」(例如某個指令、快捷鍵、寫法),在你自己的專案裡實際用一次,不看筆記
第 5 段 → P = Procedural → 立刻練習
批判類比:具體哪裡像、哪裡不像、什麼情況失效、要不要換
  1. 讀的時候問「這跟我已知的什麼有關?」
  2. 寫下新事物與已知事物
  3. 列:哪裡像 / 哪裡不像 / 什麼情況下類比失效
  4. 決定保留、修改或換一個類比
今天就做 對這一站的第 11 筆(暴食類比)先自己寫三格,寫完再按「對照 AI 版」
第 7 段 → A = Analogous → 批判類比
mapping:邊讀邊畫非線性的網路式筆記,邊加邊重組
  1. 讀到概念就當一個節點寫上去
  2. 問「它跟哪個已在圖上的節點有關?」畫線並寫關係
  3. 新節點進來就重組,不怕搬動
  4. 類比也畫進去
  5. 沒時間畫就放慢、少讀
今天就做 打開這站的 tldraw 畫布(/learn-canvas)或一張白紙,只憑記憶把 consumption、digestion、PACER 五類、balance 畫成一張圖並連線,畫完再對這裡 C 類的答案
第 9 段 → C = Conceptual → 畫地圖
證據:讀的當下立刻存,之後另撥時間演練怎麼用
  1. 辨識出這是支持某概念的細節
  2. 當下存:加進概念地圖、second brain 或 flashcard
  3. 一天或一週結束時:想它是哪個概念的例子、要怎麼用
  4. 實際用:解題、寫詳細答案、教別人、寫文章拿它當例子
  5. 當下不要為了記它反覆重讀
今天就做 把這一站 E 類的 4 筆各做一次「演練」(回答 rehearse_q),一筆兩三句就好
第 10 段 → E = Evidence → 儲存與演練
參考性資訊丟進 flashcard,每天 30 分鐘間隔重複回想
  1. 讀到時判定:不改變概念理解、不是類比、不是程序、但之後要查 → R
  2. 當下丟進 flashcard(Anki 或這裡的 R 卡)
  3. 每天撥 30 分鐘翻一輪到期的卡
  4. 絕不在讀的當下反覆重讀來背它
今天就做 按這頁上方的「開始回想」把今天到期的 R 卡翻完;沒有到期的就把這站的 R 卡先過一輪
第 11 段 → R = Reference → 用 Anki 直接回想

A 類比→ 批判類比跟已知很像的資訊:找出哪裡像、哪裡不像、何時失效,新知識才接得上舊網路。

沒消化就多讀 = 學習版的暴食,吐出來就是遺忘
新 consumption 過多、digestion 不足時知識留不住  ⇄  像 暴食:吃太多沒消化就吐掉
第 6 段 → 消費與消化必須平衡
游泳者讀肌肉收縮週期時想到自己的划水技巧
新 肌肉收縮週期(生理學)  ⇄  像 自己的划水動作(運動經驗)
第 7 段 → A = Analogous → 批判類比
「不自然」的學法反而有效,因為自然的讀法會超過腦的上限
新 PACER 的刻意動作  ⇄  像 任何需要刻意練習才會的技能(例如正確的跑姿)
第 8 段 → 不自然,所以有效

C 概念→ 畫地圖是什麼/為什麼的資訊:知識是網路不是清單,自己畫出概念間的連結。

閱讀分成 consumption(讀進來)與 digestion(留下來)兩個階段
consumption
第 2 段 → 消費期與消化期 · consumption digestion
digestion 才決定留下多少;多數人把力氣全押在讀快讀多
digestion
第 2 段 → 消費期與消化期 · digestion
PACER:讀到的資訊分五類,每類有專屬的消化流程
PACER
第 4 段 → PACER:五種資訊類別 · PACER
procedural = 教你「怎麼做」的資訊,消化流程是 practice
procedural
第 5 段 → P = Procedural → 立刻練習 · procedural practice
balance:吸收的每一樣都必須被消化才會留下;沒時間消化就少讀
balance
第 6 段 → 消費與消化必須平衡 · balance (consumption vs digestion)
analogous = 跟既有知識有關、讓你聯想到已知事物的資訊,消化流程是 critique
analogous
第 7 段 → A = Analogous → 批判類比 · analogous analogy critique
conceptual = 「是什麼」的資訊:事實、解釋、理論、原則、概念間關係;消化流程是 mapping
conceptual
第 9 段 → C = Conceptual → 畫地圖 · conceptual mapping
knowledge network:專家腦中的概念是高度連結的網路,沒有固定順序
knowledge network
第 9 段 → C = Conceptual → 畫地圖 · knowledge network
evidence = 讓概念變具體的細節、統計、案例;流程是 store and rehearse
evidence
第 10 段 → E = Evidence → 儲存與演練 · evidence store and rehearse
reference = 瑣碎、具體、不改變理解、但之後可能要查的資訊;流程是 store + 直接回想
reference
第 11 段 → R = Reference → 用 Anki 直接回想 · reference spaced repetition

E 證據→ 存+演練支持概念的細節:當下存起來,之後想它證明了什麼、你會怎麼用。

Kim Peek 能逐字背整本書,但推理與解題很差
Kim Peek(FG syndrome)可逐字默寫整本書、背地圖算最短路徑;作者的思想實驗:低年級背誦考他贏,大學以上推理考你反而有機會
證明 digestion
演練 Kim Peek 的例子證明了「記住一切」和「學會」差在哪?如果有人說「我想全部記住」,你會怎麼用這個例子回他?
第 3 段 → Kim Peek:記住一切不是目標
研究顯示讀完可忘掉高達九成
作者引用研究:讀完之後最多可忘掉 90%
證明 balance
演練 「忘掉九成」這個數字支持了作者哪一個主張?它跟「少讀一點、多消化一點」的關係是什麼?
第 6 段 → 消費與消化必須平衡
教科書是線性的,寫書的專家腦中卻是網路
教科書一句接一句;專家可從任一點導航到任一點,這正是專家解複雜問題、初學者只看到零散概念的差別
證明 knowledge network
演練 「教科書線性、專家腦中是網路」證明了學習者的任務是什麼?為什麼逐字記順序沒用?
第 9 段 → C = Conceptual → 畫地圖
聽心音是程序,知道聽到的是什麼是概念,診斷兩者都要
會聽心音(procedural)還得知道聽到的是什麼(conceptual)才能診斷
證明 conceptual
演練 在你自己的領域舉一個「程序+概念缺一不可」的例子
第 9 段 → C = Conceptual → 畫地圖

R 參考→ 存+回想瑣碎但之後要查的:存進 flashcard,間隔重複回想。

Kim Peek 的病名
作者用哪個罕見疾病解釋 Kim Peek 的超常記憶?
FG syndrome
第 3 段 → Kim Peek:記住一切不是目標
passive mode of reading 的定義
作者說的 passive mode of reading 是什麼狀態?
讀到頁面底部卻不記得剛剛讀了什麼
第 4 段 → PACER:五種資訊類別
biological limitations:人腦一次能吸收並存進記憶的量有上限
作者說「不自然的學法反而有效」的生物學根據是什麼?
人腦一次能吸收並存進記憶的量有上限(biological limitations),超過就被淹沒
第 8 段 → 不自然,所以有效 · biological limitations
second brain 的代表工具
作者說 second brain 可以用哪些工具?
Notion、Roam、Obsidian(或 flashcards、隨便一份文件)
第 10 段 → E = Evidence → 儲存與演練
E 與 R 的差別
evidence 和 reference 儲存方式一樣,差別在哪一步?
演練(rehearse):E 要想怎麼用、當例子;R 只需直接回想事實
第 11 段 → R = Reference → 用 Anki 直接回想
作者說最糟的浪費
作者說學習裡「最糟的浪費之一」是什麼?
讀的當下為了背參考性資訊反覆重讀、狂寫筆記
第 11 段 → R = Reference → 用 Anki 直接回想
second brain:外部筆記系統,E 與 R 的儲存位置
second brain 在 PACER 裡扮演什麼角色?
E 與 R 兩類的儲存位置(Notion / Roam / Obsidian / flashcards)
第 10 段 → E = Evidence → 儲存與演練 · second brain
encoding:把資訊處理後存進長期記憶
encoding 在 PACER 系統裡指什麼?
消化流程的結果:把資訊處理後存進長期記憶
第 4 段 → PACER:五種資訊類別 · encoding