技術演進的真正問題

技術演進史通常被寫成時序:「2010 年 Express,2016 年 NestJS,2022 年 Hono⋯⋯」,彷彿新的自然取代舊的。

但這個敘述跳過了最重要的問題:為什麼? 什麼問題出現了、不解決不行、才讓新的框架有存在的理由?

版本號和年份是結果,極限才是驅動力。


jQuery → React / Vue:每人寫法不一樣撞牆了

jQuery(2006)解的是 DOM 操作的跨瀏覽器相容問題。它成功了——到 2010 年代初,幾乎每個前端都有 jQuery。

但 jQuery 解決的是操作問題,沒有解決結構問題。1000 行的 jQuery code 每個人長得不一樣:有人 callback nested 5 層、有人把 state 存在 DOM 的 attribute 裡、有人把 AJAX 的結果直接渲染 HTML 字串。

這在小型專案無所謂。但 2012 年之後,web 應用越來越複雜,Facebook 的 news feed、Gmail 的 inbox 不是靠 jQuery 的 $('#elem').html(...) 能維護的規模。

撞牆點:沒有組件化,大型 UI 維護到 10k 行是一團亂;沒有狀態管理,多個地方更新同一塊 UI 就有 consistency 問題。

React(2013)和 Vue(2014)帶來的是組件(component)+ 單向資料流。不是「更新 DOM 的更好方式」——而是「讓 UI 可以被組合、狀態管理有明確流向」的架構模式。jQuery 和 React 解的不是同一個問題。


Express → NestJS:大型 Node.js 系統每個人架構長得不一樣撞牆了

Express(2010)解的是 Node.js 生態還沒成熟時缺少的東西:路由和 middleware 的基礎設施。它成功了。

但 Express 的 minimal 哲學讓每個 Express 專案的結構完全不同。5 人以下的小團隊沒問題,但 50 人一起改同一個 Express codebase 就會出問題:

  • 有人把邏輯寫在 controller、有人寫在 route、有人寫在 middleware
  • 服務之間的依賴沒有明確的注入機制,測試很難寫
  • 新人進來不知道從哪裡開始讀

撞牆點:Express 沒有強制架構 convention,大型系統的 onboarding 成本高、可維護性低。

NestJS(2017)直接借 Angular 的 Module / Injectable / Controller / Guard 架構——不是因為 Angular 是後端框架,而是 Angular 有一套讓大型應用維護的架構語言。NestJS 把這套語言帶進 Node.js 後端,代價是學習曲線高;好處是任何 NestJS 專案的結構都長得一樣。


Django → FastAPI:同步 ORM 的 I/O 等待撞牆了

Django(2005)設計時,web server 的主流並發模型是 thread-per-request(WSGI)。每個請求一個 thread,等資料庫就阻塞這個 thread。

在 2010 年以前,多數 web 應用的並發量不高,這個模型夠用。但 2015 年之後,API 設計開始常見一個 pattern:一個請求要去查多個資料庫、呼叫多個外部 API,然後組合結果回傳。每個 I/O 操作都要等,thread 就阻塞了,要撐 1000 QPS 就要起幾百個 thread——記憶體和 context switch 成本讓機器撐不住。

撞牆點:Django 的同步 ORM 在 I/O 密集的高並發 API 場景,thread-per-request 的效率上限太低。

FastAPI(2018)從第一天就是 async-first(ASGI),Pydantic 做型別安全,OpenAPI 文件自動生成。解的不只是效能——還同時解了 Django REST Framework 的繁瑣 serializer 寫法,和 API 文件要手工維護的問題。


Meta-framework 崛起:框架本身的 DX 撞牆了

React 解了大型 UI 的組件化問題,但 React 本身只是 UI library,不管路由、資料取得、server-side rendering、部署。

2017–2019 年,「如何用 React 做一個完整的 web 應用」這個問題沒有標準答案——每個團隊的 create-react-app 應用的路由方案(React Router v3/v4/v5 之間版本差異)、資料取得方式(fetch / axios / React Query / Redux)、SSR 策略都不一樣。

撞牆點:React / Vue 只解了 UI 問題,沒有解決「完整應用的 convention 在哪裡」的問題;使用者不知道 best practice 在哪、DX 不好、SEO 需要 SSR 而純 CSR 做不到。

Next.js(2016)和 Nuxt(2016)帶來的是 Meta-framework——在 React / Vue 上面加一層 convention:路由怎麼定義(檔案系統路由)、資料怎麼取(getServerSideProps / loader)、靜態生成怎麼做。

不懂 Node.js 去玩 Next.js 出問題不知道怎麼修,是因為 Next.js 的 server component、server action、API route 本質上就是 Node.js server——只是 convention 幫你藏起來了。遇到邊界問題時,不懂 event loop、不懂 CORS、不懂 HTTP header 的人就會看到一個完全看不懂的錯誤訊息。


驅動力的核心邏輯

梳理這幾個案例,每次演進的驅動力都在這幾個維度之一:

可維護性撞牆:系統夠大之後,現有的架構讓多人協作成本太高(jQuery → React;Express → NestJS)。

效能撞牆:特定的並發模型在 scale 之後有效率上限(Django sync → FastAPI async;thread-per-request → event loop)。

DX 撞牆:開發者體驗太差,同樣的事情做起來太繁瑣,或是沒有 convention 導致生態碎片化(Django REST Framework 繁瑣 → FastAPI;bare React 生態碎片化 → Meta-framework)。

部署模式改變:新的 runtime 或部署環境出現,現有框架的假設不再成立(Node.js only → Edge / Cloudflare Workers → Hono)。

普及需要時機,不只是解法存在就夠了:還有另一條很重要的規律——解法可能早就有了,但普及需要等「時機成熟」。async/await 的概念在 C# 2012 年就有,Node.js 2017 年才普及。Rust 的 ownership 模型 1970 年代的 Cyclone language 就有,2015 年才真的落地。

這讓人想到槍的例子:火藥武器很早就發明了,但在戰爭型態和後勤補給配合之前,它的優勢沒有辦法被充分利用,普及要等到「整個系統都準備好」。技術也一樣——解法存在不夠,還需要:工具鏈成熟、教育成本下降、成功的生產案例出現,才能真正普及。FastAPI 能快速普及,不只是因為它夠好,也因為 Python type hint 的普及(3.6+)、Pydantic 的成熟、async Python 的工具鏈齊備,三件事同時到位了。

技術不是「新的比較好」,是在某個維度撞牆了,而新的框架解那個極限。Express 在 2026 還活著,是因為大量場景還沒撞到它的極限;或者更準確地說,撞到的人選擇接受那個極限作為 trade-off,而不是花遷移成本解決它。


延伸閱讀