[實作筆記] ECPay 綠界沙盒串接:本機測試完整流程

前情提要

串接 ECPay MPG(多元收款)時,本機測試有幾個坑需要提前知道。
整理一下從申請沙盒帳號到 webhook 打進來的完整流程。

申請沙盒帳號

到 ECPay 開發者後台申請測試帳號:

1
https://vendor.ecpay.com.tw

登入後進入「廠商後台」→「系統開發」→「系統介接測試」,
可以拿到沙盒專用的三個憑證:

1
2
3
ECPAY_MERCHANT_ID=3002607
ECPAY_HASH_KEY=pwFHCqoQZGmho4w6
ECPAY_HASH_IV=EkRm7uxTO9nv1tog

這三組是 ECPay 官方的公開測試值,可以直接用。

兩個重要 URL 的差別

ECPay MPG 有兩個容易搞混的 URL 參數:

參數 觸發時機 方向
ReturnURL 付款完成後 ECPay 主動通知 ECPay → 你的 server(POST)
OrderResultURL 付款完成後瀏覽器跳回 ECPay → 使用者瀏覽器(POST redirect)

兩個都要設,缺一個就會有問題:

  • 沒有 ReturnURL:訂單狀態永遠不會更新,使用者付了錢系統不知道
  • 沒有 OrderResultURL:付款完成後使用者停在 ECPay 頁面,不會跳回你的網站

本機測試的問題

ECPay 的 ReturnURL 是 server-to-server 的 webhook,
ECPay 的機器要能打到你的 server,所以 localhost 根本不行。

解法是用 tunnel 工具讓本機有一個公開 URL。
推薦用 cloudflared,不要用 localtunnel(理由另篇說明)。

1
npx cloudflared tunnel --url http://localhost:3000

拿到 URL 之後更新 .env.local

1
NEXT_PUBLIC_BASE_URL=https://your-tunnel.trycloudflare.com

重啟 dev server,這個 URL 就會被用來組 ReturnURLOrderResultURL

CheckMacValue 驗證

ECPay 打來的 webhook 會帶一個 CheckMacValue
這是用 HASH_KEY 和 HASH_IV 算出來的簽章,用來確認請求是真的來自 ECPay。

驗證流程:

  1. 把收到的 POST body 解析成 key-value
  2. 移除 CheckMacValue 欄位本身
  3. 依 key 字母排序,組成 key=value&key=value 字串
  4. 前後加上 HashKey=...&&HashIV=...
  5. URL encode(小寫)後做 SHA256,轉大寫
  6. 比對結果與收到的 CheckMacValue

驗證不過要回 0|FAIL,成功要回 1|OK
不管成功失敗,HTTP status 都回 200,這是 ECPay 的協定要求。

測試信用卡

沙盒環境可以用以下測試卡:

卡號 有效月年 CVV 說明
4311-9522-2222-2222 任意未到期 任意 成功付款
4311-9522-2222-2222 任意未到期 任意 3D 驗證(依設定)

ECPay 沙盒付款頁網址:

1
https://payment-stage.ecpay.com.tw/Cashier/AioCheckout/index

這個頁面上的所有付款方式(包括 Apple Pay)都是沙盒,不會真實扣款。

常見錯誤

付款失敗,訊息代碼 10100058

通常是 CheckMacValue 算錯。
確認 HASH_KEY、HASH_IV 正確,URL encode 用的是 encodeURIComponent 後轉小寫(不是 encodeURI)。

付款成功但訂單沒更新

ReturnURL 沒收到 webhook。檢查:

  1. tunnel 有沒有在跑
  2. ReturnURL 有沒有帶到正確的 tunnel URL
  3. dev server log 有沒有收到 POST

付款完成後使用者停在 ECPay 頁面

OrderResultURL 沒設或設錯,ECPay 不知道要跳回哪裡。

redirect 跑到 https://localhost:3000

在 API Route Handler 裡用 request.url 拿 origin 時,
cloudflared 會加 X-Forwarded-Proto: https,導致 Next.js 認為 origin 是 https://localhost:3000
解法是直接讀 NEXT_PUBLIC_BASE_URL 環境變數,不要從 request 推。

參考

小結

ECPay 沙盒串接的關鍵點:

  1. ReturnURLOrderResultURL 都要設,職責不同
  2. 本機測試一定要用 tunnel,推薦 cloudflared
  3. CheckMacValue 驗證不能省,這是確認 webhook 來源的唯一機制
  4. API Route 裡的 redirect URL 要從環境變數讀,不要從 request 推

