Linear 很快。这一点值得肯定。他们在感知性能上投入了大量精力,对大多数团队来说,它是最好的 SaaS issue 追踪器。但"最好的 SaaS"附带一些开发者无法接受的限制:你的数据存在别人的服务器上,你的工作流需要迁就他们的设计理念,每次交互都要付出网络往返的开销。
这篇文章是写给那些碰到了这些墙的开发者。也许你在管理一支每小时提交 50 个 issue 的 AI agent 团队。也许你在断网或离线优先的环境中工作。也许你只是不想在你和你的 issue 之间隔着一个登录页面。以下是我们在构建 Beadbox(一个将所有数据保存在本地的原生桌面 issue 追踪器)过程中的经验。
跳转到你关心的内容:
- 本地优先的速度 -- 在真实数据集上的 CLI 和 UI 级别的计时
- Git 原生历史 -- 对 issue 数据库进行分支、diff 和合并
- 离线/断网场景 -- 无网络、无守护进程、无问题
- 脚本和 agent -- 三个可直接复制粘贴的工作流
- 各种权衡 -- 在你投入时间之前的诚实说明
- 团队决策矩阵 -- 哪种工具适合哪种团队
- 从 Linear 迁移 -- 已有的和还没有的
为什么开发者在寻找 Linear 的替代方案
常见的回答是"Linear 太固执己见了"。这是对的,但不够精确。Linear 强制要求周期、团队结构和工作流状态,假设你是一个按两周节奏发版的产品团队。如果你的情况确实如此,Linear 很好。如果你是一个协调 AI agent 的独立开发者,或者一个有非标准迭代模式的研究团队,或者一个需要把 issue 绑定到 git commit 而不是 Slack 对话的 DevOps 团队,Linear 的固执就变成了阻力。
更深层的问题是架构层面的。Linear 是一个云优先的 SaaS 产品。每次修改都要到他们的服务器走一个来回。每次查询都依赖于他们的正常运行时间。你的 issue 数据存在他们的数据库中,通过他们的 API 查询,按照他们的规则。对大多数团队来说,这是个可以接受的交换。对于关心数据主权、离线访问或大数据集原始查询速度的开发者来说,这是不可接受的。
Beadbox 做不到什么
在介绍 Beadbox 的优势之前,先说说它不适合的场景。跳过这个章节对你没有好处;在采用工具之后才碰到这些限制才是真正的损失。
没有多用户权限或访问控制。 没有用户账号、没有角色、没有按 issue 的可见性限制。任何对 .beads/ 目录(或 Dolt 服务器)有文件系统访问权限的人都可以读写一切。如果你需要限制谁能看什么,目前 Beadbox 不适合你。
有限的实时协作。 两个人可以在同一组 issue 上工作,但协作模型是推/拉(类似 Git),不是实时光标和在线状态指示器。在服务器模式下,Beadbox 每 3-5 秒轮询一次变更。在嵌入式模式下,文件系统监听检测变更更快(亚秒级),但两个进程并发写入同一个 Dolt 数据库可能会崩溃。安全模式是:一次只有一个写入者,或者使用服务器模式让 Dolt 处理并发。
没有与 Slack、GitHub、Figma 或其他 SaaS 工具的集成。 扩展点是 bd CLI 和 shell 脚本。如果你的工作流依赖于"issue 关闭时触发 Slack 消息",你需要自己搭建这个胶水层。
规模上限是真实存在的,但还比较远。 我们在万级和两万级 issue 数据集上进行测试(见下方基准测试)。这些都处理得很好。我们还没有在十万级以上的 issue 上进行压力测试。如果你是一个每年生成数十万个 issue 的大型组织,这不是经过验证的领域。
没有非技术利益相关者的访问方式。 没有 web 门户、没有访客查看器、没有可分享的仪表盘 URL。Beadbox 是一个读取本地数据库的桌面应用。向不使用你的机器的 PM 展示进度意味着屏幕共享或一个生成报告的 bd 脚本。
Beadbox 如何工作(30 秒版本)
在基准测试有意义之前,先了解架构:
嵌入式模式: Dolt 数据库存在于你文件系统上的 .beads/ 目录中。没有服务器进程,没有守护程序。bd CLI 直接读写。Beadbox 通过 fs.watch() 检测变更,带有 250ms 的防抖,通过 WebSocket 广播到 UI。这是零配置的路径。
服务器模式: 一个 dolt sql-server 进程单独运行(本地或局域网)。bd CLI 通过 MySQL 协议连接。Beadbox 每 3-5 秒轮询服务器查询变更,而不是监听文件系统。此模式支持多个并发写入者。
GUI 执行的每个操作都通过 bd CLI 路由。Beadbox 从不直接触碰数据库。如果 bd show 和 Beadbox 不一致,那是 Beadbox 的 bug。
性能:万级 issue 数据集上的真实基准测试
beads CLI 发布了基准测试,你可以在自己的硬件上复现。以下是在 M2 Pro 上对 10,000 个 issue 的 Dolt 数据库运行 Go 基准测试套件的真实数据:
| 操作 | 耗时 | 内存 | 数据集 |
|---|---|---|---|
| 过滤就绪工作(未阻塞 issue) | 30ms | 16.8 MB | 10K issue |
| 搜索(全部开放,无过滤) | 12.5ms | 6.3 MB | 10K issue |
| 创建 issue | 2.5ms | 8.9 KB | 10K issue |
| 更新 issue(状态变更) | 18ms | 17 KB | 10K issue |
| 循环检测(5K 线性链) | 70ms | 15 KB | 5K 依赖 |
| 批量关闭(100 个 issue) | 1.9s | 1.2 MB | 顺序写入 |
| 同步合并(10 创建 + 10 更新) | 29ms | 198 KB | 批量操作 |
这些是 CLI 级别的基准测试:bd 从本地 Dolt 数据库读取或写入的时间。Beadbox UI 在此基础上增加了渲染开销。我们全栈的设计目标(CLI 调用 + React 渲染 + WebSocket 传播)是:
| UI 操作 | 设计目标 |
|---|---|
| Epic 树渲染(100+ issue) | < 500ms |
| 应用/清除过滤器 | < 200ms |
| 工作区切换 | < 1 秒 |
| 实时更新传播(嵌入式) | < 2 秒 |
| 冷启动到可用 | < 5 秒 |
我们不发布与 Linear 或其他追踪器的对比基准测试,因为我们没有做过受控比较,而精挑细选的数据也不诚实。我们能说的是:整个数据路径是本地的。从点击过滤器到看到结果之间没有网络跳转。这对你是否重要取决于你的基线。如果 Linear 对你的数据集规模和网络条件来说足够快,那它可能确实够了。如果你在酒店 WiFi 上用 500 个 issue 的积压感受过延迟,你就知道这些数字解决的是什么问题。
要复现:克隆 beads,运行 go test -tags=bench -bench=. -benchmem ./internal/storage/dolt/...,然后与你的硬件进行对比。缓存的数据集在 /tmp/beads-bench-cache/。
Git 集成深度:不仅仅是把 commit 关联到 issue
大多数 issue 追踪器把 Git 集成当作功能清单上的一个勾选项:在 commit message 中提及 issue ID,issue 上就会出现一个链接。这很有用,但很浅。
Beadbox 建立在 beads 之上,这是一个以 Git 语义为存储层的 issue 追踪器,而不是后加的集成。底层数据库 Dolt 为结构化数据实现了 Git 的 merkle 树数据模型。每次 issue 变更都是一次 commit。每次 commit 都有父节点。你可以对 issue 历史使用 dolt diff、dolt log 和 dolt merge,语义与你在代码上使用的完全相同。
实际意义是什么:
你的 issue 历史是可审计的。 数据库本身就是一个 commit 图谱。你可以 diff 任意两个时间点,精确地看到哪些 issue 的哪些字段发生了变更。这不是后加的"审计日志功能"。存储格式本身就是审计轨迹。
分支不仅适用于代码,也适用于 issue。 Dolt 原生支持分支。你可以为 issue 数据库创建分支来试验重新组织,然后合并回去或直接丢弃。
同步是推/拉,不是 API 调用。 多机协作就像 git push 和 git pull 一样。不需要 API token、不需要 webhook、不需要 OAuth 流程。把 Dolt remote 指向一个服务器(或 DoltHub)然后推送。另一台机器拉取。
关于冲突的说明: Dolt 使用三路合并,与 Git 相同。如果两个人编辑了同一个 issue 的不同字段,合并会自动解决。如果两个人编辑了同一个 issue 的同一个字段,你会得到一个需要通过 Dolt CLI(dolt conflicts resolve)手动解决的冲突。Beadbox 还没有冲突解决 UI;你在 dolt 层处理冲突。实际上,当每个人(或 agent)处理不同的 issue 时,冲突很少发生,这也是典型模式。但如果你的团队经常同时编辑相同的 issue,这是一个你应该知道的摩擦点。Dolt 合并文档 详细介绍了冲突解决工作流。
原生渲染:为什么我们在 Tauri 中打包 Node.js
Linear 在浏览器标签中运行。Jira、Asana 和其他所有 SaaS 追踪器也是如此。浏览器标签竞争内存、被操作系统挂起,并通过合成器渲染,增加帧延迟。
Beadbox 作为基于 Tauri 的原生桌面应用运行。Tauri 应用通常很小(Tauri 运行时本身只有个位数兆字节),因为它们使用操作系统原生 WebView 而不是打包 Chromium。我们的包比典型的 Tauri 应用大,约 160MB,这是一个值得解释的有意取舍。
其中 84MB 是嵌入的 Node.js 运行时。我们使用 sidecar 架构:Tauri 将 Next.js 服务器作为子进程启动,处理服务端渲染、Server Actions 和用于实时更新的 WebSocket 层。Tauri WebView 指向这个本地服务器。我们选择这个方案而不是纯 Rust 后端,因为 Next.js 生态系统提供了 React Server Components、Server Actions 和 UI 层的快速迭代速度。代价是包体积。等效的 Electron 应用会超过 400MB。纯 Rust + Tauri 应用会不到 10MB,但开发时间要多 3 倍,而且会失去 React 生态。
与浏览器标签的实际区别:Beadbox 在一个专用的 WebView 进程中渲染,不与你的其他 47 个浏览器标签共享内存。展开一个包含 100 多个嵌套 issue 的 epic 树、在完整积压上应用过滤器、在工作区之间切换:当渲染器不需要竞争资源时,这些操作的感觉有质的不同。
通过 CLI 扩展,而不是 REST API
Linear 有一个 GraphQL API。设计得很好。但扩展 Linear 意味着编写与他们服务器通信、使用他们的 token 认证、处理他们速率限制的代码。
Beadbox 采用了不同的方式:bd CLI 就是 API。GUI 执行的每个操作都通过 bd,你在终端中使用的同一个命令行工具。
以下是三个你今天就可以复制粘贴的工作流:
批量更新优先级用于分拣扫描:
# 将所有开放的 bug 设置为优先级 1(关键)
bd list --status=open --type=bug --json | \
jq -r '.[].id' | \
xargs -I{} bd update {} --priority=1
生成每日状态摘要:
# 过去 24 小时有什么变化?
echo "=== Closed today ==="
bd list --status=closed --json | \
jq -r '.[] | select(.updated > (now - 86400 | todate)) | "\(.id) \(.title)"'
echo "=== Currently blocked ==="
bd blocked --json | \
jq -r '.[] | "\(.id) \(.title) (blocked by: \(.blocked_by | join(", ")))"'
echo "=== Ready to work ==="
bd ready --json | jq -r '.[] | "\(.id) [P\(.priority)] \(.title)"'
AI agent 创建并认领工作:
# Agent 发现一个 bug,提交并认领
ISSUE_ID=$(bd create \
--title "Fix race condition in auth middleware" \
--type bug \
--priority 1 \
--json | jq -r '.id')
bd update "$ISSUE_ID" --status=in_progress --assignee=agent-3
# ... agent 完成工作 ...
bd update "$ISSUE_ID" --status=closed
bd comments add "$ISSUE_ID" --author agent-3 \
"Fixed in commit abc1234. Root cause: mutex not held during token refresh."
如果你在运行 AI 编码 agent(Claude Code、Cursor、Copilot Workspace),它们已经知道怎么运行 CLI 命令。不需要 API 客户端库,不需要认证流程。只是 Unix 管道和 shell 脚本。
试试 Beadbox 看看这些工作流在 agent 执行时的实时可视化效果。
离线优先不是一个功能,而是一种架构
一些云端追踪器提供"离线模式",缓存最近的数据并在重新连接时同步。这是在根本上在线的架构上附加的功能。故障模式是可预见的:过期缓存、同步冲突、静默排队后来又失败的操作。
Beadbox 能离线工作,因为它从一开始就不是在线的。在嵌入式模式下,你的整个 issue 数据库就是文件系统上的一个目录。没有服务器进程。没有守护程序。没有网络套接字。bd CLI 直接读写那个目录。Beadbox 用 fs.watch() 监听它并渲染发现的内容。
没有什么需要同步,因为根本没有远程端。如果你后来选择协作,Dolt 的推/拉提供了明确的、可见的同步。但默认是本地的。默认是你自己的。
安全性如何? 如果你在为断网或敏感环境评估 Beadbox,以下是具体的安全态势:
- 静态加密: Beadbox 本身不加密
.beads/目录。它依赖操作系统级别的磁盘加密(macOS 的 FileVault、Linux 的 LUKS、Windows 的 BitLocker)。如果你的威胁模型要求按数据库加密,这是一个缺口。 - 备份: 你的
.beads/目录就是一个普通目录。cp -r、rsync、Time Machine 或dolt push到远程都可以。Dolt 的 commit 历史也意味着意外更改可以用dolt reset回滚。 - 什么会离开机器: 在嵌入式模式下,什么也不会。零网络调用。在桌面应用中,存在两个可选的出站连接:GitHub API 检查 Beadbox 更新(可在设置中禁用),以及 PostHog 分析(如果你选择开启;默认禁用,不收集 PII)。两者都不传输 issue 数据。
对于断网环境、机密项目或在飞机和火车上工作的开发者来说,这不是锦上添花。这是唯一可行的架构。
为你的团队选择合适的工具
没有一种工具在所有情况下都是正确的。以下是诚实的分析:
选择 Linear 如果:
- 你的团队有 10 人以上,需要集中式项目管理
- 你依赖 Slack/GitHub/Figma 集成
- 非技术利益相关者需要访问你的 issue 追踪器
- 你想要托管基础设施,零运维开销
- 你是一个按固定周期发版的产品团队
选择 Beadbox 如果:
- 你重视数据主权(issue 永远不离开你的机器)
- 你经常离线工作或在受限网络环境中
- 你管理需要以编程方式读写 issue 的 AI agent
- 你想要 Git 原生的 issue 历史(对 issue 进行分支、diff、合并)
- 你偏好 CLI 优先的工作流,在需要时有可视化辅助
- 你是独立开发者或小团队(1-10 人),不需要企业级功能
继续使用当前工具如果:
- 切换成本超过你正在经历的摩擦
- 你的团队已经在依赖当前追踪器 API 的集成上投入了大量精力
- 你的工作流已经适应了你工具的设计理念
从 Linear(或其他追踪器)迁移
直说吧:目前没有自动化的 Linear 到 Beadbox 迁移工具。没有 CSV 导入向导,没有 API 桥接,没有状态映射 UI。
如果你从零开始,没问题。bd init,开始创建 issue,Beadbox 立即就能看到。零摩擦。
如果你有一个现有的 Linear 项目想迁移过来,目前可行的路径是脚本化的:从 Linear 的 API 导出(他们支持 CSV 和 API 导出),转换数据,然后循环调用 bd create 重新创建 issue。你会丢失 Linear 特有的元数据(周期、项目视图、SLA 计时器),但能保留标题、描述、优先级和状态。迁移脚本是一个周末项目,不是一个季度的集成工程。
我们知道这对于有数千个 issue 和多年历史的团队来说还不够好。构建一个合适的导入管道在我们的路线图上,但还没有完成。如果迁移摩擦是你的主要顾虑,等我们构建好后再来,或者评估从零开始对你的场景是否可接受。
开始使用
Beadbox 在 beta 期间免费。用 Homebrew 安装:
brew tap beadbox/cask && brew install --cask beadbox
如果你已经在使用 beads,Beadbox 会自动检测你现有的 .beads/ 工作区。打开应用,你的 issue 就在那里。无需导入步骤。无需创建账号。
如果你是 beads 新用户,Beadbox 会引导你初始化第一个工作区。不到 60 秒你就能看到你的 issue。
下载 Beadbox 或查看 beads 来看看本地优先的 issue 追踪是否适合你的工作流。