Appearance
工程经验如何传递,而不是让每个人重新踩坑?
在软件开发中,我们经常会遇到相似的问题。可能是 Excel 导入导出、文件上传,也可能是服务器配置、服务部署,或者某个业务系统的功能实现。
这些问题有时已经被其他开发人员解决过,但后来的人未必知道。即使是自己曾经处理过的事情,过了一两年,也可能因为忘记了当时的操作步骤或者设计原因,不得不重新查找资料。
我逐渐觉得,工程经验的价值不应该只停留在解决当时的问题。
已经积累的经验,如果能够被后来的人找到、理解,并且有办法验证,就可能减少下一次重复摸索的成本。
这里的后来者,可以是其他开发人员,也可以是几年后的自己。
一、一次 Excel 导入导出的经历
1.1 已经实现的功能,其他人未必知道
之前有一次,我看到一位同事需要开发 Excel 导入、导出功能。
我知道自己维护的一个教育业务系统(以下简称教育系统)中已经有相应实现,于是告诉他,可以直接去那个项目里复制代码。
当时我们没有把这部分能力封装成公共 JAR,但已有实现能够满足他的需要,因此不必重新编写基础功能。
后来,我发现他只复制了实现代码,没有复制对应的单元测试。
教育系统中的 Excel 功能原本就有单元测试(UT)。我向他演示了这些测试的作用,并让他把测试代码也复制到自己的项目中运行。
这件事让我想到,代码复用和工程经验传递并不完全相同。
如果只是复制实现代码,后来的人虽然得到了一个可以使用的功能,却未必知道原作者怎样验证它。
测试用例除了检查执行结果,还能够表达原实现所关注的输入、预期行为和异常情况。后续需要修改时,这些测试可以帮助开发人员了解原来的行为约束。
复制到新项目之后,仍然需要结合实际依赖、环境和业务要求进行验证。原项目有 UT,并不能保证新项目中的实现永远正确。
相比于只有一份实现代码,带有测试的代码能够提供更多可供理解和验证的信息。
1.2 这次复用为什么能够发生
这次复用之所以发生,是因为我知道教育系统中有现成实现,而且恰好了解到同事正在开发类似功能。
如果没有这次交流,他可能会自行寻找其他方案。
在我们团队中,目前没有统一记录各系统已有基础能力的地方。开发人员需要某项功能,通常依靠相互询问了解是否存在类似实现。
也有人习惯先自己研究。
独立解决问题本身没有什么不妥。但如果团队已经积累了成熟的实现,开发人员却根本不知道它存在,那么重新研究就未必是必要的投入。
团队拥有某项实现,与团队成员知道它存在,是两件不同的事。
除了有人愿意分享,还需要考虑怎样让已有成果能够被其他人主动发现。
二、团队中的知识,怎样才能被找到
团队内部的知识并不只有代码。
已有的基础功能、基础设施操作经验,以及业务系统的功能资料,都可能被后来的人重复使用。
它们适合采用不同的传递方式:代码需要明确位置与验证入口,基础设施需要操作说明,业务系统则需要帮助阅读者建立整体认识。
2.1 基础代码:先让别人知道在哪里
不同业务系统之间,可能存在许多类似的基础需求,例如文件处理、接口封装和一些通用的工具方法。
遇到这些需求时,不一定要先建设公共组件。
公共 JAR 可以统一实现,但也会引入版本管理、依赖兼容和后续维护成本。如果只是少量系统需要某项能力,复制经过验证的实现,或者参考已有方案重新适配,也可能更合适。
因此,可以先解决一个基础问题:开发人员能否发现团队已经有哪些成果可以参考?
哪怕只有一份简单的索引,说明实现位于哪个项目、对应哪些代码、有哪些依赖和测试,也能够帮助后来的人判断是否值得复用。
至于是否进一步提取公共组件,可以根据实际复用频率和维护需求再作决定。
这里的目标不是要求所有项目使用同一份代码,而是让已经完成的工作能够成为下一次开发的参考。
2.2 基础设施:让常规操作有据可查
除了代码,团队还会积累许多基础设施方面的经验。
例如宝塔面板的使用、OSS 接入、域名和 Nginx 配置,以及如何通过已有流水线部署一个新服务。
这些操作对于熟悉环境的人来说,可能只是日常工作。但对第一次接触的人而言,需要先了解服务器环境、配置位置、操作顺序以及验证方法。
如果没有可以查阅的资料,就可能需要其他同事重新讲解,或者自己从头摸索。
这类知识适合记录成相对简洁的操作文档,包含环境要求、配置入口、关键步骤、注意事项和验证方法。
涉及权限的操作,可以记录申请入口或负责人。账号密码、访问密钥等敏感信息则应通过受控的凭据管理方式提供,而不是直接写进普通操作文档。
这样,具备相应权限的开发人员就能先通过文档了解基本操作,需要帮助时再针对具体问题交流。
2.3 业务文档:让接手者能够先预习
业务系统的功能文档,类似学生上课前的预习材料。
一个开发人员刚接触已有系统时,如果连系统主要负责什么、与哪些服务关联、有哪些核心业务流程都不了解,那么后续讨论代码实现就会比较困难。
如果接手者能够先通过文档了解业务概况,再与原维护人员交流,就不必从最基础的内容开始讲解。
这类文档可以保持轻量化。
例如,说明系统的主要业务、核心流程、相关服务,以及进一步查看技术资料的入口。没有必要把所有 Controller、Service 和数据库字段都罗列出来。
文档首先帮助读者建立整体认识。
至于复杂业务规则、历史决策和特殊情况,可以在读者提出问题后,再由了解系统的人补充。
这样,文档承担基础信息的传递,人与人之间的交流则用于解释难以理解的部分。
无论是已有代码的位置、基础设施的操作步骤,还是业务系统的功能概览,它们解决的都是同一个问题:让需要信息的人,能够先找到正确的入口。
三、个人知识库:把经验留给未来的自己
3.1 过去解决过,不代表以后还记得
工程经验的传递并不一定涉及其他人。
我平时会在个人知识库中记录一些软件安装、环境配置和操作方法,例如 Docker 的安装与使用。
这类记录通常不需要完整的文章结构。
可能只需要保存操作系统和软件版本、安装命令、配置文件位置、遇到的问题,以及最后确认可用的方法。
这些看起来普通的记录,对未来的自己仍然可能有价值。
几年之后再次安装同样的软件时,我未必还能记得当时具体使用了哪些命令、为什么修改某项配置。
如果没有记录,就可能重新查阅文档,甚至重复之前的试错过程。
个人知识库的作用,是为未来的自己提供一个已经实践过的起点。
3.2 记录的形式,应该由用途决定
个人知识库主要是为了方便自己查阅,因此不需要每份笔记都写得非常完整。
一次简单操作,保存几条命令可能就够了。
一次复杂问题排查,则可能需要记录错误现象、环境信息、尝试过的方案和最终结果。
如果涉及容易遗忘的设计原因,还可以补充当时为什么选择这种处理方式。
记录不应成为额外的形式负担。
如果只是为了记住一条命令,却需要先写背景、分析过程和总结,反而可能让人失去记录的意愿。
对自己有用、以后能够找回,已经可以是个人笔记存在的理由。
部分个人记录在积累之后,还可能具备面向其他人的分享价值。
四、从个人记录到公开的工程经验
4.1 从自己看得懂,到别人也能理解
知识分享并不局限于公司内部。个人积累的经验,也可能对其他开发人员有参考价值。
我在小刘实验室 Docs 中公开过一些技术文章,其中一部分最初只是个人知识库中的记录。
例如:
《PageHelper 两种分页写法对比:一次泛型失真的排查与源码分析》源于实际遇到的分页问题。它值得整理的地方,不只是最终采用哪种写法,还包括问题如何复现、源码中的原因以及结论的适用条件。
《从 JPA + MyBatis 到 MyBatis-Plus:一次旧项目数据访问层的维护性收敛》则来自旧项目维护中的技术调整。仅知道最终选型,无法判断是否适用于自己的项目,还需要理解原有数据访问层的问题,以及为什么以维护性收敛作为主要目标。
这些内容最初记录在个人知识库中,后来经过整理,才逐渐变成公开文章。
对我而言,这相当于让经验传递的范围从自己扩大到其他开发人员,甚至是完全不了解原项目的陌生读者。
公开分享与个人记录之间,有一个明显区别。
个人笔记中,很多背景可以省略,因为我自己知道当时发生了什么。
公开文章则需要让读者理解:问题发生在什么条件下,为什么采用这个方案,有哪些证据支持结论,以及哪些场景不能直接套用。
只有这些信息相对完整,读者才有机会理解原作者的判断,并在自己的环境中验证经验是否适用。
4.2 代码能展示实现,却不一定能解释动机
在整理工程文章时,我越来越重视一个问题:当初为什么这样设计?
因为代码能够展示程序最终做了什么,却未必能够回答原作者为什么作出某种选择。
例如,之前整理 Redis 缓存治理文章时,我们曾讨论过一个问题:
既然已经实现了类型化读取,为什么还要保留旧的 Object get 方法?
单看代码,很难确定真正原因。它可能是尚未完成迁移,也可能与历史兼容有关。
而我当时保留旧方法的实际原因,是需要兼容存量调用。
如果不知道这个兼容要求,就可能把旧方法的保留误认为重构没有完成。
这并不意味着所有历史设计都合理,而是评价一个设计之前,需要了解它在当时受到哪些约束。
代码能展示最终实现,却不一定能解释当时的设计动机。
工程经验中真正值得留下的,不只是最终实现,还包括影响方案选择的重要约束和判断依据。
4.3 AI 可以帮助整理,但不能替代事实来源
现在,我也会使用 AI 辅助整理技术文章。
它可以阅读代码、检查文章结构、发现逻辑缺口,并根据已有信息提出需要进一步核实的问题。
沿用上面的 Redis 例子,AI 可以发现新旧接口并存这一现象,却无法仅凭代码确认旧方法为什么仍然保留。这时,就需要由了解实际情况的人补充原因。
我们逐渐形成了一种合作方式:先让 AI 从现有材料中发现疑问,再由了解情况的人补充真实背景,最后检查文章中的解释是否有足够依据。
对于能够通过源码、测试、日志或官方资料确认的技术事实,AI 可以协助核实。
但涉及历史决策、业务要求和当时的实际动机时,如果缺少可靠证据,就应该向了解情况的人询问,而不是自行补全。
这里需要区分三件事:已经确认的事实、基于事实形成的判断,以及目前还不知道的原因。
这不仅适用于 AI 写作,也适用于开发人员接手遗留系统时的分析工作。
代码只是工程历史的一部分,不能代替全部背景。
4.4 小众问题也值得留下记录
有些技术问题只发生在特定框架版本、技术环境或旧系统的升级过程中,受众可能并不广泛。
但如果有人恰好遇到了类似问题,能够通过文章了解如何复现、怎样分析原因,以及最终方案为什么成立,这份经验就可能具有参考价值。
公开分享不一定需要追求很大的阅读量。
对我而言,能够把一次真实问题的解决过程记录清楚,本身也是知识积累。它既可以供其他人参考,也方便未来的自己重新审视当时的判断。
不过,公开分享仍然需要遵守必要的边界。
公司内部代码、业务数据、账号凭据和不适合公开的实现细节,不能直接从个人笔记搬到公开网站。
可以分享的是经过必要脱敏、具有独立参考价值的工程问题、分析过程与技术取舍。
五、知识分享也需要考虑成本
5.1 不是所有问题都需要留下完整记录
前面讲的是已有经验如何传递,这里还需要考虑哪些经验值得记录。
开发人员每天会处理很多问题。如果每解决一个问题,都要求整理一篇文档,那么记录工作也可能成为新的负担。
判断是否值得记录,可以考虑几个因素:
这个问题以后是否可能再次遇到?如果没有记录,重新调查需要多少时间?其中是否存在容易遗忘的关键条件?别人是否可能遇到类似问题?
一个简单、偶发、重新查阅也很容易解决的问题,可以不专门记录。
相反,有些问题虽然发生频率不高,但排查过程复杂、涉及多个系统,或者需要了解特定历史背景。这种经验即使只是留给未来的自己,也可能值得记录。
记录形式也不需要统一。
有些只需要几条操作说明;有些适合整理成团队文档;还有一些可以经过进一步核实,形成公开技术文章。
5.2 留下来的经验,还需要判断是否有效
记录并不是一次性完成的工作。
例如,基础设施配置可能因为环境迁移而发生变化,技术文章可能因为框架升级而需要补充说明,已有代码也可能逐渐不再符合新的业务要求。
如果记录缺少版本、适用条件或者验证方法,后来的人可能无法判断它是否仍然可靠。
因此,文档应该尽可能保留必要的背景信息,而不是只给出一句“按照这个方法处理”。
但也不必要求每一份个人笔记都建立复杂的维护机制。
对于使用频繁、影响较大的团队文档,可以投入更多精力保持准确;对于个人的历史排障记录,则可以明确当时的环境和时间,让后来的人知道这是一次特定条件下的实践。
记录与维护的投入,应该与内容可能带来的复用价值相匹配。
5.3 让经验能够被使用,而不是只增加文档数量
最初的 Excel 案例,只靠一次日常交流,就让已有实现和测试方法得到了复用。
这种日常分享本身就有价值。
但如果希望减少对偶然交流的依赖,团队可以尝试为已有成果提供简单的查找入口。
基础代码可以记录实现位置和测试入口;基础设施可以提供操作说明;业务系统可以保留功能概览及关联资料。
这些内容不必全部集中到一个庞大的平台中。
重要的是,当后来的人准备处理一个问题时,是否知道从哪里寻找已有经验,找到之后能否理解,并判断它是否适用于当前情况。
知识分享不是为了证明团队拥有多少文档,而是为了让已有成果在需要的时候能够发挥作用。
六、结语:让后来的人不必每次从零开始
从最初的 Excel 导入导出经历,到日常的个人知识记录,再到后来整理发布的技术文章,我逐渐发现,工程经验可以通过不同方式传递。
分享代码,可以减少重复实现;分享测试,可以传递验证方法;分享文档,可以减少基础信息的重复讲解;分享工程判断,则能帮助别人理解方案背后的原因与限制。
这些方式没有固定的先后顺序,也不需要每个问题都全部采用。
值得记录的经验,应当根据实际用途选择适合的形式,并考虑整理与维护的成本。
经验不应该只是被保存。它还应该让后来的人有机会在已有成果上继续工作。