
朋友们不知道你们有没有这种感觉接到一个开发任务的时候脑子里已经构想了完整架构然而真到动手写代码时却总是“开局就卡住”。或者是在写一个功能时总想着把所有边界情况都考虑到位结果代码写了一整天分支逻辑越来越复杂最后连自己都不敢改了。这种“一步到位”的开发方式在需求明确且简单的场景下可能还撑得住可一旦业务逻辑复杂起来就会变成巨大的灾难。我在 2022 年重新复盘了“Getting Things Done”这套任务管理思想并将其中的核心原则——“小步增量”——应用到了日常开发和项目管理中。本文将完整分享这一套实践方法包含概念拆解、底层逻辑、完整可运行的代码实战案例以及我在实际落地中遇到的常见问题与解决方案。如果你正处于“任务拆解不细、开发进度拖沓、代码质量不稳”的瓶颈期这篇文章或许能提供一个不错的破局思路。1. 什么是“Getting Things Done (in small increments)”1.1 从时间管理到工程实践“Getting Things Done”原本是大卫·艾伦提出的一套个人时间管理系统核心思想是把脑子里各种未完成的事情全部“外置”到可信赖的系统中按照上下文和优先级逐步处理从而降低焦虑、提升执行力。而“(in small increments)”则是对这套思想在工程领域的一次重新诠释。它的意思非常直白任何大的任务都应该被拆分成若干小的、可独立验证的增量然后逐个完成。每一个增量都应该能带来一个可感知的进展而不是等到所有代码都写完才看到效果。1.2 软件开发中的小增量是什么在代码开发中“小增量”可以体现为很多层面需求层面把一个用户故事拆成若干个更小的用户故事。代码层面一次提交commit只解决一个问题。功能层面每个功能点从“可运行的最小闭环”开始再逐步迭代完善。项目层面采用迭代式开发而不是“瀑布式”的一次性交付。举个例子如果你要开发一个登录功能可能很多人会先建好用户表、配好加密工具、写拦截器、再写登录接口、还要做分布式会话管理……结果三天过去了登录接口还没跑通。但按照“小增量”思路你完全可以先写一个只校验用户名和密码是否匹配的登录接口再给密码加上加密存储接着加入 token 机制最后才考虑分布式会话和 SSO。每一步都独立可验证每一步都在为最终功能积累价值。1.3 为什么它适用于当前开发环境现在的软件系统越来越复杂需求变更也越来越频繁。如果你仍然采用“一次规划、全部实现”的打法一旦需求调整你可能要推翻大量已写好的代码。小增量开发天然具备“抗需求变更”的能力——因为每个增量都足够小即使某一步的方向需要调整代价也控制在可控范围内。所以这套方法不是简单的“时间管理鸡汤”而是一种能直接影响代码质量、团队协同和交付效率的工程实践。2. 小增量开发的核心底层逻辑2.1 降低认知负荷人类大脑的工作记忆容量是有限的。当你同时面对“设计表结构、编写业务逻辑、处理异常、考虑并发安全、优化查询性能”这些复杂维度时认知负荷会迅速超标。而在小增量模式下每个时间段你只需要关注一个很小的焦点。比如这一步只解决“接口能否正常返回”下一步只解决“参数校验是否完善”。大脑处于低负荷状态时思维清晰度最高写出来的代码质量也最稳定。2.2 缩短反馈周期心理学研究表明即时的反馈能显著提升人的动机和投入感。如果你花了一个星期写了一个大功能模块但还没法运行那这一周对你来说就像是“黑灯洗衣服”——没有反馈没有成就感也没法确认方向是否正确。小增量模式要求每个增量结束时都有一个可验证的结果。哪怕这个结果只是“控制台打印了一行日志”也意味着你已经成功走通了一条链路接下来的每一步都是在已有基础上叠加。2.3 风险分散与快速纠错在大批量代码一次性提交的情况下如果线上出了问题你很难快速定位到底是哪一段代码引入的故障。但如果你严格按照小步提交的节奏每次提交只涉及一个明确的变更范围那么在排查问题时你只需要检查最近几步的变更即可。这一点在代码评审中也特别重要。几百行的大 MRMerge Request和几十行的小 MR评审者的注意力和发现问题的概率是截然不同的。小 MR 往往能得到更细致的评审进而减少带病上线的可能性。2.4 保持持续交付能力持续交付的前提是什么是代码随时保持在可发布状态。如果一行开发分支上堆积了几百个未合并的提交那就谈不上“随时可发布”。而小步增量开发天然与持续交付契合——每一步都在完善功能每一步都保持主干可用。3. 小步增量开发的落地方法论3.1 从目标到任务的精细拆分很多开发者拆分任务的时候容易走向两个极端要么拆得太粗比如“实现订单模块”要么拆得太细比如“给变量起名字”。合适的拆分粒度应该遵循一个标准每个增量都可以产生一个可验证的业务价值或技术价值且完成它不需要超过 1~2 天。以开发一个“订单状态流转”功能为例合理的增量拆分可以是创建订单表并完成基础建表脚本实现订单的创建接口只保存订单基本信息实现订单状态枚举并集成到实体类中实现“待支付 → 已支付”的状态流转实现“已支付 → 已发货”的状态流转并追加状态变更日志统一返回格式和全局异常处理。每一步都是一个完整的闭环而不是“半成品”。3.2 Git 提交粒度与信息规范Git 提交是小步增量的直接载体。我强烈建议每次提交只做一件事提交信息采用标准前缀feat: 添加用户注册接口 fix: 修复订单状态更新时并发覆盖问题 docs: 补充接口文档说明 refactor: 重构登录模块的异常处理 test: 增加订单状态流转的单元测试这样不仅方便回溯历史也能让团队成员通过提交信息快速理解每一次变更的意图。不要写那种 “update code” 或者 “修改了一点东西” 的提交信息那等于没有写。3.3 先打通最小闭环再完善分支逻辑这是小步增量最核心的实践要领之一。很多开发者在实现一个接口时会同时考虑校验、异常、日志、缓存、消息通知等所有细节结果代码写完常常超过 300 行自己都很难一次性调试通过。正确做法是先用最短路径打通主流程。比如写一个接口第一步只返回success第二步再接入数据库第三步再加参数校验第四步再补充异常分支。这样做的好处是一旦后面某一步出了问题你只需要对比当前代码与上一步的差异定位非常迅速。3.4 结合自动化工具固化节奏要让小步增量在团队中稳定落地光靠人的自律是不够的。我建议配合以下工具和策略持续集成CI每次推送到远程仓库都自动跑编译、单测、静态检查。代码评审不超过 200 行的 MR 要求至少一名同事复查。分支策略推荐采用 Trunk-Based Development 或者 GitHub Flow保持主干长期可用。自动化测试每个增量至少覆盖一个核心测试用例避免后续变更破坏已有行为。4. 完整实战案例用增量方式开发一个待办事项 API为了让你更直观地理解小步增量开发我准备了一个完整的实战案例。我们使用 Python 的 FastAPI 开发一个待办事项TodoAPI 服务从零开始分成 7 个增量完成。本文所有代码基于以下环境版本请以你自己的项目为准操作系统Windows 11 / macOS 13 / Ubuntu 22.04 均可 Python3.10 框架FastAPI 数据库SQLite内置无需额外安装 HTTP 客户端浏览器 / Postman / curl建议先创建项目目录mkdir todo-increments cd todo-increments python -m venv venv激活虚拟环境# Windows venv\Scripts\activate # macOS / Linux source venv/bin/activate安装依赖pip install fastapi uvicorn sqlalchemy增量 1创建 FastAPI 应用并启动第一个增量只做一件事让 FastAPI 成功启动并且在访问根路径时返回一段提示信息。新建文件app.py# 文件路径todo-increments/app.py from fastapi import FastAPI app FastAPI(titleTodo API) app.get(/) def read_root(): return {message: Todo API is running}运行服务uvicorn app:app --reload启动后在浏览器中访问http://127.0.0.1:8000你会看到{ message: Todo API is running }这个增量的价值在于我们确认了环境、依赖、项目结构和基础框架都没有问题后续所有逻辑都在这个可运行的基础上叠加。增量 2设计 Todo 数据模型第二个增量引入 SQLAlchemy定义 Todo 表结构。这里只需要建立模型暂时不做任何接口。新建文件database.py# 文件路径todo-increments/database.py from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker SQLALCHEMY_DATABASE_URL sqlite:///./todo.db engine create_engine( SQLALCHEMY_DATABASE_URL, connect_args{check_same_thread: False} ) SessionLocal sessionmaker(autocommitFalse, autoflushFalse, bindengine) Base declarative_base()新建文件models.py# 文件路径todo-increments/models.py from sqlalchemy import Column, Integer, String, Boolean from database import Base class Todo(Base): __tablename__ todos id Column(Integer, primary_keyTrue, indexTrue) title Column(String(200), nullableFalse) completed Column(Boolean, defaultFalse)一个 Todo 只有三个字段主键id、标题title、是否完成completed。字段足够少便于在后续增量中逐步演进。为了让建表逻辑在启动时自动执行修改app.py# 文件路径todo-increments/app.py from fastapi import FastAPI from database import Base, engine import models Base.metadata.create_all(bindengine) app FastAPI(titleTodo API) app.get(/) def read_root(): return {message: Todo API is running}注意这里主动执行了一次import models目的是让 SQLAlchemy 的元数据注册中能拿到Todo模型从而正常创建表结构。运行一次后项目目录下会出现todo.db文件说明建表已经成功。增量 3实现创建 Todo 的接口第三个增量开始写业务接口但只实现一个功能创建待办事项。我们使用 Pydantic 来定义请求体。FastAPI 自带 Pydantic不需要额外安装。新建文件schemas.py# 文件路径todo-increments/schemas.py from pydantic import BaseModel class TodoCreate(BaseModel): title: str class TodoResponse(BaseModel): id: int title: str completed: bool class Config: orm_mode True在app.py中添加 POST 接口# 文件路径todo-increments/app.py from fastapi import FastAPI, Depends from sqlalchemy.orm import Session from database import Base, engine, SessionLocal import models from schemas import TodoCreate, TodoResponse Base.metadata.create_all(bindengine) app FastAPI(titleTodo API) def get_db(): db SessionLocal() try: yield db finally: db.close() app.get(/) def read_root(): return {message: Todo API is running} app.post(/todos, response_modelTodoResponse) def create_todo(todo: TodoCreate, db: Session Depends(get_db)): db_todo models.Todo(titletodo.title) db.add(db_todo) db.commit() db.refresh(db_todo) return db_todo重启服务后在命令行执行curl -X POST http://127.0.0.1:8000/todos -H Content-Type: application/json -d {\title\: \学习小步增量开发\}预期返回{ id: 1, title: 学习小步增量开发, completed: false }此时我们已经拥有了“创建待办事项”的最小闭环。增量 4实现查询 Todo 列表和详情在创建接口稳定可用之后第二个核心功能是查询。这一增量新增两个接口查询全部列表、查询单条详情。修改app.py新增以下代码# 文件路径todo-increments/app.py from fastapi import HTTPException from typing import List app.get(/todos, response_modelList[TodoResponse]) def list_todos(db: Session Depends(get_db)): return db.query(models.Todo).all() app.get(/todos/{todo_id}, response_modelTodoResponse) def get_todo(todo_id: int, db: Session Depends(get_db)): todo db.query(models.Todo).filter(models.Todo.id todo_id).first() if todo is None: raise HTTPException(status_code404, detailTodo not found) return todo运行测试curl http://127.0.0.1:8000/todos预期返回一个数组[ { id: 1, title: 学习小步增量开发, completed: false } ]访问详情curl http://127.0.0.1:8000/todos/1如果访问一个不存在的 id预期返回 404 和错误信息。增量 5实现更新 Todo 状态第五个增量加入更新能力。更新场景包括两种修改标题内容、修改完成状态。为了简化流程我这里使用一个通用的 PUT 接口。新增一个更新用的 Schema# 文件路径todo-increments/schemas.py class TodoUpdate(BaseModel): title: str | None None completed: bool | None None在app.py中添加 PUT 接口# 文件路径todo-increments/app.py app.put(/todos/{todo_id}, response_modelTodoResponse) def update_todo(todo_id: int, payload: TodoUpdate, db: Session Depends(get_db)): todo db.query(models.Todo).filter(models.Todo.id todo_id).first() if todo is None: raise HTTPException(status_code404, detailTodo not found) if payload.title is not None: todo.title payload.title if payload.completed is not None: todo.completed payload.completed db.commit() db.refresh(todo) return todo更新测试curl -X PUT http://127.0.0.1:8000/todos/1 -H Content-Type: application/json -d {\completed\: true}预期返回{ id: 1, title: 学习小步增量开发, completed: true }增量 6实现删除 Todo第六个增量实现删除接口。DELETE 接口设计得尽量简单——删除成功返回 204 无内容删除目标不存在则返回 404。添加以下代码到app.py# 文件路径todo-increments/app.py from fastapi import Response, status app.delete(/todos/{todo_id}, status_codestatus.HTTP_204_NO_CONTENT) def delete_todo(todo_id: int, db: Session Depends(get_db)): todo db.query(models.Todo).filter(models.Todo.id todo_id).first() if todo is None: raise HTTPException(status_code404, detailTodo not found) db.delete(todo) db.commit() return Response(status_codestatus.HTTP_204_NO_CONTENT)删除测试curl -X DELETE http://127.0.0.1:8000/todos/1预期没有响应体内容HTTP 状态码为 204。再次访问GET /todos/1会得到 404。增量 7增加日志与全局异常处理小步增量的最后一步不在新增业务功能而是完善健壮性。我们添加一个简单的中间件来记录请求日志再添加一个全局异常处理器。创建一个新的middleware.py# 文件路径todo-increments/middleware.py import time from starlette.middleware.base import BaseHTTPMiddleware from starlette.requests import Request class RequestLogMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next): start_time time.time() response await call_next(request) process_time time.time() - start_time print(f{request.method} {request.url.path} - {response.status_code} - {process_time:.4f}s) return response在app.py中注册中间件并增加异常处理器# 文件路径todo-increments/app.py from fastapi import FastAPI, Depends, HTTPException, Request from fastapi.responses import JSONResponse from middleware import RequestLogMiddleware # 注册中间件 app FastAPI(titleTodo API) app.add_middleware(RequestLogMiddleware) # 全局异常处理 app.exception_handler(HTTPException) async def http_exception_handler(request: Request, exc: HTTPException): return JSONResponse( status_codeexc.status_code, content{code: exc.status_code, message: exc.detail}, )重启服务后再发起任何请求控制台都会打印对应的请求日志。这不只是锦上添花在生产排查问题时这往往是第一手线索来源。到这里一个简单的 Todo API 已经具备了创建、查询、更新、删除、日志和异常处理能力。我们只用了 7 个小增量就完成了开发而且每一步都可以独立验证。如果你在第四步就发现设计有问题那么你需要修改的代码量也就是一个接口的范围而不用动整个项目结构。5. 小步增量开发中的常见问题与排查思路5.1 增量拆得太细反而效率低问题现象常见原因解决思路每天提交 10 多次但每次都只是改一个变量名增量粒度被机械理解为“改动最小化”重新聚焦“可验证的进展”而不是“行数最小的改动”代码格式微调可以合并到功能提交中进度看板上的任务数快速增加但业务价值没有明显提升拆出来的增量没有独立的业务价值每个增量都必须回答一个问题“当前结果是否可以被用户或测试人员感知”如果一个增量不能产生可感知的结果说明拆得太细了5.2 接口调不通不知道是环境问题还是代码问题遇到这种情况我的排查顺序是先确认进程状态ps -ef | grep uvicorn或者查看任务管理器确认服务是否在运行。看启动日志有没有 import 错误、端口占用、数据库连接失败。用最小请求测试访问GET /确认框架本身是通的。再测目标接口用 Postman 或 curl 单独发一次目标请求记录响应状态码。查看数据库文件确认 SQLite 中是否真的存在对应表和数据。按照这个顺序排查90% 的问题都能在 5 分钟内定位。5.3 数据库表结构一直没有更新如果你修改了models.py中的字段但运行后表结构没变很可能是数据库文件已经存在SQLAlchemy 的Base.metadata.create_all()不会自动修改已经存在的表。解决思路开发阶段可以删除本地todo.db文件重新生成表结构。生产环境一定要使用数据库迁移工具比如 Alembic。pip install alembic然后用 Alembic 来管理表结构变更避免手动删库。5.4 合作成员经常抱怨合并冲突多人协同开发时小增量容易导致高频的代码提交如果对同一个文件频繁修改冲突概率确实会增加。解决思路提前拆解任务边界两个人尽量不要在同一时间段修改同一个模块的核心文件。频频繁同步主干每天至少从主干拉取一次变更。遇到冲突时优先沟通不要直接强制覆盖别人的代码。5.5 代码评审变成了形式如果 MR 太小评审人可能觉得“没啥好看的”草草点个通过就完事。这其实背离了小步增量的初衷。更好的做法是在 MR 描述中写清楚“本次增量的目标、验证方式、依赖的上一步提交”。这样评审人只需要花几分钟就能理解上下文也有明确的方向去检查代码。6. 小步增量开发的最佳实践与工程建议6.1 建立统一的增量完成标准我建议每个增量在完成前都对照以下标准进行检查[ ] 代码能编译或解释执行没有语法错误[ ] 主流程已经被验证过至少有手工测试记录[ ] 相关联的测试用例已补充或更新[ ] 提交信息清晰能看出变更意图[ ] 没有把无关的代码改动混入本次提交。把这套“完成定义”打印出来贴在工位上或者在 PR 模板中直接写成检查项比空喊“大家注意质量”要有效得多。6.2 保持主干可发布小型业务系统可能没有专职的发布团队也没有完善的灰度发布平台。即便如此仍然要坚持一个原则主干分支随时处于可发布状态。只要这条原则成立一旦业务方说“这个功能今晚就要”你就不需要花时间处理“代码还没合完”的尴尬局面。为了实现这一点可以使用 Git 分支策略# 从主干拉取新分支功能完成后及时合并回主干 git checkout -b feature/todo-api git add app.py schemas.py models.py git commit -m feat: 完成待办事项 API 的创建与查询功能 git checkout main git pull git merge feature/todo-api git push origin main6.3 自动化测试与增量提交绑定很多开发者觉得写测试太费时间但如果你把测试绑定到每个增量上总体投入并没有想象中那么大。比如上面第四步我们实现了查询接口就可以顺手给查询接口写一个快速测试# 文件路径todo-increments/test_todo.py from fastapi.testclient import TestClient from app import app client TestClient(app) def test_create_todo(): response client.post(/todos, json{title: 写测试}) assert response.status_code 200 assert response.json()[title] 写测试这样写测试不是为了追求覆盖率数字而是确保下一个增量改动不会悄悄破坏已有的功能。6.4 日志与监控从早期就开始小步增量模式最大的风险之一是开发周期被切碎后开发者容易陷入“只关心本增量”的局部视角忽略整体可观测性。因此我建议从第一个增量开始就预留日志字段和基础埋点。在真实项目里常用的做法包括使用结构化日志比如 JSON 格式在关键业务入口记录入参、出参和耗时在配置中心预留日志级别开关在网关层预埋链路追踪 ID。这些动作不会花太多时间但越早做后期越轻松。6.5 组件负责人机制团队稍微大一点的时候建议为每个核心模块指定一名“组件负责人”Component Owner。这个人的职责不是把所有活都干完而是负责维护这个模块的拆分节奏和代码质量基线。当有人准备往这个模块提交新的增量时组件负责人应该能快速判断这个增量是否确实是当前最需要的一步是否破坏了这个模块的既有架构约定是否有更小的拆法。组件负责人机制能有效避免小步增量变成“小步乱增”。6.6 善用“时间盒”保护增量节奏开发过程中很容易陷入“想把这个增量做得更完美”的完美主义陷阱。为了防止这一点我习惯给每个增量设置一个时间盒Timebox。比如如果这是一个前端小功能最多花 2 小时如果一个接口的业务逻辑超过 100 行那就重新审视是否拆得不够细如果一个 bug 超过 1 小时还未定位立刻做一次“暂停、回溯、重审”的检查点。时间盒的本质是用外部约束来对冲人类决策中的过度优化倾向。6.7 需求变更时如何保持小步节奏当产品经理临时插入一个新需求而且看起来非常着急时不要直接推倒重来。你应该先问自己当前增量还剩多少新需求是否真的必须打断当前节奏如果回答是“确实打断”那么应该先把当前未完成的增量收尾到一个稳定的提交点哪怕只是“调通主流程”再切换过去。这样做的好处是以后回头继续开发时可以快速恢复上下文而不需要从一堆半成品中理思路。7. 总结与后续学习方向这篇文章我们从“Getting Things Done”的时间管理思想聊到了软件开发中的小增量实践并从概念、底层逻辑、落地方法论到完整代码实战一步步展示了如何把一个待办事项 API 服务通过 7 个小增量完成开发。这里面最关键的转变不是某个工具或框架而是思维模式的变化从“一次做完美”转变为“每步都可验证地往前推进”。如果你想把这种工作方式进一步深化接下来可以朝这几个方向继续学习学习 Trunk-Based Development 和 GitFlow 的优劣选择适合自己的分支策略。深入 CI/CD 流水线把每个增量都接入自动化构建和部署。尝试 TDD测试驱动开发将“写测试”作为增量的第一步。了解领域驱动设计DDD中的限界上下文划分辅助你更合理地切分业务增量。学习 Scrum 中的需求拆分技巧比如用户故事地图和 INVEST 原则。最后送给大家一句我自己的实践体会所谓“Getting Things Done”并不要求你一次性把所有事都做完而是要求你永远知道下一步该做哪件小事并真的把它做完。这也是我在 2022 年坚持至今的开发习惯。希望这篇文章能给你带来一些启发。如果你在实际落地小步增量开发时有更好的经验或踩过不一样的坑欢迎在评论区分享我们一起交流。