关联知识库:# 编程十四年感悟:复杂度管理与工程实践
编程十四年感悟:复杂度管理与工程实践
原文链接:https://mp.weixin.qq.com/s/DYs0VIk-rduCPHHIGMUVWw
原文标题:入行 14 年,我还是觉得编程很难:给大项目写代码没意思还危险
作者:朱雷(@piglei)
来源:InfoQ 技术公众号
发布时间:2023 年
核心主题:编程经验、工程实践、复杂度管理、代码质量
核心观点
"十年很短,编程很难。软件开发的核心问题是管理复杂度,失控的复杂度就是程序员最大的敌人。"
作者在入行 14 年后的深刻感悟:
- 随着经验增长,编程并不会变得像吃饭一样简单
- 给许多"大项目"写代码不光没意思,还很危险
- 只从技术角度思考问题,成不了好程序员
- 人、流程、工程方法论远比单纯的技术更重要
目录
- 写代码很简单,但写好代码很难
- 编程的精髓是"创造"
- 打造高效试错的环境至关重要
- 避开代码完美主义陷阱
- 技术很重要,但"人"也许更重要
- 求知若渴是好事,但也要注意方法
- 越早开始写单元测试越好
- 程序员最大的敌人是什么?
1. 写代码很简单,但写好代码很难
核心论述
编程正在变得越来越容易学习:
- 学习途径多样化:教学视频、交互式课程、游戏化学习(CodeCombat)
- 编程语言更友好:动态类型语言大受欢迎,开发工具越来越完善
- 门槛大幅降低:从专业技能变成人人可学的普通手艺
但低门槛 ≠ 高质量代码
现实困境:好代码仍然很少
作者在多家公司的真实经历:
- 大型互联网公司的"大项目",代码质量远不如预期
- 数百行的函数、神秘的数字字面量比比皆是
- 不论公司多大、项目多牛,好代码仍是小概率事件
好代码的评价维度
Martin Fowler 的经典定义:
"任何傻瓜都能写出计算机能理解的代码。优秀程序员写人类能理解的代码。"
多维度评价标准:
- ✅ 可读性:易于理解,把人类读者放在第一位
- ✅ 贴合语言特性:使用推荐写法和恰当的语法糖
- ✅ 易于修改:考虑未来需求变更
- ✅ API 设计合理:简单场景易用,高级场景可扩展
- ✅ 性能够用:满足业务需求,保留提升空间
- ✅ 避免过度设计:不过早优化
写好代码的捷径:阅读 <-> 编程 循环
编程和写作的相似性:
- 没有作家从不读书,只读自己的作品
- 职业作家每天在"阅读 <-> 写作"循环中提升
程序员应该:
- 阅读经典项目代码:学习 API 设计、模块架构、编码技巧
- 定期读技术书籍:保持阅读习惯
- 循环往复:在"阅读 <-> 编程"中快速成长
推荐资源:
- Jeff Atwood《Programmers Don't Read Books -- But You Should》(2008,15年后仍不过时)
2. 编程的精髓是"创造"
核心论述
"创造"的定义不限于发布新软件:
- 写一个可复用的工具函数 ✅
- 设计一套清晰的数据模型 ✅
- 优化一个算法提升性能 ✅
创造的价值
保持对"创造"的热情带来的益处:
-
更高效的学习
- 学习新技术的最佳方式:用它开发真实项目
- 在创造过程中学习,效果最好
-
邂逅了不起的东西
- Linux - Linus Torvalds 出于兴趣创造
- Python - Guido van Rossum 在 1989 年圣诞假期开始创造
创造者思维 vs 搬砖工思维
经典的"砌砖工"故事对比:
- ❌ 搬砖工:"我只是在砌砖"
- ✅ 创造者:"我在建造一座大教堂"
思维转变带来的影响:
场景举例:给 API 增加报错提示文字
- 搬砖工思维:快速完成需求就好
- 创造者思维:
- 我想为用户创造什么样的产品体验?
- 怎样的报错文字更能帮助用户?
- 这个设计能达成我的目标吗?
实践建议
立即行动:问自己一个问题
"我的下一份创造会是什么?"
3. 打造高效试错的环境至关重要
反面案例:糟糕的工程环境
作者参与过的一个"成功"产品:
- ✅ 市场表现:设计精美、功能丰富、用户量大
- ❌ 工程质量:
- 零单元测试
- 零自动化测试流程
- 业务逻辑复杂
- 意外耦合多如牛毛
- 每次发布氛围紧张,紧急回滚常见
结果:乐趣荡然无存,只剩下心理素质的锻炼
理想编程体验 ≈ LeetCode 刷题
LeetCode 做题的三大特征:
特征 | 具体表现 |
---|---|
关注点分离 | 每道题独立,可以完全沉浸其中 |
快速精准反馈 | 每次调整代码后,自动化测试快速反馈 |
零成本试错 | 语法错误、逻辑问题无不良后果,心理负担小 |
改善编程环境的工具和理念
可用的改进手段:
1. 设计层面
- 模块化思想:降低耦合,提升正交性
- 设计原则:应用 SOLID 等经典原则
2. 质量保障
- 自动化测试:单元测试 + Mock 技术
- 覆盖关键路径:用测试覆盖业务核心
3. 反馈优化
- 缩短反馈回路:
- 切换更快的编译工具
- 优化单测性能
- 竭尽全力缩短"改代码 → 获得反馈"的时间
4. 架构层面
- 微服务架构:必要时拆分大单体,分散复杂度
核心目标
创造允许高效试错的"代码乐园":
- 让工作像刷题一样轻松愉快
- 这是经验丰富的程序员能为团队做出的最好贡献之一
4. 避开代码完美主义陷阱
核心观点
精益求精 ≠ 追求极致
- ✅ 在代码质量上精益求精是好事
- ❌ 但要避免掉进完美主义陷阱
编程 ≠ 艺术创作
关键区别:
- 作家:可以花数年打磨一本传世之作
- 程序员:在代码上无限钻牛角尖很有问题
务实的代码标准
足够好的代码:
- 满足当前需求
- 为未来扩展留了一些空间
- 就够了
警惕"代码强迫症"标签
作者的招聘观察:
- 有候选人给自己打"代码强迫症"标签
- 虽然能感受到对代码质量的重视
- 但更期望已经远离完美主义陷阱
5. 技术很重要,但"人"也许更重要
单一职责原则(SRP)背后的"人"
SRP 原则定义:
"每个软件模块应该只有一个被修改的理由"
关键洞察:程序不会自己改变,修改的理由都来自"人"
案例对比分析
案例 1:字典数据类
- 支持:存数据、取数据
- 判断:✅ 没问题
案例 2:员工资料类
- 支持:更新个人信息、渲染用户资料卡片图
- 判断:❌ 违反 SRP
为什么?两个不同的修改理由(人):
- 管理员:需要校验"个人电话"字段
- 某员工:希望资料卡片上的"名字"字体加大
理解 SRP 的关键
"是人在要求软件变更。你绝不想把那些不同人出于不同原因所关心的代码混在一起,这样只会把他们和你自己搞糊涂。"
核心要素:
- 理解人在软件开发中的角色
- 关注不同利益相关者的诉求
- 根据"人"来划分模块边界
微服务架构与组织规模
常见误区:只关注微服务的技术优势
真正的关键:微服务与"人"的关系
- 数百人维护一个大单体 → 效率低下
- 许多小组织各自维护独立微服务 → 高效
本末倒置的做法:
- ❌ 缺少特定组织规模(人)作为前提
- ❌ 空谈微服务的各种技术优势
实践建议
转换视角:
- 技术视角 → 人的视角
- 关注组织结构、团队协作、沟通成本
- 必要时的视角转换对你大有裨益
6. 求知若渴是好事,但也要注意方法
程序员的学习挑战
技术迭代极快的现实:
- 三年前流行的框架,一个月前可能已过时
- "每分钟都有一个新的 JS 框架被发明"(玩笑)
后端工程师需要掌握的知识清单(示例):
- 编程语言 / MySQL / Redis / 设计模式 / 用户体验 / 软件工程 / 编译原理 / 操作系统 / 网络基础 / 分布式系统 / …
关注学习性价比
学习成效与投入的关系曲线:
成效 ↑
│ ┌─────(指数级投入)
│ ╱
│ ╱ (快速增长期)
│ ╱
│ ╱
└────────────→ 投入
关键洞察:
- 初级阶段:投入少,成效增长快
- 超过阈值后:继续提升需要指数级投入
学习策略
关键问题:在开始学习前先问自己
"我应该在上图中的哪个位置停下来?"
分类处理:
- ✅ 需要持续精进:核心专业技能(成年累月学习)
- ✅ 蜻蜓点水即可:周边辅助技能(皮毛绰绰有余)
准确判断并分配有限的学习精力,甚至比努力学习本身更重要
挑选合适的学习资料
作者的失败经历
目标:提升产品交互设计能力
做法:购买《About Face 4: 交互设计精髓》(经典权威)
结果:❌ 连第一章都读不完(隔行如隔山)
经验教训
不要"眼睛大,嘴巴小":
- ❌ 只奔着最经典、最权威的资料
- ✅ 挑选更易读、更适合"门外汉"的资料
推荐高性价比入门书
领域 | 推荐书籍 | 特点 |
---|---|---|
设计 | 《写给大家看的设计书》 | 门外汉友好 |
Web 体验 | 《点石成金》 | 简单易懂 |
Linux | 《鸟哥的 Linux 私房菜》 | 系统全面 |
核心原则
接受现实:
- 时间和精力总是有限
- 不能也不需要在所有领域都成为专家
- 在有限资源下做出明智选择
7. 越早开始写单元测试越好
作者的强烈推荐
个人声明:
"我非常非常喜欢单元测试,我认为写单测这件事,对我的编程生涯影响极大。"
生涯分界线:
- 以"开始写单元测试"作为分界线
- 后面那段远比前面那段精彩得多
单元测试的核心价值
多重好处:
- 驱动代码设计改善:倒逼你写出可测试的代码
- 充当代码文档:测试即文档,展示如何使用
- 构建高效试错环境:完善的单测是第 3 点的关键基础
相关深入资源
作者的系列文章:
- 《有关单元测试的 5 个建议》
- 《游戏"蔚蓝山"教我的编程道理》
行动建议
立即开始:
"如果到目前为止,你从未试过写单元测试,或从没重视过测试,我建议你从明天就开始写起来。"
不要犯的错误:
- ❌ "我不测试我的代码"
- ❌ "假如测的话,我在生产环境测"(讽刺梗)
8. 程序员最大的敌人是什么?
常见误解:产品经理是敌人?
段子文化:
- 产品经理总是改需求
- 一天一个新想法
- 程序员苦不堪言
真相澄清:
产品经理不是敌人
为什么需求总在变化?
软件的本质特性:
- 软件生来就是准备被修改的
- 不然为什么叫"软"件?
关键对比:
- 建房子:没人会推倒重建,用更少的钢筋和水泥建一样的楼
- 写软件:需求变更是常态,修改和优化是日常
结论:
- ❌ 产品经理和不稳定需求不是敌人
- ✅ 能否写出易于修改、适配变化的代码,是区分普通程序员和优秀程序员的重要标准
真正的敌人:失控的复杂度
核心论断(引自《代码大全 2》):
软件开发的核心问题是管理复杂度
失控的复杂度就是程序员最大的敌人
导致复杂度增长的要素
来源 | 具体表现 |
---|---|
功能增加 | 更多功能 = 更多代码 = 更高复杂度 |
高可用需求 | 引入消息队列等额外组件和代码 |
高性能需求 | 引入缓存、拆分模块、部分重写 |
推迟的重构 | 项目排期紧张,技术债越积越多 |
忽视测试 | 没人写单测,没人关心质量 |
复杂度失控的后果
最终结果:
"咚!一个大家不愿改、不敢改的'大坑'凭空出现在了所有人的 IDE 中。"
反思问题:
"究竟是谁挖下了这个坑?"
减缓复杂度增长的实践
核心要求:每个人都做到以下事项
1. 编码层面
- ✅ 精通编程语言与工具
- ✅ 写整洁的代码
- ✅ 使用合适的设计模式
2. 代码质量
- ✅ 对重复代码零容忍
- ✅ 抽象库和框架
- ✅ 编写详尽的文档和注释
3. 测试保障
- ✅ 编写规范有效的单元测试
4. 架构设计
- ✅ 适当运用整洁架构、领域驱动设计
- ✅ 分离变动的与不变的
总结
所有实践的核心:
写更好的代码
关键认知:
- 复杂度增长不可避免
- 但可以通过工程实践减缓
- 长期控制在合理范围内是可行的
核心启示
1. 编程是一门需要终身精进的手艺
- 14 年工作经验不代表编程变简单
- 好代码需要在多个维度上反复权衡、精心设计、持续打磨
2. 工程方法论比技术更重要
- 单元测试、模块化、设计原则不是可选项
- 打造高效试错环境是资深工程师的核心贡献
3. 关注"人"而非只关注技术
- 理解 SRP 的关键在于理解"人"
- 微服务的价值在于组织效率,不只是技术花活
- 转换视角看问题,大有裨益
4. 复杂度是最大的敌人
- 产品经理不是敌人,失控的复杂度才是
- 管理复杂度是软件开发的核心问题
- 写更好的代码 = 减缓复杂度增长
5. 保持创造者思维
- 不要只见砖块,不见教堂
- 问自己:"我的下一份创造会是什么?"
6. 学习要讲究性价比
- 准确判断学习投入的停止点
- 挑选适合门外汉的学习资料
- 不在所有领域都成为专家
7. 务实胜于完美
- 避开完美主义陷阱
- 足够好的代码:满足需求 + 保留扩展空间
8. 越早开始单元测试越好
- 单测是职业生涯的分界线
- 从明天就开始写
相关资源
作者其他作品
- 《Python 工匠:案例、技巧与工程实践》
- 《有关单元测试的 5 个建议》
- 《游戏"蔚蓝山"教我的编程道理》
推荐阅读
- Martin Fowler - 代码可读性相关著作
- Jeff Atwood - 《Programmers Don't Read Books -- But You Should》(2008)
- 《代码大全 2》- 复杂度管理
- 《写给大家看的设计书》- 设计入门
- 《点石成金》- Web 用户体验
- 《鸟哥的 Linux 私房菜》- Linux 入门
延伸思考
问题 1:你当前的编程环境
- 能否像 LeetCode 刷题一样快速试错?
- 反馈回路有多长?如何缩短?
- 单元测试覆盖率如何?
问题 2:你的学习策略
- 哪些技能需要持续精进,哪些点到为止?
- 最近的学习资料选对了吗?
- 是否陷入了"只买经典书但读不下去"的陷阱?
问题 3:代码质量现状
- 你的项目代码质量如何?
- 好代码多还是烂代码多?
- 复杂度是否在可控范围内?
问题 4:思维方式
- 你是"砌砖工"还是"建教堂者"?
- 最近一次的"创造"是什么?
- 下一份创造会是什么?
"十年很短,编程很难。复杂度是最大的敌人,而好代码是最好的武器。"