效能、可靠性、安全性——技術品質的硬指標
一句話總結:使用者看不見你的程式碼,但他們感受得到你的效能、可靠性和安全性。這些是「看起來能用」和「真正好用」之間的鴻溝。
結論先講:技術品質不是工程師的自嗨指標。效能差使用者會離開,可靠性差使用者會不信任,安全性差使用者的資料會外洩。這三個做不好,其他做再好也沒用。
效能:100 毫秒和 10 秒之間的世界
效能不是技術 KPI,是使用者體驗的一部分。研究反覆證實:
- 100ms 以內:使用者覺得是「即時」的
- 100ms - 1s:有點延遲,但可接受
- 1s - 10s:流暢感消失,需要進度指示
- 超過 10s:使用者離開
你可能覺得「我的 API 回應時間才 2 秒,還好吧?」不好。2 秒在使用者感知裡已經是「這東西有點慢」。加上網路延遲、前端渲染,使用者實際等待時間可能接近 4 秒。
「看起來能用」和「真正好用」的差距:
| 指標 | 「看起來能用」 | 「真正好用」 |
|---|---|---|
| 首次內容繪製(FCP) | < 5s | < 1.5s |
| 最大內容繪製(LCP) | < 4s | < 2.5s |
| 首次輸入延遲(FID) | < 300ms | < 100ms |
| 累積版面位移(CLS) | < 0.25 | < 0.1 |
| API P95 回應時間 | < 3s | < 500ms |
| API P99 回應時間 | < 10s | < 1s |
看到了嗎?差距不是一點半點,是好幾倍。而且注意,要看 P95 和 P99,不是平均值。平均值是最會騙人的指標——你的平均回應時間 200ms,但如果 1% 的請求要 15 秒,那 1% 的使用者每次都會想摔手機。
可靠性:系統不掛比什麼都重要
可靠性衡量的是系統在各種條件下持續正常運作的能力。你的功能再強大,掛了就什麼都不是。
| 指標 | 「能用」 | 「好用」 | 「出色」 |
|---|---|---|---|
| Uptime | 95%(每月 36 小時停機) | 99.9%(每月 43 分鐘) | 99.99%(每月 4.3 分鐘) |
| Error Rate | < 5% | < 0.5% | < 0.1% |
| MTTR | < 24 小時 | < 1 小時 | < 15 分鐘 |
| MTBF | > 1 天 | > 30 天 | > 90 天 |
95% 的 Uptime 聽起來不錯?每個月停機 36 小時。換算一下,大概每天都會掛一個多小時。你的使用者一定會崩潰。
可靠性的問題通常出在「沒有防禦性程式設計」。你的 API 呼叫沒有 retry、沒有 timeout、沒有 circuit breaker。外部服務一掛,你的服務跟著掛。
// 沒有防禦——外部服務一慢你就跟著慢
async function fetchUserData(userId) {
const response = await fetch(`/api/users/${userId}`);
return response.json();
}
// 有防禦——timeout + retry + exponential backoff
async function fetchUserData(userId, { maxRetries = 3, timeoutMs = 5000 } = {}) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
const response = await fetch(`/api/users/${userId}`, {
signal: controller.signal,
});
clearTimeout(timeoutId);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
} catch (error) {
if (attempt === maxRetries) {
logger.error('Failed after retries', { userId, attempts: maxRetries });
throw error;
}
await sleep(Math.pow(2, attempt) * 100); // exponential backoff
}
}
}第一種寫法三行搞定,看起來很簡潔。第二種寫法多了十幾行。但第一種在生產環境會讓你半夜被叫起來,第二種不會。你選哪個?
安全性:不是事後加的功能
安全性不是「功能做完了再來補」的東西。它是產品品質的基本要求。你不會蓋完一棟大樓再來裝門鎖吧?
OWASP Top 10 對應的最低要求:
| 風險 | 你至少要做到 |
|---|---|
| Injection | parameterized query / ORM,絕不拼接 SQL |
| Broken Authentication | 密碼 bcrypt 雜湊、JWT 有過期時間、支援 MFA |
| Sensitive Data Exposure | HTTPS everywhere、敏感資料加密、log 不記錄 PII |
| Broken Access Control | 每個 API 端點都有權限檢查,不只靠前端隱藏按鈕 |
| Security Misconfiguration | 關閉 debug mode、移除預設帳號、定期更新依賴 |
| XSS | 輸出編碼、CSP header、避免 innerHTML |
| Known Vulnerabilities | 定期跑 npm audit / snyk test |
「靠前端隱藏按鈕」這件事我要特別講。我見過太多系統,管理員功能只是前端把按鈕藏起來,API 端點完全沒有權限檢查。你用 curl 打一下就能做任何事。這不是安全,這是自欺欺人。
可擴展性:現在不需要,但改動成本有多大?
可擴展性不是「現在就要能撐百萬用戶」,而是「未來需要的時候,改動的成本有多大」。
| 擴展方式 | 做法 | 優點 | 缺點 |
|---|---|---|---|
| 垂直擴展(Scale Up) | 升級硬體 | 簡單、不用改架構 | 有上限、成本指數增長 |
| 水平擴展(Scale Out) | 增加實例 | 理論上無上限 | 需要無狀態設計 |
設計時就該問自己這些問題:
- Session:存在 server memory 裡?還是 Redis?前者無法水平擴展
- 檔案:存在 local disk?還是 S3?前者加機器時檔案不會跟著走
- 背景任務:在 API server 裡跑?還是有獨立 worker?前者會阻塞 API 請求
- 資料庫:有 connection pool 嗎?有讀寫分離嗎?
你不需要現在就做到所有這些。但你需要知道:如果現在 session 存在 memory 裡,哪天要水平擴展的時候,你要改的東西會有多少?如果答案是「需要重寫整個認證模組」,那你可能要提前想一想。
這篇的重點回顧
技術品質有四個面向:效能(使用者能感知的速度)、可靠性(系統不掛的能力)、安全性(基本衛生要求)、可擴展性(未來變更的成本)。不要追求完美,但要知道底線在哪裡。
系列文章:
- 好產品的定義(一):好專案 vs 好產品
- 你在這裡 → 好產品的定義(二):技術品質
- 好產品的定義(三):可維護性
- 好產品的定義(四):商業價值與團隊
- 好產品的定義(五):生命週期、指標與反模式
「效能優化跟減肥一樣——量之前你永遠覺得自己還可以。」