news 2026/9/8 9:03:04

Getting Real:拒绝过度设计,用最小可行产品提升研发效率

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Getting Real:拒绝过度设计,用最小可行产品提升研发效率

先从一个很常见的研发场景说起。

很多团队都在做同一个项目:需求评审开了三轮,架构设计文档写了二十页,数据库表设计到第六版,权限系统、审计日志、消息队列、多租户方案全部规划完毕。然后到了真正的开发阶段,发现第一个版本光是最小可用功能就做了三个月。上线后用户根本不关心权限模型,只问了一个问题:为什么保存按钮点了没反应?

这种“做了很多,却没做出真实可用东西”的状态,在软件行业太常见了。它和团队能力无关,和流程、判断、资源分配方式高度相关。

今天想聊的是这本经典小书里的产品方法论: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 过度抽象带来的交付延迟

许多开发者一接到需求,第一反应是设计一个可扩展的架构。于是在还没有任何真实用户的时候,团队已经在讨论“将来是否需要多租户”“将来是否需要消息队列”“将来是否需要跨语言调用”。这些“将来”消耗了当下的时间和注意力。

YAGNI(You Aren't 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 流程总览

每一周都围绕一个真实可用的目标展开:

  1. 用一句话定义本周“真实可用”的样子。比如“用户可以创建待办并把待办标记为完成”。
  2. 不写复杂方案文档,直接从 API 或界面开始设计。
  3. 用最精简的数据结构和代码实现核心链路。
  4. 在真实环境里运行,并让真实用户、或至少是真实的业务人员试用。
  5. 获取反馈,判断是保留、调整,还是砍掉。

这个循环的关键不是快,而是“真实验证”。每个迭代结束时,你都应该知道当前方向是否正确。如果方向错了,一周的试错成本远小于三个月的试错成本。

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。
  • 依赖:expressbetter-sqlite3

先用命令初始化项目:

mkdir getting-real-demo cd getting-real-demo npm init -y npm install express better-sqlite3

6.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能看到刚创建的待办且done=0;第二次GET /todos能看到done=1。如果接口返回了预期数据,说明这个最小闭环已经是“真实可用”的。

6.4 如何判断这个模块“真实可用”

判断标准有三个:

  1. 新环境上,按步骤操作后可以启动服务。
  2. 核心流程可以完整走通:创建、查看、更新。
  3. 数据是持久化的,重启进程后数据依然存在。

满足这三条,就可以把这个版本交给真实用户验证。什么富文本、标签、日历视图,通通可以等反馈再说。

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 从理念变成日常:

  1. 合并标准是“可运行的最小版本”。任何 PR 合入前,检查它是否让系统处于可运行状态,而不是让系统多了一个孤立模块。
  2. 建立“真实走查”清单。每个迭代结束时,从最真实的用户场景出发走一遍完整流程,而不是只看测试报告。
  3. 为 Mock 和 Stub 设定边界。可以用 Mock 解决联调依赖,但必须明确哪些是临时模拟,哪些是真实实现,避免“假真实”。
  4. 把“删除功能”当作工程决策。删除代码和新增代码一样重要。没有真实使用量的功能,敢于从代码库中移除,系统才会更健康。
  5. 给单周迭代留出缓冲。不要把一个迭代塞满到 100%,留出 20% 的缓冲来处理真实反馈。
  6. 用真实数据做验证。测试环境里全是对的,不代表真实数据下还能跑通。尤其是边界数据和异常输入,必须用接近生产的数据做验证。

这些实践的共同点是:让团队每一次决策都能被真实世界验证。复杂度和成本会被压缩,不是因为团队不思考,而是因为团队把思考用在了真实问题上。

最后说一个可以直接用的小技巧:下一次接到新项目时,先别急着画架构图。先问自己一个问题——如果只剩七天,我必须砍掉哪些功能,才能让用户真正完成一次核心任务?然后从砍完的版本开始做。这就是启动 Getting Real 的第一步。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 9:02:32

如何3步找回崩溃时丢失的SQL脚本:DBeaver新手自救指南

如何3步找回崩溃时丢失的SQL脚本&#xff1a;DBeaver新手自救指南 【免费下载链接】dbeaver Free universal database tool and SQL client 项目地址: https://gitcode.com/GitHub_Trending/db/dbeaver 凌晨写完两小时的报表SQL&#xff0c;DBeaver编辑器突然卡死&#…

作者头像 李华
网站建设 2026/9/3 17:37:19

免费开源 AI 图像增强:Upscayl 4 倍超分新手实操指南

免费开源 AI 图像增强&#xff1a;Upscayl 4 倍超分新手实操指南 【免费下载链接】upscayl &#x1f199; Upscayl - #1 Free and Open Source AI Image Upscaler for Linux, MacOS and Windows. 项目地址: https://gitcode.com/GitHub_Trending/up/upscayl Upscayl 是一…

作者头像 李华
网站建设 2026/9/4 9:02:46

Compose ConstraintLayout实战:告别嵌套布局

如果你正在用 Jetpack Compose 做安卓开发&#xff0c;大概率会遇到这样一个场景&#xff1a;界面层级越来越深&#xff0c;Column套Row&#xff0c;Row再套Box&#xff0c;最后嵌套了五六层&#xff0c;改一个间距要翻半天代码&#xff0c;预览也卡得不行。这时候很多人会想&a…

作者头像 李华
网站建设 2026/9/4 14:16:04

Prisma 跑不起来?3 步定位 Node.js 版本不兼容并修好它

Prisma 跑不起来&#xff1f;3 步定位 Node.js 版本不兼容并修好它 【免费下载链接】orm Next-generation ORM for Node.js & TypeScript | PostgreSQL, MySQL, MariaDB, SQL Server, SQLite, MongoDB and CockroachDB 项目地址: https://gitcode.com/GitHub_Trending/pr…

作者头像 李华
网站建设 2026/9/4 16:26:23

Claude Code核心词汇与上手实践:从环境配置到报错排查

看到《Show HN&#xff1a;克劳德的核心词汇》这个标题时&#xff0c;我第一反应是&#xff1a;这会不会又是一份命令速查表&#xff1f;仔细想想才发现&#xff0c;这个角度比速查表更接近本质——Claude Code 的上手门槛&#xff0c;本质上是一道词汇门槛。很多人卡在安装阶段…

作者头像 李华