尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Getting Real:用最少功能构建真正解决问题的软件

Getting Real:用最少功能构建真正解决问题的软件 在实际软件项目里真正让团队陷入泥潭的往往不是某个技术难点无法攻克而是系统被塞进了大量“以后可能会用到”的功能。Getting Real并不是一个框架、一个库或者一套具体的开发工具它是一套关于如何做产品的工程方法论用更小的团队、更少的功能、更快的迭代去解决一个真实存在的问题。这篇文章会把Getting Real从口号拆成可执行的技术决策需求怎么拆、数据模型怎么设计、代码怎么写才不算过度设计、上线以后用什么信号判断继续还是砍掉。无论是做独立开发、小团队内部工具还是在公司里负责一个边缘业务模块这套思路都能直接用到。1. 先理解 Getting Real 到底在反对什么1.1 软件开发中最常见的浪费不是写得慢而是写“不需要的东西”很多项目失败并不是因为程序员能力不够而是因为交付了大量没人用的功能。一个典型的场景是产品经理从竞品列表里抄了二十个功能研发用三个月把整套系统搭出来结果核心用户只依赖其中三个功能其它十七个功能不仅没有带来价值还带来了持续的维护成本——接口要兼容、页面要调整、测试要回归、文档要更新。Getting Real的核心观点非常直接不要凭空想象用户需要什么而是做最少的东西去验证真实的用户行为。它反对的是“按计划堆功能”的开发方式主张“按问题拆方案”的开发方式。也就是说每一次需求评审都应该先回答这个功能解决的是谁的问题这个问题现在有多痛如果不做这个功能用户会用什么样的替代方案做了以后用什么指标判断它是否真的被使用如果这四个问题有一个答不上来这个功能就应该被推迟。1.2 Getting Real 的技术本质用约束换速度用速度换判断很多人以为Getting Real只是“砍需求”其实它的技术本质是另一件事用约束换取反馈速度再用反馈速度换取决策质量。当团队决定只做三个功能而不是二十个功能时系统的架构可以更简单数据库模型可以更小前后端接口可以更直接部署链路可以更短。代码量减少带来的不是“看起来简单”而是可变更性提高。一个只有三个页面、两个表、十个接口的系统改起来只需要一个小时一个二十个模块、五十张表、两百个接口的系统改一个字段都要拉上五个团队开会。这种取舍在工程上的直接表现就是决策维度大而全的做法Getting Real 的做法功能范围覆盖竞品大部分功能只覆盖核心用户的主流程数据模型预留扩展字段、通用表结构按当前真实业务建模变更时再演进技术栈上微服务、消息队列、全套中间件单服务、单数据库必要时再加组件发布节奏按月甚至按季度发版按天或按周小步发布回滚成本高涉及多服务多库低基本一次部署就能恢复这里的核心不是“技术越简单越好”而是“技术复杂度必须匹配当前的业务判断阶段”。业务还没有验证清楚时引入高复杂度基础设施等于在沙地上打桩。1.3 它和敏捷、MVP、精益开发的区别Getting Real与敏捷开发、MVP最小可行产品、精益创业经常被混在一起但它们关注点并不一样。敏捷开发更多是一种项目管理方式解决的是“如何在一个迭代周期内稳定交付”的问题。它不关心功能该不该做。MVP 强调的是用最小成本验证商业假设但它没有告诉你“验证完假设之后、下一版该加什么”。精益创业提供了一套“构建-测量-学习”的循环属于商业方法论落不到具体的数据表结构上。Getting Real更接近一种产品与研发之间的协作策略它要求研发直接参与真实问题的定义要求用代码去检验假设并且明确反对“功能多等于产品好”的惯性思维。落到工程上它是一套约束机制在给定的时间和人力下主动选择少做一些把每件做的事情做到足够扎实。2. 把 Getting Real 变成可执行的工程方法2.1 用“问题清单”代替“功能清单”大多数团队的需求文档是功能清单的写法用户登录、权限管理、个人中心、消息通知、数据报表、导出 Excel、多语言、暗黑模式。每个功能看起来都有道理但合在一起产品就失去了焦点。Getting Real主张先写问题清单。问题清单的格式很简单每个问题必须包含场景、痛点和验证方式问题 1 场景运营每周要导出一次活动报名数据再人工整理成周报。 痛点导出字段固定但周报格式经常变运营每次都要手工调整 Excel。 验证方式如果工具支持自定义导出列顺序运营是否可以独立完成周报。问题清单写完之后再问一个问题哪些问题最痛对于内部工具最痛的通常是耗时最长、出错最多的那个环节。对于面向用户的产品最痛的通常是最影响留存的那个环节。把这个环节单独拎出来做成第一个版本其余全部推迟。这里面有一个容易被忽略的工程价值问题清单写清楚了验收标准也就有了。开发不再需要猜测“这个字段要不要加进列表页”因为判断标准只有一个——它是否帮助用户解决了那个明确的问题。2.2 从一页纸规格到数据模型一个最小可行案例假设团队要做一个内部任务打点工具解决“每个任务花了多少时间无法追踪”的问题。按传统做法需求会拆成任务管理、项目分组、成员权限、工时统计、审批流、导出报表、消息提醒、移动端适配。按Getting Real的做法第一版只需要回答一个问题用户能否在五秒钟内记录一个任务并在一天结束后看到今天所有任务的时间汇总。这个问题的数据模型非常小。task表保留最核心的字段即可不需要一开始就设计状态机CREATE TABLE task ( id BIGINT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(255) NOT NULL, minutes INT NOT NULL DEFAULT 0, created_by VARCHAR(64) NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, done_at DATETIME NULL, INDEX idx_creator_date (created_by, created_at) );这张表直接对应一个真实使用场景每次开始一个任务时插入一条记录结束时更新done_at。所有统计查询都围绕created_by和created_at两个字段展开不需要额外的标签表、分类表、项目表。项目维度的统计等真正有统计需求时再加而不是现在预留。这里的关键是表结构不是“能支撑未来所有可能功能”而是“能支撑当前这个真实功能”。未来加表、加字段是正常的认为现在不设计完整就是挖坑反而是最大的误判。数据库演进成本远低于功能废置成本。2.3 约束设计单人团队和三人团队的不同做法Getting Real强调用约束驱动设计但约束的大小决定了技术方案的取舍。单人维护的内部工具和三人以上协作的业务系统不能使用同一套技术方案。团队规模典型约束合理技术选择需要避免的选择1 人维护时间有限必须尽量少出问题单体应用、SQLite/PostgreSQL、简单认证、单一部署微服务、Kubernetes、前端独立工程化3 人并行开发需要明确模块边界单体应用按模块分包、数据库加迁移脚本、API 统一规范按业务拆微服务导致联调成本高10 人以上团队协作和交付节奏是核心矛盾按领域拆服务或模块引入 CI/CD 和监控每个模块引入独立技术栈增加交接成本约束设计的本质是把“我们不能做什么”提前想清楚。一个人维护的项目如果为了“以后可能多端使用”而上 GraphQL 网关就是在给未来制造负担。技术选型的第一原则不是先进而是“当前团队在长期内能维护得住”。3. 最小功能闭环一个任务打点工具的实现示例3.1 需求拆解什么是“半个功能”Getting Real中提到一个很有价值的词half而不是 half-assed。翻译过来是“做半个功能但把那半个切切实实做好”而不是“做一个半吊子的完整功能”。以任务打点工具为例完整的多项目管理需要项目表、成员表、项目任务关联表、权限组。但第一版只需要“记录一条任务”和“按天汇总”。于是功能切分不是按模块切而是按“用户完成一件事的最小路径”切第一版功能 1. 新建任务输入标题选择时长保存。 2. 任务列表今天是哪些任务每条任务消耗多少分钟。 3. 当天汇总总时长、任务条数。 非第一版功能 1. 任务的编辑、删除、跨天修改。 2. 项目分组、标签、负责人。 3. 审批、提醒、导出。为什么编辑、删除可以放到第二版因为第一版的核心是验证一个假设用户是否愿意在任务完成后马上花几秒钟记录。如果这个假设不成立编辑功能做得再完善也没有意义。如果假设成立用户会自己提出“我想改一下时长”那时候再做编辑需求已经经过真实场景验证设计起来反而更准确。3.2 数据结构和接口设计保持单一职责第一版的 API 不需要设计成 RESTful 资源全套。以 Flask 为例三个接口就足够from flask import Flask, request, jsonify from datetime import datetime app Flask(__name__) tasks [] app.post(/api/tasks) def create_task(): data request.get_json() title (data.get(title) or ).strip() minutes data.get(minutes, 0) if not title or minutes 0: return jsonify({error: title and positive minutes are required}), 400 task { id: len(tasks) 1, title: title, minutes: minutes, created_at: datetime.now().isoformat(), done_at: None, } tasks.append(task) return jsonify(task), 201 app.get(/api/tasks/today) def list_today(): today datetime.now().date().isoformat() result [t for t in tasks if t[created_at].startswith(today)] return jsonify({items: result, total_minutes: sum(t[minutes] for t in result)}) app.patch(/api/tasks/int:task_id/done) def mark_done(task_id): for task in tasks: if task[id] task_id and task[done_at] is None: task[done_at] datetime.now().isoformat() return jsonify(task) return jsonify({error: task not found or already done}), 404 if __name__ __main__: app.run(debugTrue)这段代码没有使用数据库、没有使用蓝图、没有使用序列化框架但它完整覆盖了核心闭环创建任务、查看当天任务、标记完成。它说明了一个Getting Real重要原则第一版代码可以局部有缺陷但不能整体有缺陷。内存数组代替数据库是局部简化接口语义清晰、错误处理正确、数据格式稳定才是整体靠谱。3.3 验证脚本用接口测试代替人工点击第一版代码写完不要直接打开浏览器点几个按钮就认为完成。给接口写一个最小的验证脚本把核心路径自动跑一遍curl -X POST http://localhost:5000/api/tasks \ -H Content-Type: application/json \ -d {title: 写周报, minutes: 30} curl -X POST http://localhost:5000/api/tasks \ -H Content-Type: application/json \ -d {title: 排查线上告警, minutes: 45} curl http://localhost:5000/api/tasks/today预期的返回结果应该是两条任务记录total_minutes为 75。用一个 Shell 脚本把这几个请求串起来作为回归用例保留下来。以后每加一个功能先跑一遍旧脚本确认核心路径没有被破坏。这个习惯比写复杂的测试框架更能保证Getting Real项目的稳定性。4. 验证“做得对不对”而不是“做得快不快”4.1 功能是否被真正使用的判断方式Getting Real最关键的一步发生在功能上线之后。几乎所有团队都会犯同一个错误功能上线 项目结束。实际上功能上线只是验证的开始。判断一个功能是否值得保留不能只看“有没有人用”要看“有多少人依赖它解决原来的问题”。具体的观察指标取决于功能形态功能类型核心信号需要警惕的信号记录型功能每天有重复提交记录注册用户多但提交次数极少查询型功能查询接口调用次数稳定增长首页访问高详情页点击率低配置型功能用户主动修改默认配置配置页打开率高但保存率低自动化功能任务执行成功率和触发次数无人配置、无人查看执行结果以任务打点工具为例第一版上线一周后只需要关注三个数字新增任务数、当天完成标记数、第二天重复使用率。如果大多数人用了第一次就不再打开说明“记录成本”仍然太高问题不在功能缺失而在使用路径不够顺。此时应该观察用户流程而不是急着加项目管理功能。4.2 通过日志和监控确认使用路径工程上可以通过日志把“使用路径”变得可见。第一版的打点工具至少要在三个位置输出结构化日志创建任务、查看当天列表、标记完成。日志字段保持简单{event: task_created, user_id: u_001, task_id: 1, minutes: 30, ts: 2025-01-06T10:15:0008:00} {event: task_list_viewed, user_id: u_001, ts: 2025-01-06T18:00:0008:00} {event: task_done, user_id: u_001, task_id: 1, ts: 2025-01-06T18:10:0008:00}拿到这批日志后可以做一个最简单的漏斗创建任务的人数、查看汇总的人数、第二天继续记录的人数。如果创建到查看的转化率超过 80%说明记录流程基本符合直觉如果转化率低说明列表页信息不足以激励用户查看。这些判断完全不需要数据分析平台一条 SQL 或一段 Python 脚本就能完成。Getting Real不代表不做度量而是用最小的度量成本拿到最大的决策信息。4.3 什么时候该继续什么时候该砍掉功能上线后会出现四种情况使用率高、问题解决率高这是核心功能继续投入。使用率低、问题解决率高说明需求真实但入口或触发频率低可以优化入口或增加提醒。使用率高、问题解决率低功能被频繁使用但没有真正解决问题需要重新定义问题。使用率低、问题解决率低直接砍掉不要投入资源挽救。砍功能在工程上不复杂复杂的是团队心理。一个已经写好的功能删掉会让人觉得“浪费了工作量”。但从技术债务角度看保留一个没人用的功能才是真正的浪费——它消耗测试时间、升级负担和认知成本。建议每个迭代结束都做一次功能清理把使用率低的功能标记为废弃在下个版本移除。5. 实际项目中常见的五个坑5.1 把“少做”理解成“草率”Getting Real不是让你写一个没经过思考的 demo。它主张少做功能但没主张少做校验、少写错误处理、少做日志。恰恰相反因为功能少每个功能更应该打磨到位。错误示范因为功能少就不做异常捕获用户输入非法数据导致接口返回 500。正确做法是保留完整参数校验和错误响应但把功能数量控制在最小范围。“少做”砍的是需求范围不是工程质量。5.2 用技术栈过重抵消产品速度有些团队嘴上说做 MVP手里却搭了微服务、配置中心、链路追踪、容器编排。这些基础设施本身没有问题问题是它把第一版从两周拖到了两个月。第一版的目标是验证问题不是验证架构。正确的顺序是先跑通最小闭环再根据真实瓶颈引入基础设施。如果第一版单服务就能跑就绝不拆服务。如果日志可以落文件就先不上采集系统。等并发量、数据量明确成为瓶颈时再升级成本完全可控。5.3 无法拒绝需求导致核心被稀释Getting Real中最难执行的一条是“默认说不”。业务方提十个需求其中九个也许都有道理但团队的核心精力是有限的。每接受一个边缘需求核心功能的打磨时间就少一块。工程上的处理方式是建立需求决策表把需求按“频率、痛点、替代方案、实现成本”排序。得分低的自动排到下一轮核心功能永远排在最高优先级。这不是产品经理一个人的事研发也应该参与评分因为研发对实现成本最清楚。5.4 混淆“简单”和“简陋”简单是指把复杂问题拆成一个清晰的核心路径路径上每个环节都可靠。简陋是指只实现了表面流程遇到边界情况就崩。两者的区别可以在代码层面明确区分# 简陋的写法只处理正常输入 def create_task(data): return save_task(data[title], data[minutes]) # 简单的写法正常输入可跑通非法输入可返回明确错误 def create_task(data): title (data.get(title) or ).strip() minutes data.get(minutes, 0) if not title or minutes 0: return {error: title and minutes are invalid} return save_task(title, minutes)简单不等于没有判断而是把判断放在最必要的位置。参数校验、幂等控制、超时处理这些基础可靠性投入在任何规模的系统里都不能省。5.5 只砍功能不砍流程有些项目功能已经很克制了但流程仍然冗长需求评审、技术方案评审、排期、测试、发布审批每个环节都按照大项目标准执行。Getting Real认为流程和功能一样也需要保持一致。小团队、小功能的发布流程应该压缩到最短代码审查过关、自动测试通过、一键发布、失败快速回滚。流程的每一步都应该有明确目的没有目的的流程步骤可以直接删除。检查一个流程是否合理可以看它的通过时间从提交代码到上线超过一个小时的流程对内部小工具来说就是过重。6. 适用边界和工程实践建议6.1 适合与不适合 Getting Real 的场景Getting Real不是万能方法论它有自己的适用边界。判断标准不是团队大小而是当前阶段的业务不确定性。场景是否适合原因从零孵化一个内部工具适合问题明确反馈快迭代成本低面向新市场的产品试点适合需要通过最小功能验证需求和用户行为大型企业已有稳定业务流程的系统重构部分适合核心流程固定但合规、审计、权限要求不能砍涉及金额、合同、法律风险的业务不适合很多功能是合规要求不能按使用率裁撤平台型基础设施不适合基础设施需要稳定性和完备性少做功能不等于缺失边界能力所以Getting Real的适用范围是“可选功能”远多于“必需功能”的软件。必需功能由法规、契约、核心流程决定不能砍可选功能才是Getting Real发挥价值的空间。6.2 团队协作时的落地方式Getting Real落地不是产品经理写文档、研发只执行。它要求研发直接面对真实问题参与功能取舍。推荐一个小团队协作方式每个迭代开始前所有成员各自写一条本周最想解决的用户问题投票选出最重要的一个问题整个迭代只围绕这个问题开发。其它需求写入 backlog但不进入开发队列。一个迭代的输出不是“完成了几件事”而是“核心问题是否被显著改善”。研发在这个过程中的职责是给出每个功能真实的技术成本估算包括开发时间、维护成本、上线后长期负担。产品侧则负责评估问题价值和用户收益。两边数据汇合后再做功能决策。6.3 一张可复用的 Getting Real 检查清单每次新功能立项、每个迭代开始前建议逐条过一遍这张清单这个功能解决的具体问题是什么能用一句话说清楚吗这个问题的真实用户是谁他们在什么场景下遇到它如果不做用户现在用什么替代方案实现这个功能的最少代码路径是什么数据库和接口是否能按当前需求设计而不是为了可能的功能预留技术栈是否超出了当前团队的长期维护能力上线后用什么指标判断成功如果指标不合格是否愿意在一周内砍掉这个功能发布流程是否足够短能支撑小步快跑核心路径是否有自动化或脚本验证清单的目的是把Getting Real从理念变成每个迭代都能执行的判断标准。实际问题千差万别但判断逻辑是稳定的先定义问题再确认用户然后做最小方案上线后看数据不行就砍。Getting Real对研发最有价值的一点是它把“少做”重新定义成了专业能力。抵挡住需求诱惑、理解哪些功能真正解决问题、在上线后用数据判断去留这些能力比写复杂系统更难也更能决定一个产品的生死。下一步可以在自己的项目中找一个正在膨胀的需求清单用问题清单重新拆一遍再挑出一个核心问题用最小闭环在两周内交付并观察数据。这个练习做完一次对Getting Real的理解会比读任何文章都深。
返回列表