(fin)

[工具筆記] 本地 webhook 測試:用 cloudflared,不要用 localtunnel

前情提要

在本機開發時,第三方金流(ECPay、TapPay)的 webhook 需要一個公開 URL 才能打回來。
我試了 localtunnel,然後換成 cloudflared,體驗差很多,記錄一下。

localtunnel 的問題

localtunnel 安裝很簡單:

1
npx localtunnel --port 3000

但實際用起來有幾個痛點:

不穩定。tunnel 動不動就斷,斷了 URL 就換掉,要重新設定金流後台的 webhook URL。
測到一半付款成功,webhook 送過來的時候 tunnel 已經掛了,log 什麼都沒有,很難 debug。

IP 驗證頁。localtunnel 為了防濫用,第一次打這個 URL 會先跳出一個「請輸入你的 IP」確認頁面。
金流打 webhook 的時候是自動 POST,不是人在點,所以它打到的是那個驗證頁,根本進不來。

解法是在 header 加 bypass-tunnel-reminder: true,但第三方的 webhook request 你改不了,等於這個問題無解。

cloudflared:免安裝,開箱即用

cloudflared 是 Cloudflare 官方出的 tunnel 工具。
最棒的一點:不需要帳號,不需要安裝,一行指令直接跑:

1
npx cloudflared tunnel --url http://localhost:3000

跑起來會拿到一個 trycloudflare.com 的 URL,例如:

1
https://rolled-intro-operator-meat.trycloudflare.com

沒有 IP 驗證頁,webhook 直接打進來,不會被擋。
穩定度比 localtunnel 好很多,整個測試過程沒斷過。

快速比較

localtunnel cloudflared
安裝 npm i -g localtunnel 不需要
帳號 不需要 不需要
穩定度 容易斷 穩定
IP 驗證頁 有(webhook 無法繞過)
URL 固定
免費

使用方式

1
npx cloudflared tunnel --url http://localhost:3000

拿到 URL 之後,把它設到需要公開 URL 的地方,例如 .env.local

1
NEXT_PUBLIC_BASE_URL=https://rolled-intro-operator-meat.trycloudflare.com

然後重啟 dev server,金流後台的 webhook URL 也一起更新,就可以測了。

每次重新跑 URL 會換,這點跟 localtunnel 一樣,需要重設一次。
但只要 tunnel 不斷,URL 就是固定的,這點比 localtunnel 好太多。

雷點:Port 衝突導致 404

cloudflared 的 tunnel 指向的 port 是靜態設定,
但 Next.js 如果 3000 被佔用,會自動 fallback 到 3001:

1
⚠ Port 3000 is in use by process 65277, using available port 3001 instead.

tunnel 不知道這件事,流量還是打到 3000,金流 callback 就 404。

本機瀏覽器開 localhost:3001 完全正常,只有走 tunnel 的路徑壞,很難發現。

解法很簡單,先砍掉佔用 3000 的 process 再重啟:

1
2
lsof -ti:3000 | xargs kill -9
pnpm dev

小結

本機測 webhook,直接用 cloudflared,不要碰 localtunnel。
免安裝、沒有驗證頁擋路、穩定,完全沒有理由繼續用 localtunnel。

測試前記得確認 dev server 跑在哪個 port,tunnel 要對齊。

(fin)

[實作筆記] Next.js 錯誤處理的三層:middleware、try-catch、instrumentation.ts

前情提要

在串接 ECPay 金流時,一個 API Route 裡用了 redirect()(from next/navigation),結果 ECPay 的 callback 打進來之後整個 dev server 直接掛掉。

沒有 log、沒有提示,user 看到 503。

這讓我重新想了一遍 Next.js 的錯誤處理應該怎麼設計——中間討論到 middleware、Edge Runtime、instrumentation.ts,整理在這裡。

Next.js 請求的三層

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
HTTP Request


┌─────────────────────────────────────┐
│ middleware(Edge Runtime) │ ← auth guard、redirect
│ │
│ 出錯 → onRequestError 觸發 │ ← instrumentation 接得住
│ → 但 Pino logger 在這裡失效 │ ⚠️ Edge Runtime 限制
│ → Next.js 回 500 │
└──────────────┬──────────────────────┘


