昨天,我們把候選時段演算法一路拆到最後,核心開發也走到了尾聲。
接下來,這些原本只在開發者電腦裡運作的功能,終於要真正走到使用者面前了。
一開始,我對 Deployment(部署)的理解其實很模糊。
只知道部署完成之後,網站就會有自己的連結。原本只能在我的電腦上跑的東西,也就終於可以分享給朋友們一起玩玩看了!
那時候,我覺得這件事很神奇,卻又不太明白它到底是怎麼發生的。
明明還是同一份 Code,為什麼離開 localhost 之後,就能從「只有我看得到」,變成「任何拿到連結的人都能使用」?
今天就從這個問題開始,進入「實際運行&優化」這一關的第一步,看看 Deployment 到底把一個網站帶去了哪裡吧~
Deployment(部署),可以先理解成:
把準備好的應用程式版本,送進指定的執行環境,讓它能在那裡建置、啟動並提供服務。
所以 Deployment 不只是把 Source Code 上傳到網路。
一份應用程式真的要運作,還會依賴:
例如在自己的電腦開發時,我們可能會在 Terminal 輸入:
npm run dev
接著用瀏覽器打開:
http://localhost:5173
網站就能正常運作。
但 localhost 指向的是:
目前正在使用的這台電腦。
朋友即使拿到這串網址,也只會連回他自己的電腦,不會自動跑進我的電腦看網站。我們也不能期待自己的電腦永遠開著 Terminal,替所有使用者維持服務。
因此,Deployment 真正要送出去的,不只是某個資料夾裡的 Code。
而是:
一個能在目標環境裡正確建置、啟動並持續提供服務的版本。
不過,這裡所說的「目標環境」到底是什麼?
明明還是同一份 Code,為什麼只是換個地方執行,就可能得到不一樣的結果?
Environment(環境),指的是:
程式實際執行時,周圍所有會影響它運作的條件。
開發流程裡常見的 Environment,可以先分成三種:
| Environment | 主要用途 |
|---|---|
| Development(開發環境) | 開發者平常寫 Code、除錯、測試功能的地方 |
| Testing / Staging(測試/預備環境) | 正式上線前,用來執行測試,或在接近正式環境的條件下驗證 |
| Production(正式環境) | 真正提供給使用者使用的環境 |
不同團隊不一定都有完全相同的三套環境。
有些小型專案只有 Development 和 Production;有些團隊則會把 Testing、Staging、UAT 分得更細。
重點不在名稱一定要怎麼取,而是每個 Environment 負責哪一階段的驗證。
以 BuJo 來說,Code 合併進 dev 後,會先部署到測試環境;確認穩定之後,才繼續合併到 main,進入正式環境。
這裡也要分清楚:
dev 和 main 是 Branch,不是 Environment。
只是 BuJo 把不同 Branch 的變化,設定成會觸發不同環境的部署。
因此,測試環境的價值不只是多準備一個網址。
它真正多出來的是:
一個讓部署後才會出現的問題,先被看見的地方。
很多設定在本機早就存在,所以平常開發時不太會注意。
但 Code 被 Push 到 GitHub,不代表這些條件也會自動出現在新的 Environment 裡。
因此部署時,通常還要一起確認:
| 要確認什麼 | 本機正常,部署後卻可能出問題的原因 |
|---|---|
| Environment Variables | .env 通常不會跟著 Git 上傳,部署平台需要另外設定 |
| Dependencies | 目標環境必須知道要安裝哪些 Package |
| Build / Start Command | 平台要知道怎麼建置,以及怎麼啟動應用程式 |
| Runtime / Version | Node.js、作業系統、Timezone 等執行條件可能不同 |
| URL / Domain | localhost 要換成正式的 Frontend、Backend 與 Callback URL |
| Database Migration | Code 已經使用新 Schema,Database 卻可能還停在舊版本 |
| External Services | OAuth、Storage、Email 等服務也可能需要正式網址與 Credential |
例如 Backend 本機可能已經設定好:
DATABASE_URL
JWT_SECRET
GOOGLE_CLIENT_ID
GOOGLE_CLIENT_SECRET
但 Password、Secret、API Key 這類敏感資訊,本來就不應該直接寫進 Code,或跟著 Commit 進 Git。
所以部署到 Render、Vercel 或其他平台時,還要在對應的 Environment 裡另外設定。
這裡還有一個容易忽略的差異:
Environment Variable 是在 Build Time 還是 Runtime 被讀取?
例如 Vite 的 VITE_ Environment Variables,會在 Build Time 進入前端產物,最後也能被瀏覽器讀到。
因此:
VITE_ 變數不能拿來存放 Secret所以 Deployment 真正要確認的,不只是:
Code 有沒有成功上傳?
而是 Code 周圍那整套執行條件,有沒有一起準備好。
假設每次準備合併 Code 前,都要由工程師自己記得:
一兩次可能還記得。
但當團隊成員和修改次數變多,只靠每個人手動執行,就很容易少做其中一步。
所以開發流程會把適合重複執行的檢查自動化。
這就來到:
CI(Continuous Integration,持續整合)。
CI 的核心概念是:
開發者持續把修改整合進共同的 Codebase,並透過自動化檢查,盡早發現整合問題。
常見的 CI 檢查包括:
| 檢查 | 主要確認什麼 |
|---|---|
| Lint | Code 有沒有違反設定好的規則 |
| Unit Test | 單一函式或模組的行為是否正確 |
| Integration Test | 多個元件或模組接在一起後是否正常 |
| Build | 專案能不能成功產生可部署的版本 |
| Type Check | 型別是否符合規則 |
| Security Scan | 是否出現已知的 Dependency 或程式碼風險 |
不同專案不一定會把這些檢查全部放進 CI。
重點是根據專案真正需要把關的事情,挑出適合自動執行的項目,再把它們固定進同一套流程。
所以 CI 不等於 Test。
Test 很重要,但它只是 CI Pipeline 裡可能出現的一個關卡。
CI 和 Code Review 看的是不同問題。
| 檢查方式 | 適合處理的問題 |
|---|---|
| CI 自動檢查 | Test 有沒有通過、格式有沒有違規、專案能不能 Build |
| Code Review | 設計是否合理、命名是否清楚、需求有沒有漏掉、後續是否容易維護 |
CI 擅長檢查那些可以被寫成明確規則、反覆自動執行的事情。
但它很難只靠一條規則判斷:
這個函式是不是承擔太多責任?
這個命名會不會讓下一個人誤解?
這個功能雖然能跑,但真的符合需求嗎?
因此自動化不是取代 Review,而是先把適合機器處理的部分交給機器。
CI 後面經常接著看到 CD。
不過 CD 可能代表兩個不同概念:
| 名稱 | 核心差異 |
|---|---|
| Continuous Delivery(持續交付) | 自動把版本準備到隨時可以發布的狀態,最後是否進入 Production 可以保留人工決定 |
| Continuous Deployment(持續部署) | 通過整套流程後,自動繼續部署到 Production |
兩個名字很像,差異主要在最後進入 Production 前,是否還保留人工決定。
假設 Code 已經通過所有檢查:
因此大家說的 CI/CD Pipeline,可以理解成:
Code 從整合、自動檢查,一路走到可以交付或部署的整套流程。
Pipeline 不一定只有一種正確走法。
有些團隊會在最後保留人工核准,有些團隊則會在所有條件通過後自動部署;有些專案還會加入安全掃描、端對端測試或其他關卡。
真正需要先想清楚的不是「一定要自動到哪一步」,而是:
一份 Code 必須滿足哪些條件,才有資格前往下一個 Environment?
GitHub Actions 是 GitHub 提供的 Workflow Automation(工作流程自動化)工具。
它可以設定:
當某個 Event 發生時,自動執行哪些 Job。
一份 GitHub Actions Workflow 裡,常會遇到幾個重要角色:
| 名稱 | 負責什麼 |
|---|---|
| Event | 什麼事情發生時啟動,例如 Push 或 Pull Request |
| Workflow | 整套自動化流程 |
| Job | 一組要完成的工作,例如 Test 或 Build |
| Step | Job 裡依序執行的單一步驟 |
| Runner | 實際執行 Workflow 的環境 |
放回 BuJo 的 CI,其中一個 Event 設定是:
on:
pull_request:
branches: [dev, main]
這段可以直接讀成:
當有人建立以
dev或main為目標的 Pull Request 時,啟動這套 Workflow。
接下來,Frontend 和 Backend 會分別執行自己需要的檢查:
| 專案 | CI 主要步驟 |
|---|---|
| Frontend | 準備 Node.js → npm ci → Lint → Vitest → Build |
| Backend | 準備 PostgreSQL 與 Environment Variables → npm ci → 套用 Prisma Migration → 產生 Prisma Client → Jest Test |
這些檢查不是直接在我的 MacBook 裡執行。
GitHub Actions 會在 Runner 裡重新準備專案,再按照 Workflow 設定好的 Step 依序執行。
因此它不只是在確認:
「我的電腦現在跑得過 Test。」
也會進一步確認:
「這份專案放進 Workflow 指定的 Runner 後,還能不能重新完成安裝、建置和測試?」
這裡的 Runner 是執行 CI 檢查的環境,不等同於 BuJo 部署後使用的測試環境或 Production。
CI/CD 是開發、檢查、交付與部署的流程概念。
GitHub Actions 則是可以拿來實作這些流程的工具之一。
就像 Test 是一種開發方法,Jest、Vitest 是執行 Test 的工具;CI/CD 是流程,而 GitHub Actions、GitLab CI/CD、Jenkins 則是能把流程自動化的工具。
GitHub Actions 可以只負責 CI:
也可以繼續負責 Deployment:
實際做到哪裡,要看專案怎麼設定。
所以看到一個專案「有 GitHub Actions」,不能直接推論它已經完成整套 CI/CD。
還是要打開 Workflow,看看裡面究竟設定了哪些 Event、Job 和 Step。
BuJo 就曾經在 Demo 前一天,正面撞上一個由 Environment 差異造成的 Bug。
建立活動後,本機顯示的時間完全正常;部署到測試環境,同一個功能卻固定差了 8 小時。
當時大家真的超崩潰啊!!!
隔天就要 Demo 了,明明已經在本機再三確認過沒有問題,怎麼一部署,時間還是整整差了 8 小時?
更讓人困惑的是,Code 明明是同一份,問題也不在活動本身的業務邏輯,而是程式處理時間時依賴了系統時區。
本機使用台北時區,部署環境卻有自己的預設值。
同一段時間處理邏輯進入不同的 Environment,因而得到了不同的結果。
後來,Backend 明確設定:
TZ=Asia/Taipei
測試與 CI 使用的 Environment 也一起固定,避免每個地方各自依賴不同的預設時區。
這個 Bug 最直接地證明了一件事:
本機測試正常,只能證明 Code 在目前的 Development Environment 正常。
換到另一套執行條件後,還是要在那個 Environment 裡重新驗證。
那麼今天的主題——Deployment(部署),在 Vibe Coding 和專業開發上的差異在哪呢?
現在請 AI 建立 CI Workflow、設定部署平台,甚至一步一步把網站 Deploy 出去,都可以很快。
輸入幾個指令後,原本只存在本機的網站也真的有了網址。
但如果需求只寫成:
「幫我把網站部署上去。」
最後的驗收就很容易停在:
「網址打得開。」
Environment Variables、Database Migration、OAuth Callback URL,或 Runtime 的差異,仍然可能沒有被檢查。
差別不在 AI 能不能完成 Deployment。
而是使用 AI 時,有沒有把 Code 以外的執行條件,也一起放進「成功部署」的標準裡。
一開始,我最期待 Deployment,是因為網站終於有了自己的連結,可以真的傳給朋友使用。
現在再看那張連結,我還是覺得這件事很神奇,只是終於知道,它背後不只是按下一次 Deploy。
Code 離開本機之後,會進入另一套 Runtime、Database、Domain 與 Configuration;而 CI/CD 的價值,就是把沿途重要的檢查與部署步驟固定下來,讓它們不必每一次都靠人重新記得。
那張可以分享出去的連結,不只是網站被放上網路的證明,也代表這份 Code 已經走過一條能被檢查、也能反覆執行的路。
網站終於真的走到了使用者面前。
接下來,當使用者和資料越來越多,原本一下就能查到的內容,會不會開始變慢?
下一篇,就回到 Day6 曾經先看過一眼的 Index(索引),看看它到底怎麼影響 Database Query 的效能。