生產應用程式
核心理念
每家大型公司最終都會收斂到相同的架構形狀:前端有邊緣網路,前端可獨立部署,後端位於閘道之後,工作者從佇列拉取任務,一個關聯式的記錄系統,一個快取層,一個事件或任務骨幹,物件儲存,以及圍繞所有這些的監控系統。Shopify、Notion、Figma、Instacart 和 Airbnb 在語言和供應商上有所不同,但架構形狀不變。
這個形狀在週末的 MVP 與擁有平台團隊的公司之間並不會改變。只有每個盒子背後的供應商會改變。因此,此技能從第一天起就使用便宜的受管供應商建立完整的架構形狀,並強制執行一系列可攜性規則,同時為每個層級保留一份劇本,以便在可衡量的觸發條件發生時切換供應商。透過這種方式,從 Vercel 和 Supabase 遷移到 Kubernetes 和 RDS 只是一系列配置變更和資料複製,絕非重寫。
如何使用此技能
- 閱讀專案描述。僅針對描述中未提及的部分詢問下方的事前調查問題。
- 使用
references/providers-and-tiers.md為每個層級選擇供應商。除非選擇器規則另有規定,否則從第 1 層開始。 - 根據下方模板撰寫
docs/PLAN.md,包括從references/architecture.md改編的圖表,並在開始架構建立前展示。 - 按照以下順序進行架構建立和建置。
- 在上線前,執行
references/launch-checklist.md並僅報告失敗及其修復方法。 - 當某個觸發條件稍後被觸發時,開啟
references/migrations.md並移動該層級。切勿在同一週內移動兩個層級。
對於既有應用程式:先進行稽核。將儲存庫對應到此架構形狀,按風險(資料洩漏、服務中斷、濫用、供應商鎖定、成本)列出差距,然後停止。在擁有者從清單中挑選後,才進行變更。
事前調查
僅在一則訊息中詢問描述未解答的問題。
- 使用者是誰,以及什麼是租戶:擁有資料的單位(一家公司、一棟建築、一個經銷商、一個個人)?
- 什麼是大量資料:動態消息、訊息、上傳、影片、事件、搜尋?粗略的行數或每日檔案數,以及成長率。
- 網頁、iOS、Android 還是混合?哪個先發布?
- 即時需求:聊天、即時狀態、協作編輯,還是輪詢即可?
- 金流:訂閱、一次性付款、市集分潤、無?
- 是否有任何受監管或敏感項目:儲存的付款資訊、健康資料、未成年人、位置、政府或企業買家(會要求 SOC 2)?
- 受眾地理分布:單一國家還是全球?
- 限制:現有的雲端點數、現有供應商、所需的語言。
這些答案將驅動 references/providers-and-tiers.md 中的選擇器規則。
架構形狀
每個層級都有一個角色、一個大型公司的參考案例,以及一條保持其可攜性的規則。每個層級的供應商清單位於 references/providers-and-tiers.md。
- 邊緣網路: DNS、TLS、CDN、DDoS 防護、WAF、速率限制、機器人過濾,位於所有主機之前。大型公司:Cloudflare、Akamai、Fastly、搭配 AWS WAF 的 CloudFront。規則:你擁有 DNS 區域;每個供應商都位於 CNAME 之後,因此更換供應商只需更改 DNS 設定。
- 前端: 一個可獨立部署的項目,靜態或伺服器端渲染,不包含任何商業邏輯和資料庫存取。大型公司:一個 React 或 Next.js 應用程式,建置為容器並透過 CDN 提供服務。規則:前端建置為標準的 Node 容器或靜態套件,並且僅與公開 API 通訊。
- 閘道: 一個負載平衡器或 API 閘道,終止來自邊緣網路的 TLS,按路徑路由,並強制執行粗略的限制。大型公司:ALB、Envoy、NGINX ingress、Kong。規則:API 絕不依賴於閘道在其自身中介軟體中無法重現的功能。
- 後端: 閘道後方的一個單體服務,僅在團隊發生衝突時才拆分為多個服務。大型公司:Shopify 和 GitHub 使用 Rails、Instagram 使用 Django、Notion 使用 Node、內部使用 gRPC、外部使用 GraphQL 或 REST。規則:一個具有健康檢查端點的 Docker 容器,從環境變數讀取配置,商業邏輯中不使用供應商 SDK。
- 工作者和佇列: 消費任務的獨立長時間執行程式;當任務有多個步驟或需要人為等待時,使用工作流程引擎。大型公司:Shopify 使用 Sidekiq 和 Kafka 消費者,Netflix 和 Duolingo 使用 Temporal,Notion 使用 Kafka。規則:任務具有冪等性,攜帶 ID 而非大型資料,且佇列使用標準的 Redis 或 Kafka 協定。
- 關聯式資料庫: 一個作為記錄系統的 Postgres(或 MySQL)主庫,複本用於讀取,僅在超過單一機器限制時才進行分片。大型公司:Notion 和 Figma 使用搭配 PgBouncer 的 RDS Postgres,Shopify 使用 100 多個 MySQL 分片。規則:僅使用 Postgres Wire Protocol,SQL 遷移腳本放在儲存庫中,角色和 RLS 使用 SQL 定義,不使用任何僅適用於特定供應商 API 的功能。
- 快取: 用於熱讀取、速率限制計數器、鎖、工作階段的 Redis 或 Memcached。規則:使用標準的 Redis 協定,鍵以租戶為範圍並帶有版本號,應用程式必須能在快取為空的狀態下運行。
- 物件儲存: 一個 S3 或與 S3 相容的儲存桶,用於存放所有檔案;絕不使用資料庫。規則:僅使用 S3 API,鍵以前綴按租戶分類,透過簽署的 URL 或 CDN 提供服務。
- 身份識別: 一個發出 JWT 的身份提供者,一個由應用程式擁有的
users資料表。大型公司:內部實作加上 Okta 或 Auth0 用於企業 SSO。規則:API 透過 JWKS 自行驗證權杖,並擁有使用者資料表,因此身份提供者可以更換。 - 事件和分析: 產品分析,後續透過變更資料捕獲 (CDC) 饋送資料的資料倉儲。大型公司:從 Kafka 匯入 Snowflake 或 BigQuery。規則:資料倉儲從資料庫讀取資料,永遠不會從應用程式進行雙重寫入。
- 可觀測性: 結構化日誌、錯誤、指標、追蹤、正常運行時間、警報,並與操作手冊相關聯。大型公司:Datadog、Grafana、Honeycomb、Sentry、OpenTelemetry。規則:應用程式發出 OpenTelemetry 資料和 JSON 格式日誌,因此後端僅需設定 Exporter。
- 交付: 每次推送都執行 CI、基礎設施即程式碼、金絲雀或藍綠部署、功能旗標、快速回滾。大型公司:Terraform、Buildkite 或 GitHub Actions、ArgoCD 或 Spinnaker、LaunchDarkly。規則:任何在儀表板中點擊的操作,必須在當天記錄於
infra/目錄中,並從第 2 層開始使用 Terraform。
可攜性規則
這些規則使後續的遷移成本降低。它們在 MVP 階段幾乎不花費任何成本,卻是週末遷移與長達一季重寫之間的區別。
- 從第一次提交開始,前端和後端就是獨立的儲存庫和可獨立部署的項目。它們透過有版本的 HTTP API(包含共享的 schemas)進行通訊。
- 後端和工作都是 Docker 映像檔,可以在任何執行容器的地方運行。API 不使用無伺服器函數:Vercel Functions、Lambda Handlers 和 Edge Functions 是唯一無法在事後無需重寫就能遷移到容器中的東西。
- 僅透過標準協定與基礎架構通訊:Postgres Wire Protocol、Redis 協定、S3 API、SMTP 或位於介面後方的 HTTP Email API、用於權杖的 JWKS、用於遙測的 OpenTelemetry。供應商自身的客戶端函式庫僅允許在一個輕量的轉接器模組中使用,並且該模組可以被替換。
- 商業邏輯中不使用供應商功能:不使用 PostgREST 呼叫、後端不使用 Supabase 或 Firebase 客戶端、不使用 Vercel 特定的快取 API、不使用 Workers KV。前端僅可用於登入的供應商 Auth SDK。
- 應用程式擁有其 Schema:SQL 遷移腳本存放在儲存庫中,由遷移工具執行,絕不使用儀表板。角色、RLS 和函數都是 SQL 檔案,儲存庫可以在任何 Postgres 上重播這些檔案。
- 應用程式擁有其使用者:一個從身份提供者在首次登入時鏡像過來的
users資料表,並將提供者 ID 儲存為一個欄位。權限資料存在於應用程式的資料表中。 - 配置是啟動時驗證的環境變數。機密存放在平台的機密儲存庫中,但以環境變數的形式提供給應用程式,因此機密儲存庫可以更換。
- 你擁有 DNS 區域(Cloudflare)。每個供應商都是一個 CNAME 目標。沒有任何東西指向供應商的名稱伺服器。
- 物件儲存鍵和儲存桶佈局在 R2 和 S3 上是相同的;程式碼使用帶有端點設定的 S3 SDK。
- 每個層級都有一個文件化的匯出方式:資料庫使用
pg_dump和邏輯複寫,物件使用rclone,從不需要 Redis dump,因為快取是可棄置的,而佇列會被清空。 - 從第一天起就存在健康檢查端點、對 SIGTERM 的優雅關閉以及水平無狀態性,因為從 Railway 到 Kubernetes 的每個平台都依賴這些特性。
- 從第一天起,遙測和日誌就使用 OpenTelemetry 和 JSON 格式;供應商代理不會安裝在程式碼中。
層級
每個層級分為三個層級。觸發條件是可衡量的;包含供應商、成本和選擇器規則的完整表格位於 references/providers-and-tiers.md。
- 第 1 層,MVP: 幾分鐘內即可設定完成、每月總成本約低於 150 美元的受管供應商。前端使用 Vercel,後端容器使用 Railway,Postgres 使用 Supabase,Redis 使用 Railway,物件儲存使用 R2,邊緣網路使用 Cloudflare,監控使用 Sentry 和 Better Stack。一直沿用,直到有付費客戶詢問正常運行時間,或達到以下數字。
- 第 2 層,成長期: 在你自己的雲端帳戶中保持相同的架構形狀,但只在關鍵層級遷移:Postgres 使用搭配 PgBouncer 或 RDS Proxy 的 RDS 或 Aurora Multi AZ,容器使用 ECS Fargate 或小型 EKS,ElastiCache,S3 或 R2,所有基礎設施使用 Terraform,一個讀取複本,使用 Temporal 處理工作流程。觸發條件:付費客戶要求 SLA 或 SOC 2,或持續 5000 名月活躍使用者且資料庫 CPU 使用率超過 60%,或 PaaS 帳單超過每月 500 美元,或加入第二位工程師。
- 第 3 層,規模化: Shopify 和 Notion 運行的規模。Kubernetes 搭配自動擴展、分片或分割的主庫、Kafka 作為事件骨幹、透過 CDC 的資料倉儲、多區域讀取、Datadog 或 Grafana、一個平台團隊。觸發條件:持續每秒 1000 個請求、單一主庫接近 1 TB 或在添加複本和快取後寫入受限、需要多區域部署、或存在一個營運團隊來管理它。
移動層級是針對單一層級進行的,而不是一次性移動所有層級。資料庫通常最先移動,然後是後端,最後是前端,因為將前端留在原處是最便宜的選擇。
建置順序
- Schema、遷移腳本、角色、RLS(
references/database.md、references/security.md)。 - 身份驗證和租戶上下文中介軟體。
- 具有驗證和寫入冪等性的核心 API 路由。
- 速率限制和快取中介軟體(
references/rate-limiting.md、references/caching.md)。 - 工作者和佇列;任何緩慢或外部的操作都應從請求路徑中移出(
references/jobs-and-queues.md)。 - 針對 API 的前端。
- 針對相同 API 的行動端。
- 日誌、指標、追蹤、健康檢查、警報(
references/observability-and-reliability.md)。 - 啟動檢查點,在預期尖峰流量的三倍下進行負載測試,一次還原測試(
references/launch-checklist.md)。
PLAN.md 範本
# <專案名稱> 計畫
## 以三句話描述產品
## 使用者、租戶、角色
## 按層級劃分的供應商 (每個層級的層級、原因、觸發遷移的條件,來自 providers-and-tiers.md)
## 資料模型 (資料表、標記的 tenant_id、索引、分割資料表)
## API 表面 (按資源劃分的路由、驗證級別、速率限制層級)
## 背景任務 (佇列、觸發條件、冪等性鍵、重試策略)
## 第三方服務 (服務、用途、機密名稱、限制、故障模式)
## 快取對映 (什麼、鍵、TTL、哪些寫入會使其失效)
## 威脅模型 (前 5 大濫用案例及每種案例的控制措施)
## 規模假設 (上線時和使用者/資料成長 10 倍後的使用者數、行數、每分鐘請求數)
## 上線成本 (每個供應商、每月)
## 每個供應商的退出計畫 (適用的 migrations.md 劇本,每行一個)
## 圖表
不可妥協的規則
- 每個由租戶擁有的資料表都必須有
tenant_id;啟用並強制執行 RLS;API 根據驗證過的成員資格在每個事務中設定租戶,絕不從請求輸入中讀取。 - 客戶端永遠不會直接連線資料庫或持有特權金鑰。所有請求都必須經過 API。
- 所有輸入必須在邊界進行驗證,拒絕未知的鍵;所有查詢必須使用參數化查詢。
- API 和工作透過連線池進行連線,並使用小型連線池;每個角色都設定陳述式超時時間。
- 在邊緣網路和 API 中實施速率限制,以使用者、租戶和 IP 為鍵。快取鍵必須以租戶為範圍並帶有版本號。
- 機密存放於平台機密儲存庫中,每個環境一套,絕不提交到 Git 或行動裝置套件中。生產環境和測試環境必須是獨立的專案和帳戶。
- 啟用 PITR(時間點恢復),並至少測試還原一次。
- Webhook 簽章必須經過驗證,具有重播保護機制、冪等性,並在兩秒內回覆。
- 每次對外呼叫都必須設定超時;僅對具有冪等性的工作使用帶抖動的重試機制;失敗的工作必須對開發人員可見。
- 每條日誌行都必須包含請求 ID 和租戶 ID;針對錯誤率、p95 延遲、5xx 數量、佇列年齡、失敗工作、資料庫連線數設定警報。
行動端
- 現有的 React Native 或 Expo 應用程式:保留原樣。
- 新應用程式,僅限 iOS 或以 iOS 優先:使用原生 Swift 和 SwiftUI。到 2026 年,大型公司已轉向原生開發(Shopify 轉回 Swift 和 Kotlin,Notion 轉向 SwiftUI),因為編碼代理消除了第二個程式碼庫的成本;這同樣適用於小型團隊。僅在實際建置 Android 版本時才使用 Kotlin。
- 必須由一或兩人同時交付 iOS 和 Android 的新應用程式:使用 Expo。
- 無論哪種方式,應用程式只與 API 通訊,將權杖存放在安全儲存中,並且不在應用程式套件中包含任何機密資訊。
參考檔案
references/providers-and-tiers.md:每個層級的表格(大型公司實務、第 1、2、3 層供應商、觸發條件、成本)以及將專案描述轉化為選擇的選擇器規則。references/migrations.md:將每個層級向上遷移一層的逐步劇本。references/architecture.md:帶有層級標籤的圖表、三種請求路徑、儲存庫佈局、環境、邊緣網路和平台配置。references/security.md:租戶隔離和 RLS、身份驗證、機密、標頭、輸入、上傳、Webhook、加密、資料刪除和匯出、行動端、稽核日誌、事件處理。references/rate-limiting.md:四個層級,包含數字和中間軟體。references/caching.md:每個快取層級、鍵 Schema、TTL、失效策略、雪崩保護。references/database.md:Schema 規則、角色、連線池、遷移。references/observability-and-reliability.md:結構化日誌、OpenTelemetry、健康檢查、正常運行時間 SLA、警報、負載測試、災難復原。references/jobs-and-queues.md:佇列選擇(Sidekiq、Temporal、托管的佇列服務)、至少一次語意、冪等性、進度追蹤、SoR 作為持久性儲存、延遲和定期任務、佇列關閉。references/launch-checklist.md:安全性、可靠性、監控、資料庫、部署、第三方服務、法律/合規、負載測試、災難復原。references/cost-estimates.md:5000 個無伺服器使用者的成本模型,比例因子。
一旦專案開始,請要求我使用
references目錄中的參考檔案。