┌─────────────────────────────────────┐
│ API Route / Server Component │
│ (Node.js,完整環境) │
│ │
│ ┌─────────────────────────────┐ │
│ │ 業務層 try-catch │ │ ← 有業務理由才放
│ │ (ECPay→0|FAIL、OAuth→ │ │
│ │ /login?error=...) │ │
│ └──────────────┬──────────────┘ │
│ │ 未被接住 │
│ ▼ │
│ ┌─────────────────────────────┐ │
│ │ instrumentation.ts │ │ ← 最終防線,全域自動生效
│ │ onRequestError │ │
│ │ Node.js → Pino logger ✅ │ │
│ │ → Next.js 回 500 │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────┘


HTTP Response

每一層的職責不一樣,不能混用。

第一層:middleware

Next.js middleware 跑在 Edge Runtime——這是一個故意閹割過的 JS 執行環境,只有 Web 標準 API(fetchRequestResponse),沒有 Node.js 的東西。

所以 middleware 能做的事很有限:

1
2
// ✅ 可以:讀 cookie、做 redirect、檢查 JWT
// ❌ 不行:連資料庫、用 Pino logger、用大多數 npm 套件

這不是 Vercel 的限制,是 Next.js 的設計決策——middleware 在每個請求最前面跑,設計目標是輕量、快,所以鎖死在 Edge Runtime。

結論:middleware 只放輕量的守衛邏輯(auth check、redirect),不是錯誤處理的地方。

第二層:業務層 try-catch

API Route 跑在完整 Node.js 上,可以做任何事。但 try-catch 的原則是:只有業務理由才放,不要為了「防止 crash」而加

什麼叫業務理由?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// ✅ ECPay webhook 協定要求:出錯必須回 0|FAIL,不能回 500
try {
// ... 處理邏輯
return new Response('1|OK', { status: 200 })
} catch (err) {
logger.error('ECPay webhook error', { error: err })
return new Response('0|FAIL', { status: 200 })
}

// ✅ OAuth 失敗:應該導到 /login?error=... 而不是白畫面
try {
// ... OAuth 流程
} catch {
return NextResponse.redirect(new URL('/login?error=server_error', request.url))
}

// ❌ 只是怕 crash,沒有業務意義
try {
// ...
} catch (err) {
return new Response('error', { status: 500 }) // 跟 Next.js 預設行為一樣,多此一舉
}

另外注意:在 API Route Handler 裡不能用 redirect() from next/navigation,那是給 Server Component / Server Action 用的。API Route 要用 Response.redirect()

1
2
3
4
5
6
// ❌ API Route 裡用這個會讓 server crash
import { redirect } from 'next/navigation'
redirect('/some-page')

// ✅ 正確做法
return Response.redirect(`${baseUrl}/some-page`, 302)

第三層:instrumentation.ts

Next.js 15 起提供 onRequestError,是真正的全域最終防線。任何沒被 try-catch 接住的 exception 都會進來。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// src/instrumentation.ts
import type { Instrumentation } from 'next'

export const onRequestError: Instrumentation.onRequestError = async (
err,
request,
context,
) => {
const { getLoggerService } = await import('@/infrastructure/di/container')
getLoggerService().error('Unhandled request error', {
error: err.message,
digest: err.digest,
path: request.path,
method: request.method,
routeType: context.routeType,
routePath: context.routePath,
})
}

這個 hook 的好處:

  • 全域自動生效,新加的 route 不需要記得處理
  • API Route、Server Component、Server Action 全部覆蓋
  • 用 dynamic import 避免初始化順序問題

它不會改變 response(user 還是收到 500),但你起碼有 log 可以查根因。

陷阱:middleware 錯誤接得住,但 logger 失效

onRequestError 在 Edge Runtime 和 Node.js 都會觸發,包含 middleware 的錯誤(context.routeType === 'proxy')。

但 Pino 是 Node.js 專用的,在 Edge Runtime 下會直接失敗。

要完整處理,需要根據 runtime 分開:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
export const onRequestError: Instrumentation.onRequestError = async (
err,
request,
context,
) => {
if (process.env.NEXT_RUNTIME === 'nodejs') {
// Node.js:用 Pino 正常記 log
const { getLoggerService } = await import('@/infrastructure/di/container')
getLoggerService().error('Unhandled request error', {
error: err.message,
path: request.path,
routeType: context.routeType,
})
} else {
// Edge Runtime(middleware 錯誤):只能用 console 或送外部服務
console.error('[edge] Unhandled middleware error', err.message, request.path)
}
}

這個專案的 middleware 只做 auth guard,邏輯薄、出錯機率低,目前先用 console.error 兜底即可。

三層的職責總結

