
先从一个很常见的研发场景说起。很多团队都在做同一个项目需求评审开了三轮架构设计文档写了二十页数据库表设计到第六版权限系统、审计日志、消息队列、多租户方案全部规划完毕。然后到了真正的开发阶段发现第一个版本光是最小可用功能就做了三个月。上线后用户根本不关心权限模型只问了一个问题为什么保存按钮点了没反应这种“做了很多却没做出真实可用东西”的状态在软件行业太常见了。它和团队能力无关和流程、判断、资源分配方式高度相关。今天想聊的是这本经典小书里的产品方法论Getting Real。它由 37signals也就是后来 Basecamp 的团队提出核心观点非常反常规与其做大而全的规划不如更早地把一个真实、瘦小、能用的东西做出来然后用它去验证一切。从开发者视角看这不是理想主义而是最务实的一种工程策略。本文会先解释 Getting Real 到底是什么、它解决哪几类工程问题再给出可以落地的流程、代码示例、常见误区和工程建议。读完以后你可以用一套清晰的标准判断当前项目里哪些任务是“为了实现真实价值”哪些只是在“自嗨式地增加复杂度”。1. Getting Real 是什么它不是“少做”而是“做对真正的产品”Getting Real 是 37signals 团队在 2005 年前后提出的一套产品方法论。当时他们用很小规模的团队做出了 Basecamp 这类面向真实用户的产品并把这套做法写成了同名电子书。书名直译是“变得真实”更准确的理解是不断把项目拉回真实世界而不是停留在文档、假设和规划里。很多人第一次听到这个概念会把它等同于“砍需求”。这个理解太浅了。Getting Real 的核心不是简单地少做功能而是少做那些基于猜测的功能。它要求团队把精力集中在“真实用户会使用、真实场景会触发、真实业务会产生价值”的路径上。这是一种优先级策略不是偷懒策略。1.1 三个最常见的误读第一个误读是“Getting Real 等于不做设计”。实际上它反对的是过度设计而不是不做设计。它强调先做好关键部分的体验而不是把所有边缘场景都考虑周全。第二个误读是“Getting Real 只适合小团队创业公司”。这个说法也不够准确。大团队、成熟项目里同样存在大量“为未来假设而做”的代码。即便在严格的合规领域需求的真实核心路径也依然需要被优先定义。第三个误读是“Getting Real 就是敏捷开发”。敏捷更强调迭代和响应变化而 Getting Real 更强调“真实感”和“克制感”。它可以和敏捷配合使用但它不是敏捷的另一种说法。1.2 为什么它在开发者圈子里值得讨论对开发者来说Getting Real 不是产品经理专属理论。它直接影响你的代码量、架构复杂度、加班次数和系统稳定性。一个遵循 Getting Real 的项目第一版可能只需要三张表、两个接口、一个页面一个不遵循的项目第一版可能要面对十几张表、几十个接口、一套权限体系、两个中间件。两者的开发成本差异是数量级的。所以这篇文章真正想解决的问题是如何用 Getting Real 的思路减少不必要的复杂度同时保证交付物真实可用。适合的读者是正在做一个新项目的开发者、被大而无当的需求压到喘不过气的团队成员、以及想系统化提升研发效率的技术负责人。2. 开发者视角Getting Real 解决的是哪三类工程问题抛开产品哲学从纯工程角度看Getting Real 针对的是三类几乎每个项目都会遇到的问题。2.1 需求不确定性带来的返工产品需求天然存在不确定性尤其是在项目早期。传统的应对方式是“提前把需求尽可能详细地写出来然后让开发按文档执行”。但问题在于文档写得再详细也只是团队的推测。真实用户拿到产品后产生的反馈几乎一定会推翻文档里的假设。Getting Real 的应对方式是用最短时间做出一个可以被真实使用的最小版本让用户和代码发生真实交互。这样需求的不确定性会被提前暴露而不是拖到三个月后上线时才暴发。2.2 过度抽象带来的交付延迟许多开发者一接到需求第一反应是设计一个可扩展的架构。于是在还没有任何真实用户的时候团队已经在讨论“将来是否需要多租户”“将来是否需要消息队列”“将来是否需要跨语言调用”。这些“将来”消耗了当下的时间和注意力。YAGNIYou Arent Gonna Need It原则在这里特别适用。Getting Real 本质上是把 YAGNI 从方法论层面推进到流程层面只有当真实需求出现时才值得引入对应的抽象。否则你就是在为一个可能永远不发生的未来写代码。2.3 缺少端到端验证带来的“假完成”还有一种常见现象是单个模块都开发完了API 文档也写了代码也覆盖了单元测试但把整个流程串起来一看用户根本没法完成核心任务。这种“假完成”比延期更隐蔽因为它让团队误以为项目在正常推进。Getting Real 强调端到端的“真实”。一个功能只有在用户可以真正使用时才算完成而不是在代码提交时就算完成。这个标准如果贯彻到位能减少大量表面进度、实际无用的工作。2.4 传统流程与 Getting Real 流程的对比维度传统流程常见状态Getting Real 流程常见状态需求阶段大量文档、完整 PRD一句话定义核心路径设计阶段先做全功能架构设计先做一个真实可用的最小闭环开发阶段并行开发所有模块单周内串通核心链路验证阶段等所有模块完成后联调每周用真实场景走查新增功能按模块清单逐步实现用真实反馈判断是否值得实现从表中可以清晰看到Getting Real 并不是“少干活”而是把资源和注意力集中到真实路径上让不确定因素更早暴露让无用工作更早停止。3. 核心原则拆解真实感、克制、单点聚焦如果想把这个方法论真正用起来需要理解它的几个核心原则。这些原则不是口号背后都有对应的工程动作。3.1 真实感做出来的东西必须能被真实用户使用“真实感”是 Getting Real 的第一原则。一个功能、一个页面、一个接口只有在真实环境里被真实用户使用才算真正被验证。Demo 不算测试数据不算演示环境也不算。这要求团队把“可以部署”“可以被真实调用”“可以处理真实输入”作为完成标准。落到工程上就是每个迭代结束时都有一个可运行的产物而不是一堆半成品的代码片段。3.2 克制少一点假设多一点验证克制不是不做功能而是不急着做“基于假设的功能”。如果一个功能没有真实反馈支撑就不应该进入开发队列。更具体的执行方式是每当你想加一个功能时先问自己这个功能解决的是真实存在的问题还是我们想象的、将来可能存在的问题。这个原则同时适用于产品功能和架构设计。比如“将来系统可能要支持多语言”这句话里的“将来”就是典型的假设。真实情况是当前用户只需要中文那第一版就不需要引入国际化框架。3.3 单点聚焦一次只做一件核心事一个真实可用的产品通常只需要一个核心功能点。Basecamp 当时的切入点就是“一个足够简单的项目管理工具”。对一个模块来说也一样它应该有一个清晰的核心任务而不是同时承担多个角色。单点聚焦落到代码层面意味着代码结构更简单测试更容易编写部署更轻量。它不会让系统失去扩展性反而因为更清晰后续扩展时逻辑更可控。3.4 从界面或 API开始而不是从数据库开始这是 Getting Real 一个很有冲击力的建议。传统开发习惯是从数据库表设计开始然后写后端最后才会到界面。但 Getting Real 建议反过来从用户/调用方最直接的触点开始设计。为什么因为数据库是内部实现而界面和 API 是用户真实接触的部分。从真实触点出发更容易看清什么才是有价值的。对纯后端系统来说这个触点就是 API 边界。先把 API 的输入输出定义清楚再设计内部表结构反而更不容易被不必要的表设计拖住。4. 从架构设计看 Getting Real最小可用设计与“未来完整版”的差别很多人觉得 Getting Real 只适合产品层面和架构设计无关。其实是反的架构设计是最能体现过度设计的地方也是 Getting Real 最能节省成本的地方。4.1 数据库设计你其实只需要四五个字段做一个待办事项模块时团队很容易把表设计成未来完整版-- 你很想写的“未来完整版” CREATE TABLE todos ( id INTEGER PRIMARY KEY, title VARCHAR(255) NOT NULL, description TEXT, owner_id INTEGER, project_id INTEGER, parent_id INTEGER, priority ENUM(low,medium,high), status ENUM(todo,doing,done,archive), due_date DATETIME, tags VARCHAR(255), created_by INTEGER, updated_by INTEGER, created_at DATETIME, updated_at DATETIME, deleted_at DATETIME );而 Getting Real 思路下第一版可能只需要这样-- Getting Real 的“当前真实需要” CREATE TABLE todos ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, done INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL DEFAULT (datetime(now)) );差异不是字段数量的差异而是判断依据的差异。前者在为“将来可能用到”的字段写代码后者只支持“现在用户真实操作”的流程。等真实业务验证了“确实需要优先级”这个需求再新增字段完全来得及成本远比一开始就设计完要低。4.2 API 设计只留必需的端点同样在 API 层第一版不需要做完整的 CRUD。待办模块第一版只需要查看待办列表GET /todos新增待办POST /todos标记完成PATCH /todos/:id/done什么编辑接口、删除接口、批量接口、排序参数、分页参数都可以等真实用户提出需求后再补。少一个接口意味着少一份测试、少一份文档、少一份维护成本。4.3 硬删除还是软删除先不要为“未来”写代码很多开发者在第一版就加入deleted_at字段理由是“将来用户可能会删除数据我们需要恢复功能”。但真实情况是第一版用户可能根本没有删除需求。即使有删除需求直接执行硬删除然后把数据备份做好对第一版也是可接受的方案。这里要区分“为了真实业务需求写代码”和“为了避免未来风险写代码”。前者值得做后者应该放一放。软件架构不解决所有未来问题更重要的能力是当未来问题出现时你可以快速重构。5. 让 Getting Real 落地的单周迭代流程理解了原则还需要可执行的流程。Getting Real 的落地可以浓缩为“单周真实交付”。5.1 流程总览每一周都围绕一个真实可用的目标展开用一句话定义本周“真实可用”的样子。比如“用户可以创建待办并把待办标记为完成”。不写复杂方案文档直接从 API 或界面开始设计。用最精简的数据结构和代码实现核心链路。在真实环境里运行并让真实用户、或至少是真实的业务人员试用。获取反馈判断是保留、调整还是砍掉。这个循环的关键不是快而是“真实验证”。每个迭代结束时你都应该知道当前方向是否正确。如果方向错了一周的试错成本远小于三个月的试错成本。5.2 用 Makefile 固化“跑通”的本地工作流为了让团队习惯“随时可运行”的真实感可以用 Makefile 把启动、安装、测试命令统一起来# 文件路径Makefile init: npm init -y npm install express better-sqlite3 run: node server.js demo: echo 启动服务后依次执行 echo curl -X POST http://localhost:3000/todos -H Content-Type: application/json -d {\title\:\写一篇 Getting Real 技术笔记\} echo curl http://localhost:3000/todos echo curl -X PATCH http://localhost:3000/todos/1/done echo 再次访问 http://localhost:3000/todos 查看 done 状态这样团队只需要make init make run就能启动项目。真实可用的“真实”首先体现在环境的一致性上新成员拉下代码后必须能立刻跑起来。5.3 一个决策过滤脚本防止过早膨胀在新增功能或字段时可以用一个简单的脚本帮团队建立“先判断后实现”的习惯#!/usr/bin/env bash # 文件路径should_i_build.sh # 用法./should_i_build.sh 功能名 feature$1 if [ -z $feature ]; then echo 用法: ./should_i_build.sh 功能名 exit 1 fi echo 检查功能: $feature echo Q1: 没有它用户能完成核心任务吗(y/n) read -r q1 if [ $q1 y ]; then echo 建议砍掉或延后。 exit 0 fi echo Q2: 它真的会在未来一两个月内被用到吗(y/n) read -r q2 if [ $q2 n ]; then echo 建议先用 TODO 记录不要现在实现。 exit 0 fi echo Q3: 你能在一天内做出一个最小可用版本吗(y/n) read -r q3 if [ $q3 y ]; then echo 建议做。 else echo 建议先拆分再挑出可验证的最小版本。 fi这类脚本不需要复杂关键是让团队停下来说一句这个功能真的有必要现在做吗6. 完整示例60 行代码跑通一个真实可用的待办接口下面用一个最小待办接口演示“真实可用”的第一版长什么样。这个例子不追求功能丰富追求的是能安装、能运行、能完成核心任务。6.1 环境准备操作系统Linux、macOS、Windows 均可。运行时Node.js 18 及以上。包管理器npm。依赖express、better-sqlite3。先用命令初始化项目mkdir getting-real-demo cd getting-real-demo npm init -y npm install express better-sqlite36.2 服务端代码创建server.js// 文件路径server.js const express require(express); const Database require(better-sqlite3); const app express(); const db new Database(todos.db); app.use(express.json()); db.exec( CREATE TABLE IF NOT EXISTS todos ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, done INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL DEFAULT (datetime(now)) ); ); // 查看待办列表 app.get(/todos, (req, res) { res.json(db.prepare(SELECT * FROM todos ORDER BY id DESC).all()); }); // 新增待办 app.post(/todos, (req, res) { const { title } req.body || {}; if (!title || !title.trim()) { return res.status(400).json({ error: title is required }); } const result db.prepare(INSERT INTO todos (title) VALUES (?)).run(title.trim()); res.status(201).json(db.prepare(SELECT * FROM todos WHERE id ?).get(result.lastInsertRowid)); }); // 标记完成 app.patch(/todos/:id/done, (req, res) { const { id } req.params; const result db.prepare(UPDATE todos SET done 1 WHERE id ?).run(id); if (result.changes 0) { return res.status(404).json({ error: todo not found }); } res.json(db.prepare(SELECT * FROM todos WHERE id ?).get(id)); }); const port process.env.PORT || 3000; app.listen(port, () { console.log(todo api listening on http://localhost:${port}); });这个代码模块虽然只有三个接口但已经形成了完整的核心路径用户可以创建待办、查看待办、标记待办完成。它把“真实可用”的标准闭环跑通了。6.3 运行与验证启动服务node server.js打开另一个终端按顺序执行curl -X POST http://localhost:3000/todos \ -H Content-Type: application/json \ -d {title:写一篇 Getting Real 技术笔记} curl http://localhost:3000/todos curl -X PATCH http://localhost:3000/todos/1/done curl http://localhost:3000/todos预期结果是第一次GET /todos能看到刚创建的待办且done0第二次GET /todos能看到done1。如果接口返回了预期数据说明这个最小闭环已经是“真实可用”的。6.4 如何判断这个模块“真实可用”判断标准有三个新环境上按步骤操作后可以启动服务。核心流程可以完整走通创建、查看、更新。数据是持久化的重启进程后数据依然存在。满足这三条就可以把这个版本交给真实用户验证。什么富文本、标签、日历视图通通可以等反馈再说。7. 常见误区、失败场景与排查思路即使理解了原则实践中还是会遇到不少反模式。这里列出几个典型场景和对应的排查方式。7.1 “砍到最后团队不知道要做什么”一个常见问题是遵循 Getting Real 后功能被砍到只剩三四个团队反而不知道第二天该干什么。这个问题的根源不是砍多了而是没有定义清楚“核心路径”。如果团队成员能说清“用户要通过这个产品完成什么事”那就自然会知道第一版该做什么。排查方式很简单让每个人用一句话写下产品的核心价值如果大家的答案差异很大说明核心路径还不清晰。解决方案是回到白板先讨论清楚产品要解决的真实问题再谈功能清单。7.2 “单周迭代老是排期爆炸”另一个常见问题是单周周期定了但每到周五都发现做不完。这通常不是因为时间太短而是因为“真实可用”的定义太宽。比如把“用户可以管理待办”定义为本周目标范围就太大了因为“管理”这个词可以包含增删改查、排序、分组、定时提醒。更好的定义是“用户可以新增一条待办并把它标记为完成”。范围越小越容易交付也越容易获得真实反馈。7.3 “代码是能跑了但离真实使用还差很远”还有一种情况服务能启动接口能通但用户说“这东西根本没法用”。比如没有界面、没有校验、没有错误提示。这是“能用”和“可用”的区别。Getting Real 强调的真实不只是一个能返回 JSON 的接口而是用户能完成任务的完整链路。如果用户是普通业务人员第一版至少需要一个简单页面如果用户是 API 调用方第一版至少需要清晰的错误响应格式。7.4 误区排查对照表问题现象可能原因排查方式解决方案砍需求后团队失焦核心路径未定义让成员各自写下产品核心价值并对比先统一核心路径再排功能清单单周迭代超时“真实可用”定义过宽检查迭代目标是否包含多个动词只保留一个核心用户动作能跑但没人用只做了技术验证没做体验闭环从用户角度走查完整流程补上用户真正接触的界面或错误处理总想加表加字段为未来假设设计用决策脚本逐条审查新增字段必须有真实需求支撑接口文档比代码多团队习惯文档先行统计文档与代码的维护成本先跑通闭环再补最小必要文档这些问题的共同本质是团队在“内部假设”上花的时间超过了在“真实反馈”上花的时间。排查的方向始终是回到真实路径。8. Getting Real 的边界与最佳实践Getting Real 不是银弹它有自己的适用范围和边界。同时把它融入日常研发也有一些值得遵循的工程习惯。8.1 什么时候不该盲目“砍”金融、医疗、电力等强合规行业或者那些天然依赖复杂长链路的基础设施项目不能因为“第一版”就直接砍掉审计、安全、合规等硬性要求。在这些场景里安全合规不是“未来假设”而是“当前真实约束”。但即使在强约束场景也可以区分核心路径与外围功能。比如交易系统必须有审计日志这不能砍但交易系统的数据看板是不是第一版就要做就可以根据真实使用情况来决定。Getting Real 的“砍”对象永远是猜测型需求而不是法律、安全与用户最低预期。8.2 把 Getting Real 融进日常研发的最佳实践在实际项目中以下几个做法能帮助团队把 Getting Real 从理念变成日常合并标准是“可运行的最小版本”。任何 PR 合入前检查它是否让系统处于可运行状态而不是让系统多了一个孤立模块。建立“真实走查”清单。每个迭代结束时从最真实的用户场景出发走一遍完整流程而不是只看测试报告。为 Mock 和 Stub 设定边界。可以用 Mock 解决联调依赖但必须明确哪些是临时模拟哪些是真实实现避免“假真实”。把“删除功能”当作工程决策。删除代码和新增代码一样重要。没有真实使用量的功能敢于从代码库中移除系统才会更健康。给单周迭代留出缓冲。不要把一个迭代塞满到 100%留出 20% 的缓冲来处理真实反馈。用真实数据做验证。测试环境里全是对的不代表真实数据下还能跑通。尤其是边界数据和异常输入必须用接近生产的数据做验证。这些实践的共同点是让团队每一次决策都能被真实世界验证。复杂度和成本会被压缩不是因为团队不思考而是因为团队把思考用在了真实问题上。最后说一个可以直接用的小技巧下一次接到新项目时先别急着画架构图。先问自己一个问题——如果只剩七天我必须砍掉哪些功能才能让用户真正完成一次核心任务然后从砍完的版本开始做。这就是启动 Getting Real 的第一步。