我過去經驗主要是從使用者視角來打造我心中想要的產品。對於如何把產品(尤其是後端服務)做到足夠穩健(robust)並不是我的強項。所以我選擇直接用官方 CLI 來開始建構一個專案。觀察 Effect 團隊預設的「起手式」是什麼?有用什麼樣的套件?那讓我們開始吧!但如果你只是想來學習 Effect 的話,也可以直接跳過這一個章節,直接從下下一篇開始。
我偏好用 pnpm 建立專案:
# pnpm
pnpm create effect-app@latest
畫面會出現互動式精靈,依序詢問下列選項;我把自己的選擇附在下面:
✔ What is your project named? … effect-app
✔ What type of project would you like to create? … Template
✔ What project template should be used? … Basic
✔ Initialize project with Changesets? … on
✔ Initialize project with a Nix flake? … on
✔ Initialize project with ESLint? … on
✔ Initialize project with Effect's recommended GitHub actions? … on
cd effect-app
pnpm install
pnpm tsx ./src/Program.ts
# 輸出:
# timestamp=2025-09-10T17:39:52.827Z level=INFO fiber=#0 message="Hello, World!"
如果都成功的話,建議先初始化 git repo (git init),Effect CLI 沒有幫你處理這一個部分。然後我們就來看一下,Effect CLI 幫我們做了哪些事情。
ESLint 我就不多說啦~基本上網站開發必備。如果有用網站開發框架,例如 Next.js、Vite 等,通常會內建安裝 ESLint。我這裡假設大家都很熟悉,所以我們就只講 GitHub Actions、Changesets 和 Nix flake 這三個也許對 Junior 的工程師較陌生的選項。
GitHub Actions 是 GitHub 提供的事件驅動自動化服務(CI/CD 與各式工作流)。你可以在 .github/workflows/*.yml
定義「工作流程(workflow)」:
它可用來自動執行型別檢查、Lint、測試、建置、建立版本 PR、發佈到 npm、產生快照套件、部署到雲端等,讓重複性流程標準化並在 PR/主線上持續把關品質。
.github/workflows/
裡放的是自動化流程的設定檔(YAML)。下面就用目前專案裡的三個檔案,逐一解釋它們在做什麼:
name: Check
on:
workflow_dispatch:
pull_request:
branches: [main]
push:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions: {}
jobs:
build:
name: Build
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Install dependencies
uses: ./.github/actions/setup
- run: pnpm codegen
- name: Check source state
run: git add src && git diff-index --cached HEAD --exit-code src
types:
name: Types
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Install dependencies
uses: ./.github/actions/setup
- run: pnpm check
lint:
name: Lint
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Install dependencies
uses: ./.github/actions/setup
- run: pnpm lint
test:
name: Test
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Install dependencies
uses: ./.github/actions/setup
- run: pnpm test
workflow_dispatch
、對 main
的 pull_request
與 push
concurrency
),避免重複浪費資源pnpm codegen
後用 git diff-index --cached HEAD --exit-code src
檢查產物是否乾淨,避免忘記提交產生碼pnpm check
做型別檢查pnpm lint
做語法/風格檢查pnpm test
執行測試./.github/actions/setup
安裝環境與依賴name: Release
on:
push:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
permissions: {}
jobs:
release:
if: github.repository_owner == 'Effect-Ts'
name: Release
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
id-token: write
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Install dependencies
uses: ./.github/actions/setup
- name: Create Release Pull Request or Publish
id: changesets
uses: changesets/action@v1
with:
version: pnpm changeset-version
publish: pnpm changeset-publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
push
到 main
if: github.repository_owner == 'Effect-Ts'
(目前只在 Effect-Ts
擁有的 repo 才會執行)
EricTsai83
, 這是我的帳號,請別用這./.github/actions/setup
changesets/action@v1
pnpm changeset-version
pnpm changeset-publish
GITHUB_TOKEN
(自帶)與 NPM_TOKEN
(你需在 Repo Settings → Secrets 設定)main
後會 bump 版本、產生 changelog 並嘗試發佈到 npmname: Snapshot
on:
pull_request:
branches: [main, next-minor, next-major]
workflow_dispatch:
permissions: {}
jobs:
snapshot:
name: Snapshot
if: github.repository_owner == 'Effect-Ts'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Install dependencies
uses: ./.github/actions/setup
- name: Build package
run: pnpm build
- name: Create snapshot
id: snapshot
run: pnpx pkg-pr-new@0.0.24 publish --pnpm --comment=off
main
、next-minor
、next-major
的 PR;或手動 workflow_dispatch
if: github.repository_owner == 'Effect-Ts'
pnpm build
pnpx pkg-pr-new@0.0.24 publish --pnpm --comment=off
以上三個 Workflow 搭配 Changesets 設定,可以把「品質檢查 → 版本 PR → 合併後發佈 → PR 快照測試」串成一條穩健的產品級發佈管線。若你要在自己的帳號下啟用發佈與快照,請務必在 Actions secrets 新增 NPM_TOKEN
。但如果你只是來學習 Effect 的話,就先跳過這一部分吧~我自己也不太確定30 篇的文章篇幅,最終文章會不會包含這一個部分。
但如果你想嘗試的話,步驟如下:
這樣 GitHub Actions Workflow 執行時,就可以透過 ${{ secrets.NPM_TOKEN }}
使用這個 Token。
.github/actions/setup/action.yml
pnpm/action-setup@v3
)actions/setup-node@v4
),開啟 pnpm
快取pnpm install
node-version
(預設 20.16.0
),可在使用時覆寫。使用方式(可選覆寫 Node 版本):
- name: Install dependencies
uses: ./.github/actions/setup
with:
node-version: 20.16.0
Changesets 是一個為 Monorepo 設計、同樣適用於單一專案的版本與發佈管理工具。每次你修改功能或修 bug,只要新增一個 changeset 檔(YAML/Markdown),把哪些套件有改變、變更等級(major/minor/patch),以及發佈後要寫進 changelog 的內容說清楚即可。
版本變動有跡可循,不用到處手動改 package.json;changelog 可以自動產生,方便團隊與社群追蹤;而且搭配 GitHub Actions,在 PR merge 後就能自動決定是否 bump 版本並發佈到 npm。
如果只是 demo 或 side project,關掉也無妨;但若你要維護可重用套件、發佈到 npm,或管理 Monorepo,建議開啟,幾乎是必備。
如果你是用 Effect CLI 建立專案,在互動精靈中勾選了 Changesets,會自動安裝並初始化。
初始化後會產生 .changeset/
資料夾和 config.json
設定檔。一開始的設定檔如下:
{
"$schema": "https://unpkg.com/@changesets/config@3.0.2/schema.json",
"changelog": [
"@changesets/changelog-github",
{
"repo": "<PLACEHOLDER>"
}
],
"commit": false,
"fixed": [],
"linked": [],
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}
@changesets/changelog-github
,需將 repo
改為實際的 owner/repo
(目前為 <PLACEHOLDER>
)。例如,"repo": "EricTsai83/effect-app"
。changeset version
時,Changesets
只會更新各 package.json
的版本號與 CHANGELOG.md
,不會自動建立 Git commit。這代表你需要手動執行 git add
與 git commit
來保存變更。{
"fixed": [
["@acme/ui", "@acme/docs"]
]
}
// 效果:
// 1. 只要 @acme/ui 改版 → @acme/docs 即使沒變動也會同步 bump 同一個版本。
// 2. 所以如果這次變更需要 @acme/ui 從 1.2.0 → 1.3.0,那麼 @acme/docs 也會被強制一起升到 1.3.0。
// 3. 這樣能保證這兩個套件的發佈版本號 永遠一致。
{
"linked": [
["@acme/ui", "@acme/docs"]
]
}
// 效果:
// 假設現在版本號是:A=1.5.0、B=2.3.1。如果 A 有改動需要 minor bump,B 也會 minor bump。結果就會變成 A=1.6.0、B=2.4.0
main
)。當你在主要分支(baseBranch,例如 main)執行 changeset version
,Changesets 會:
package.json
裡的版本號與 CHANGELOG.md
pnpm changeset
# 互動式選擇有影響的套件、版本等級(major/minor/patch),並撰寫摘要
pnpm changeset status
main
後進行版本號與 changelog 生成:pnpm changeset-version
補充:你可能一開始運行的時候會失敗,這是因為 CLI 初始化時,幫你建的 script 運行語法是 changeset version && node scripts/version.mjs
,但他卻沒有給你能運行的 script。我這邊參考官方 GitHub 寫了一個版本,如下:
路徑: scripts/version.mjs
import * as Fs from "node:fs"
import Package from "../package.json" with { type: "json" }
const tpl = Fs.readFileSync("./scripts/version.template.txt").toString("utf8")
Fs.mkdirSync("src/internal", { recursive: true })
Fs.writeFileSync(
"src/internal/version.ts",
tpl.replace("VERSION", Package.version)
)
路徑: scripts/version.template.txt
/** @internal */
export const moduleVersion = "VERSION"
我們先前有講package.json 內的版號是靠 changeset 來變更。而我們可以利用讀取 package.json 內的版號,來生成 src/internal/version.ts
檔案。把 package.json 的版本在編譯時「寫進程式碼」成常數,執行時直接 import 使用,不需要讀檔或動態載入。讓程式內的版號可以與實際版號同步。方便在日誌、錯誤回報、HTTP 標頭/UA、CLI --version 中引用。進一步提升可觀測性。
這一步會根據 .changeset/*.md
決定每個套件的新版本,更新 package.json
與 CHANGELOG.md
。
NPM_TOKEN
並完成登入/權限設定):pnpm changeset-publish
不過研究如何發佈 npm 套件不是本文的重點。所以就不多做介紹啦~😀
本篇示範以 pnpm 建立並驗證 Effect 專案,並整理 GitHub Actions 與 Changesets 的關鍵設定與實務流程;下一篇將介紹 Nix flake 在此專案中的定位與作用。