層次 位置 做什麼 限制
middleware 請求最前面 auth guard、redirect Edge Runtime,無 DB/Logger
try-catch 各 route 內 業務錯誤對應 只放有業務理由的
instrumentation.ts 全域最後 接住所有漏網錯誤、記 log 無法改 response;Edge 下需另處理

小結

不要為了「怕 crash」而到處加 try-catch,那只是把問題藏起來。

正確的順序是:先把 instrumentation.ts 設起來,確保所有未處理的錯誤都有 log,然後只在真的有業務需求的地方加 try-catch。

這樣新加的 route 自動有防護,不需要靠人記得。

(fin)

[實作筆記] 為什麼 InversifyJS 在 Next.js 不能用?SWC 相容性問題與 awilix 解法

前情提要

在做電商平台時,遇到一個問題:要同時支援多家金流(TapPay、ECPay、藍新),
PaymentService 是同一個 interface,但有三個不同的實作。

這是 DI 的經典場景,自然就想到引入 DI container 來管理。
調查一輪之後,才發現 Next.js 的編譯器對這件事有一個很重要的限制。

Next.js 預設用 SWC 編譯

SWC 是用 Rust 寫的 JavaScript/TypeScript 編譯器,Next.js 從 v12 開始改用它作為預設編譯器。

快很多,這是事實。但問題來了。

InversifyJS 和 tsyringe 為什麼不能用

InversifyJS 和 tsyringe 是目前最主流的兩個 TypeScript DI framework,
兩個都靠裝飾器(decorator)做依賴注入:

1
2
3
4
@injectable()
class TapPayPaymentAdapter implements PaymentService {
constructor(@inject('Logger') private logger: LoggerService) {}
}

這個模式需要兩個東西:

  1. emitDecoratorMetadata:TypeScript 編譯時把型別資訊保留下來
  2. reflect-metadata:在執行期讀取那些型別資訊

問題在於:SWC 對 emitDecoratorMetadata 的支援不完整

SWC 的目標是快,不是完整複製 tsc 的行為。
reflect-metadata 依賴的 metadata 在 SWC 編譯後不保證存在,跑起來會出錯或行為異常。

要硬用 InversifyJS,得把 Next.js 的編譯器換回 tsc,放棄 SWC 的效能優勢。代價太高。

awilix 為什麼可以

awilix 完全不用裝飾器,也不依賴 reflect-metadata
它靠的是命名慣例:建構子參數的名稱對應 container 裡的 key。

1
2
3
4
5
6
7
8
9
10
11
// container 裡這樣註冊
container.register({
loggerService: asClass(PinoLoggerService),
tapPayService: asClass(TapPayPaymentAdapter),
ecpayService: asClass(EcpayPaymentAdapter),
})

// adapter 建構子這樣寫
class TapPayPaymentAdapter {
constructor({ loggerService }: { loggerService: LoggerService }) {}
}

名稱對上,awilix 自動注入。不需要編譯器幫你保留任何型別資訊,SWC 完全相容。

三家比較

InversifyJS tsyringe awilix
週下載量 1.5M 600K 400K
裝飾器 需要 需要 不需要
reflect-metadata 需要 需要 不需要
Next.js / SWC
學習曲線

下載量 InversifyJS 最高,但 Next.js 專案用不了。
Next.js 能用的只有 awilix。

業界趨勢

2026 的調查顯示,小型 Node.js 專案的趨勢反而是遠離 DI container
回到手寫的 module-level singleton 或手動 dependency passing。

這不是說 DI container 不好,而是:

  • 依賴圖簡單時,手寫 container 清楚又好讀
  • 依賴圖複雜到手寫開始讓人痛了,再引入才是真正有感的投資

對我的情況,引入 awilix 的理由不只是技術問題,
還有早期建立團隊共識:早期引入就變成慣例,晚期引入沒人敢動。

小結

Next.js 用 SWC,SWC 不完整支援 reflect-metadata,所以 InversifyJS 和 tsyringe 不能用。
Next.js 專案要引入 DI container,awilix 是目前唯一合理的選擇。

(fin)

[實作筆記] Next.js 16 — middleware 改名為 proxy 了

前情提要

升到 Next.js 16 之後,dev server 一直噴這個警告:

1
2
⚠ The "middleware" file convention is deprecated.
Please use "proxy" instead.

查了一下,原來 Next.js 16 把 middleware 整個改名了,記錄一下到底換了什麼。

為什麼改

Next.js 官方說,middleware 這個名字太模糊,
實際上它做的事情是網路邊界的路由代理
改叫 proxy 更能表達它的職責。

改了哪些東西

項目 舊(deprecated)
檔名 middleware.ts proxy.ts
export 函式名 export function middleware export function proxy
config export 不變 不變(matcher 一樣)
runtime edge / nodejs 只支援 nodejs

configmatcher 不用動,只有檔名和 function 名稱要換。

最重要的限制

新的 proxy 不支援 edge runtime,只跑 nodejs。

如果你有設定:

1
export const runtime = 'edge'

或者用了 next-intl、其他依賴 edge runtime 的套件,
先不要急著遷移,繼續用 middleware.ts 也沒問題,
Next.js 官方說後續 minor release 會再補 edge 的支援。

怎麼遷移

方式一:官方 codemod(推薦)

1
npx @next/codemod@latest middleware-to-proxy .

自動幫你改檔名和 function 名稱。

方式二:手動

1
mv src/middleware.ts src/proxy.ts

然後把 function 名改掉:

1
2
3
4
5
// 原本
export async function middleware(request: NextRequest) { ... }

// 改成
export async function proxy(request: NextRequest) { ... }

NextRequestNextResponseconfigmatcher 全部不用動。

小結

Next.js 16 把 middleware 改名叫 proxy,概念一樣,名字更準確。
遷移很簡單,用 codemod 一行搞定;
唯一要注意的是 edge runtime 目前不支援,有用到的先等等。

(fin)

[實作筆記] 形象站滿版背景影片(一):前端靜態資源放哪裡

前情提要

做形象站,Hero 要放一支滿版自動播放的背景影片,第一個冒出來的問題是「影片放哪裡」。

這個問題帶出了前端靜態資源的兩種擺法:public/src/assets/。對一般人看起來只是資料夾名字不同,但對工程師來說,兩者的機制差很多。

做形象站 Hero 背景影片的前置研究。完整的流量估算與播放器選型,見下一篇

素材放哪:public/ vs src/assets

對一般人來說看起來只是資料夾名字不同,但對這裡有蠻多的細節,我們來逐一分析。

首先要了解兩個時間點:

  • 編譯期(Build Time):網站還沒上線前,可以將一些資源優化打包減少不必要的流量,或是使用雜湊命名來避免快取問題。
  • 執行期(Runtime):網站上線後,使用者實際使用的期間。程式在這個階段才真正執行,才能動態取得資源、回應使用者的操作。

src/assets/ — 打包工具接管

現代主流網站打包工具(Vite / Webpack)會在編譯期處理這些檔案:

  • 雜湊檔名:輸出 bg.a1b2c3d4.mp4,URL 跟著內容變,瀏覽器可以放心永久 cache(Cache-Control: immutable)——改了檔案雜湊就換,訪客自動抓新的
  • 可能優化:圖片壓縮、小檔案 inline 成 base64 等
  • 追蹤依賴:沒有被 import 的檔案,build 產出裡就不會出現

限制一import 是編譯期的靜態行為,URL 在 build 時就決定了,沒辦法在 runtime 動態組字串——更不可能從資料庫讀一個 URL 出來用。

限制二:打包工具適合處理 JS、CSS、小圖,遇到影片這種大檔案,build 時間拉長,而且影片可壓縮或優化的效果有限,過一手只是浪費。

public/ — 繞過打包工具

這裡的資源不會被打包工具處理,本質跟早期 Apache / Nginx 放一個靜態資料夾直接 serve 檔案是同一件事——收到請求,原樣回傳。快取責任落在「伺服器那層」:早期要自己設 cache header,現代部署平台(Vercel、Netlify、Cloudflare Pages)會自動推到 Edge Network 幫你卸載流量——但這是平台做的,不是框架。

URL 永遠是 /你放的路徑,開發時字串直接用不用 import

1
2
<video src="/hero.mp4" />     ← 寫死字串
<video src={dbValue} /> ← 從資料庫來的字串,也走得通

Next.js 本身真正在 framework 層處理靜態資源的是 next/image:server side 做圖片壓縮、格式轉換、依裝置裁切,回傳正確的 cache header。public/ 它不碰。

判斷比較

| | src/assets/ | public/ |
| — | — | — |
| 引用方式 | import,編譯期靜態 | 字串 URL,runtime 動態 |
| 快取 | 自動雜湊,可放心 immutable | 手動管理,或靠部署平台預設值 |
| 大檔案 | 不適合(影片、字型) | 適合 |
| 適合 | Icon、logo、小圖 | 後台可換的媒體、使用者貼的 URL |

我們的例子是背景影片,幾乎都是「後台可換、runtime 才知道 URL」的需求 → 判斷上選擇 public/ 較為適合。

參考

小結

  • src/assets/ 交給打包工具管,享有自動雜湊快取,但只能靜態 import、不適合大檔案。
  • public/ 繞過打包工具,直接 serve,URL 是字串、runtime 拿來用都沒問題。
  • 影片、字型這類大媒體放 public/;icon、logo 等小圖用 src/assets/
  • 換 CDN 只需換 URL 字串,不用動程式碼。

(fin)

[AI生成] 20260612 科技周報

AI 週報配圖

本周要點

其他訊息

(fin)

[工具筆記] 2026 年中 AI Agent Coding 工具盤點:Claude Code、Cursor、Codex 怎麼選

前情提要

AI Coding 工具迭代太快,半年就是一個世代。
趁 2026 年中把目前市場上的 Agent Coding 工具盤點一次,
排名依據是實際開發者採用度、Agent 能力、社群討論度、企業導入率,不是單純的 IDE 補全。

結論先講:2026 最主流的組合是 Cursor + Claude Code,不是單一工具。


總排名

排名 工具 類型 最大優勢 最大缺點
1 Claude Code CLI Agent 長任務、多檔案重構、架構理解最強 成本高
2 Cursor IDE Agent UX 最成熟、上手最快 被 Claude Code 追趕
3 OpenAI Codex CLI + Cloud Agent 雲端背景執行、速度快 生態仍在追趕
4 Windsurf IDE Agent 多 Agent 協作體驗佳 穩定度略輸 Cursor
5 GitHub Copilot IDE Extension 企業整合最強 Agent 能力不如前三
6 Cline Open Source Agent 完全控制模型 Token 燒很快
7 Aider Open Source CLI Git Flow 最強 UX 不友善

功能比較

功能 Claude Code Cursor Codex Windsurf Copilot Cline Aider
自動修改整個專案 ★★★★★ ★★★★★ ★★★★★ ★★★★ ★★★ ★★★★★ ★★★★★
架構理解 ★★★★★ ★★★★ ★★★★ ★★★★ ★★★ ★★★★ ★★★★
長 Context ★★★★★ ★★★★ ★★★★ ★★★ ★★★ ★★★★★ ★★★★★
多 Agent ★★★★ ★★★ ★★★★ ★★★★★ ★★ ★★ ★★
Git 整合 ★★★★ ★★★ ★★★ ★★★ ★★★ ★★★ ★★★★★
開源
成本控制 ★★★ ★★★ ★★★★ ★★★★ ★★★★ ★★★★★ ★★★★★
適合企業 ★★★★ ★★★★ ★★★★ ★★★ ★★★★★ ★★ ★★

以我的情境怎麼選

我的背景:技術主管、Python / FastAPI、Azure / GCP、系統架構、DevOps、AI 應用開發。

第一名:Claude Code

適合的場景:

  • 重構大型專案
  • 架構分析
  • Code Review
  • 寫 ADR
  • 拆 Epic

目前幾乎是 Agent 能力的天花板,缺點就是貴。

第二名:Cursor

適合日常寫程式:FastAPI、Terraform、Docker、Kubernetes,體驗最好。

值得注意的是,很多人現在的用法是 Cursor + Claude 模型
而不是用 Cursor 自己的 Agent。

第三名:OpenAI Codex

如果你想要的是:

  • 一次開 10 個任務
  • Agent 自己修 Bug
  • Agent 自己開 PR

Codex 的雲端 Sandbox 架構很有潛力,背景執行是它的主場。


開源派排行

不想被平台綁定的話:

排名 工具
1 Aider
2 Cline
3 Roo Code
4 OpenHands
5 Continue

代價是 UX 與 Token 成本要自己扛。


趨勢:三年三個世代

  • 2024:Copilot 時代
  • 2025:Cursor 時代
  • 2026:Claude Code vs Codex 時代

很多工程師的演進路徑是:

1
Copilot → Cursor → Claude Code

而目前最常見的組合是:

1
Cursor IDE + Claude Code Agent + Claude Opus

單一工具吃全場的時代已經過去了。


參考


小結

  • 2026 年中的格局:Claude Code 是 Agent 能力天花板,Cursor 是體驗最好的 IDE,Codex 押注雲端背景執行。
  • 排名看綜合採用度,但選工具要看自己的場景:重構與架構工作用 Claude Code,日常開發用 Cursor。
  • 要省成本或不想被綁平台,開源派看 Aider 與 Cline。
  • 最主流的答案不是二選一,是 Cursor + Claude Code 的組合。

(fin)

[AI生成] 20260610 科技周報

AI 週報配圖

本周要點

  • 秘密向 SEC 提交 S-1 註冊聲明草案:OpenAI 宣布已向美國證券交易委員會(SEC)秘密提交了 S-1 格式的招股說明書草案,準備進行首次公開募股(IPO)。這標誌著該公司在商業化與資本市場化道路上的重大里程碑。more
  • 智能時代的產業政策:OpenAI 提出了針對智能時代的產業政策藍圖,強調需要建立強大的 AI 基礎設施,包括能源、晶片及數據中心。此政策旨在確保國家競爭力並促進 AI 技術的民主化與安全發展。more
  • 築夢:為更具幫忙能力的 ChatGPT 提供更好的記憶力:OpenAI 推出名為「Dreaming」的新技術,旨在升級 ChatGPT 的記憶機制。透過在後台進行「夢境」般的離線整合,ChatGPT 能更有效率地整理與回顧過往的對話上下文,提供更具連貫性且個人化的互動體驗。more
  • 旨在造福大眾:我們的計劃:OpenAI 發表了其未來的發展計劃,重申確保通用人工智能(AGI)造福全人類的使命。該計劃詳細說明了治理結構的優化以及如何將 AI 技術帶來的經濟價值公平地分配給社會各界。more
  • 介紹 GPT-Rosalind 的全新功能:OpenAI 宣布為 GPT-Rosalind 引入全新功能,提升其在科學研究與複雜邏輯推理方面的表現。這次更新將使該模型能更有效地協助生物學及其他前沿科學領域的研究人員進行探索。more
  • 推出 OpenAI 經濟研究交流平台:OpenAI 成立了「經濟研究交流平台(Economic Research Exchange)」,旨在與全球學術界和政策制定者合作,共同研究 AI 對勞動力市場、生產力以及整體經濟結構的深遠影響。more
  • 智能時代的生物防禦:本文探討了在 AI 快速發展的時代下,如何利用先進模型來強化全球生物防禦系統。OpenAI 強調了制定安全防護措施的重要性,以防止技術被濫用,同時利用 AI 預防與應對潛在的生物安全威脅。more

其他訊息

(fin)

[實作筆記] 形象站滿版背景影片(二):流量、CDN 與播放器選型

前情提要

Hero 要放一支滿版自動播放的背景影片。

Hero 區塊是網頁最頂部的全版區塊,通常含標題、說明文字與背景,是訪客進站看到的第一眼。

素材要放哪,已在上一篇整理好了,結論是放 public/,URL 字串直接用。

這篇專注在另外兩個問題:

  1. 流量控管:滿版自動播放背景影片是首頁最吃流量的東西,怎麼估、怎麼省?
  2. UI 體驗:背景影片要無控制列、自動播放、靜音、循環——原生 <video> 還是嵌入 YouTube/Vimeo?

自存影片:會吃多少流量?

會,而且滿版自動播放的背景影片,是整頁最吃流量的東西

粗估一下:一支壓到 SD 的背景影片約 3.5MB,每個訪客每次載入首頁就抓一次(瀏覽器會快取,回訪者不重抓)。

1
3.5MB × 10,000 次造訪/月 ≈ 35GB/月

對小型品牌站通常還在額度內,但要注意兩件事:

  1. 行動裝置上,這 3.5MB 是吃訪客自己的行動數據,UX 不友善。
  2. 影片越大支越可怕,HD 1080p 動輒 12MB 起跳。

重點觀念:走 CDN ≠ 不算流量

很多人以為「丟到平台、平台有 CDN」就沒事了。

以主流部署平台來說,public/ 的靜態檔確實會由它的 Edge Network(CDN)快取分送——但傳輸量仍然計入你的方案額度

CDN 解決的是「分送速度與快取」,不是「免費流量」。要真正卸載流量,得換 host。

要省流量:CDN / 影音 host 比較

影片放在 public/ 的話,流量算在部署平台的額度裡。

以我們用的平台 Vercel 免費方案只有 100GB/月,一支背景影片流量大的時候很快就吃光。

解法是把影片搬到外部 host,讓別人的伺服器去扛流量。

好消息是,只要程式碼只用 URL 字串引用影片,換 host 就是換那個字串,不用改程式碼

1
2
3
4
5
// 原本放 public/
<video src="/hero.mp4" />

// 搬到 R2 後,只改這一行
<video src="https://pub-xxx.r2.dev/hero.mp4" />

兩條路線:

路線一:自己存,外部 CDN 來送(Cloudflare R2

把影片傳到 R2,egress(流出流量)免費,只付儲存費($0.015/GB/月)。一支 5MB 影片幾乎是 $0,部署平台那邊完全不計這筆流量。

路線二:讓別人的平台吸收(Vimeo/Youtube)

影片上傳到 Vimeo,你的伺服器完全不出流量,由 Vimeo 負責送給訪客。代價是多了第三方依賴,免費方案有上傳量限制。

方案 流量誰扛 備註
Cloudflare R2 R2(egress 免費) 自己管檔案,換 URL 即可
Cloudflare Stream / Mux 平台 專業影音串流,自動轉檔、自適應位元率
Cloudinary 平台 媒體優化 + CDN
Vimeo Vimeo 免費方案有上傳限制,背景模式乾淨
YouTube 嵌入 YouTube(完全免費) 有播放器外觀問題(下節詳述)

形象站的滿版背景,通常不會用 YouTube,原因看下面。

YouTube 當「無播放器」背景?hack 與代價

YouTube 這麼主流,能不能拿來當乾淨背景?

沒有官方乾淨方案,主流做法都是 hack,而且 YouTube 近年反而把「藏品牌」收得更緊。

做法是 IFrame Player API + 參數 + CSS:

1
autoplay=1&mute=1&loop=1&playlist=VIDEO_ID&controls=0&playsinline=1&rel=0&disablekb=1&iv_load_policy=3
1
2
3
4
5
6
7
8
/* 把 iframe 放大超出視窗、置中裁切,藏掉黑邊與殘留 UI */
.bg-wrap { position: absolute; inset: 0; overflow: hidden; }
.bg-wrap iframe {
position: absolute; top: 50%; left: 50%;
width: 150vw; height: 150vh; /* 刻意超出 */
transform: translate(-50%, -50%);
pointer-events: none; /* 防止點到跳出控制列 */
}

代價很實際:

  • modestbranding 已被 YouTube 淡化/移除,載入、暫停、結束仍可能閃一下 logo/標題
  • 單片循環要用 loop=1&playlist=ID 的 trick,接點會黑屏跳一下(要用 API seekTo(0) 才順)。
  • 16:9 硬裁成滿版 → 邊緣被切掉
  • 比原生 <video> 重很多(要載播放器 JS、追蹤 cookie,除非用 youtube-nocookie)。
  • 手機(尤其 iOS)自動播放不穩
  • 「完全藏品牌」本身遊走在 ToS 邊緣。

真正乾淨的主流解

1. Vimeo 背景模式

Vimeo 的嵌入支援 background=1 參數,這是官方為背景影片設計的模式:自動播放、循環、靜音、全程無控制列、無品牌

1
https://player.vimeo.com/video/VIDEO_ID?background=1&muted=1&autoplay=1&loop=1

要 iframe 又要乾淨,這才是該選的,比 YouTube hack 乾淨太多。

2. 影音 CDN 的 mp4 + 原生 video

把影片放 Stream / Mux / R2,拿到真正的 mp4/HLS URL,用原生 <video> 播。

最乾淨、最可控,也最好做手機降級(poster、preload、小螢幕只給圖)。

架構建議:媒體以 URL 為主

把這些湊起來,得到一個簡單原則:渲染層不要綁死 host。

  • 媒體只存一個 URL 字串(哪來的都行:自家 public/、R2、Mux、Vimeo iframe)。
  • 範例/預設素材放 public/,跟使用者貼的網址走同一條路徑。
  • 正式上線要換成 CDN?貼上新 URL 即可,不用改程式碼。
  • 要支援 iframe 型(Vimeo/YouTube)就多開一種「媒體類型」,渲染分流到 iframe,原生影片仍走 <video>

這樣早期可以自存 demo、之後無痛換成專業影音 CDN,決策成本最低。

參考

小結

  • 滿版自動播放背景影片最吃流量;CDN 加速 ≠ 免費流量,傳輸仍算額度。
  • 省流量靠換 host:R2(egress 免費)/ Stream / Mux / Cloudinary。
  • YouTube 無播放器背景只能 hack,缺點一堆;要 iframe 乾淨解請用 Vimeo background=1
  • 最乾淨:影音 CDN 的 mp4 + 原生 <video>
  • 架構上讓媒體「只認 URL」,換 host 就是換字串,不用動程式碼。

(fin)

Please enable JavaScript to view the LikeCoin. :P