<a id="philosophy-software-engineering-truths"></a>

# 软件工程的朴素真理

> 软件工程不是把代码写出来，而是在变化中持续交付可靠价值。

软件工程的核心，不是写代码，而是管理复杂性。

代码只是结果。真正困难的是：需求会变化，人会误解，系统会膨胀，历史包袱会累积，边界会模糊，成本会被低估。

软件工程不是把复杂问题变成复杂代码，而是尽量把复杂问题变成可理解、可维护、可演进的系统。

<a id="philosophy-software-engineering-truths-一关于代码"></a>
### 一、关于代码

<a id="philosophy-software-engineering-truths-1-代码是写给人读的顺便让机器执行"></a>
#### 1. 代码是写给人读的，顺便让机器执行

机器不在乎变量名叫 a1 还是 user_discount_rate，但人会在未来反复阅读、修改、排查这段代码。

代码首先是一种沟通媒介，其次才是机器指令。

可读性比聪明感重要。没人会长期欣赏只有你自己能看懂的代码。

<a id="philosophy-software-engineering-truths-2-清晰比聪明重要"></a>
#### 2. 清晰比聪明重要

聪明的代码可能让人佩服一分钟，清晰的代码能让团队少痛苦几年。

调试通常比写代码更难。如果代码本身已经写得过于“聪明”，未来排查问题的人会付出更高代价。

<a id="philosophy-software-engineering-truths-3-命名是编程中最难的事之一"></a>
#### 3. 命名是编程中最难的事之一

一个好名字抵得上一段注释，一个坏名字会制造无数误解。

命名困难，往往说明概念还没有想清楚。

<a id="philosophy-software-engineering-truths-4-最好的代码是不存在的代码"></a>
#### 4. 最好的代码，是不存在的代码

能不写的代码，永远是最好的代码。

代码越多，维护成本越高，出错面越大。删除代码常常才是真正的进步。

少即是多，不是偷懒，而是克制。

<a id="philosophy-software-engineering-truths-5-重复代码不一定坏错误抽象更坏"></a>
#### 5. 重复代码不一定坏，错误抽象更坏

抽象是有代价的。

好的抽象减少复杂性，坏的抽象制造复杂性。没有被真实使用验证过的抽象，往往只是提前制造复杂度。

过早抽象，很多时候比适度重复更糟。

<a id="philosophy-software-engineering-truths-二关于复杂度"></a>
### 二、关于复杂度

<a id="philosophy-software-engineering-truths-6-软件工程的核心是管理复杂性"></a>
#### 6. 软件工程的核心是管理复杂性

软件开发本质上，是把业务世界里的混乱逻辑，转化为可运行、可理解、可维护的数字逻辑。

业务本身有多复杂，系统最终就会有多复杂。

工程能力不是消灭所有复杂度，而是让复杂度有边界、有位置、有解释。

<a id="philosophy-software-engineering-truths-7-复杂度不会消失只会转移隐藏或命名"></a>
#### 7. 复杂度不会消失，只会转移、隐藏或命名

你可以通过架构、抽象、封装、平台化来移动复杂度，但很难真正消灭复杂度。

如果一个系统看起来简单得不可思议，复杂度很可能被推给了用户、运维、调用方，或者藏在某个尚未暴露的角落里。

<a id="philosophy-software-engineering-truths-8-简单不是简陋而是克制"></a>
#### 8. 简单不是简陋，而是克制

简单不是少写几行代码，而是少让人记住东西。

好的设计不是堆更多功能，而是减少不必要的概念、状态、分支和例外。

真正的简单，是让正确的事情容易发生，让错误的事情难以发生。

<a id="philosophy-software-engineering-truths-三关于需求"></a>
### 三、关于需求

<a id="philosophy-software-engineering-truths-9-需求永远不会完整"></a>
#### 9. 需求永远不会完整

用户往往知道自己不满意什么，却未必能准确描述自己真正需要什么。

很多失败项目，不是因为程序员不会写代码，而是因为一开始就没有搞清楚要解决什么问题。

<a id="philosophy-software-engineering-truths-10-需求不清技术越强偏得越快"></a>
#### 10. 需求不清，技术越强，偏得越快

需求不清时，技术能力越强，越可能把错误的方向实现得又快又复杂。

最贵的 bug，通常不是写错了代码，而是理解错了问题。

<a id="philosophy-software-engineering-truths-11-变化是常态"></a>
#### 11. 变化是常态

软件不是在稳定世界里运行的静态产物。

市场会变，用户会变，组织会变，依赖会变，监管会变，基础设施也会变。

所以软件设计不能只追求“第一次做对”，还要考虑未来如何修改、扩展、替换和回滚。

<a id="philosophy-software-engineering-truths-四关于维护"></a>
### 四、关于维护

<a id="philosophy-software-engineering-truths-12-上线不是结束而是开始"></a>
#### 12. 上线不是结束，而是开始

软件不是一次性交付物，而是一项长期债务。

上线只是软件开始接受现实检验的第一天。维护、扩展、排障、迁移、兼容，才是成本的大头。

<a id="philosophy-software-engineering-truths-13-不动的代码也会腐烂"></a>
#### 13. 不动的代码也会腐烂

即使一行代码都不改，系统也可能逐渐失效。

依赖会过时，环境会升级，接口会废弃，业务规则会变化，安全风险会积累。

不维护的系统，迟早会出问题。

<a id="philosophy-software-engineering-truths-14-技术债不是罪假装没有技术债才是罪"></a>
#### 14. 技术债不是罪，假装没有技术债才是罪

为了赶进度牺牲质量，有时是现实选择。

但技术债必须被看见、被记录、被评估。债可以借，但要知道借了多少，利息是什么，什么时候还。

“以后再重构”通常意味着“以后也不会重构”。

<a id="philosophy-software-engineering-truths-五关于质量"></a>
### 五、关于质量

<a id="philosophy-software-engineering-truths-15-测试不是为了证明代码正确"></a>
#### 15. 测试不是为了证明代码正确

测试不能证明系统没有 bug，但能阻止很多旧 bug 复活。

它最大的价值，是让修改不那么可怕。

没有测试的系统，越成功越难改；越难改，越容易变成负担。

<a id="philosophy-software-engineering-truths-16-性能问题要靠测量不要靠感觉"></a>
#### 16. 性能问题要靠测量，不要靠感觉

过早优化是万恶之源。

先让它跑起来，再让它正确，最后才是让它快。

大多数性能问题不是靠猜出来的，而是靠监控、profiling、压测和真实数据定位出来的。

<a id="philosophy-software-engineering-truths-17-安全不是一个功能而是一种默认假设"></a>
#### 17. 安全不是一个功能，而是一种默认假设

系统最脆弱的地方，通常不在算法，而在边界。

输入、权限、网络、并发、时间、状态、依赖、异常路径，才是事故高发区。

安全的基本假设应该是：任何输入都可能有问题，任何边界都可能被突破。

<a id="philosophy-software-engineering-truths-18-稳定不是没有故障而是故障可控"></a>
#### 18. 稳定不是没有故障，而是故障可控

稳定系统靠的不是永远不出问题，而是出问题时能够被发现、被定位、被隔离、被恢复。

日志、监控、告警、追踪、降级、限流、回滚，都是系统稳定性的一部分。

日志不是给程序看的，是给凌晨三点处理事故的人看的。

<a id="philosophy-software-engineering-truths-六关于架构与权衡"></a>
### 六、关于架构与权衡

<a id="philosophy-software-engineering-truths-19-没有银弹"></a>
#### 19. 没有银弹

没有任何语言、框架、架构、平台或工具能解决所有问题。

换语言不一定能解决性能问题，引入新框架不一定能解决组织问题，使用 AI 写代码也不等于解决了需求定义和工程责任。

技术只是手段，不是答案本身。

<a id="philosophy-software-engineering-truths-20-软件工程本质上是权衡"></a>
#### 20. 软件工程本质上是权衡

软件世界里很少有绝对最优，更多是当前条件下的相对合适。

速度、质量、成本、灵活性、稳定性、安全性、复杂度，往往互相牵制。

架构设计不是寻找完美方案，而是在多个不完美选项中，选择团队当前最能承担的代价。

<a id="philosophy-software-engineering-truths-七关于团队"></a>
### 七、关于团队

<a id="philosophy-software-engineering-truths-21-软件工程是团队运动"></a>
#### 21. 软件工程是团队运动

再厉害的独行侠，也比不上一个沟通顺畅的团队。

代码是媒介，协作才是核心。

团队里的隐性知识越多，系统风险越大。没有被写下来、讲清楚、传递出去的知识，都会在未来变成成本。

<a id="philosophy-software-engineering-truths-22-系统的形状往往反映组织的形状"></a>
#### 22. 系统的形状，往往反映组织的形状

沟通混乱的团队，很难产出边界清晰的软件。

职责不清、目标不一、协作低效，最终都会反映到系统里，变成混乱的模块、模糊的接口和难以维护的依赖关系。

<a id="philosophy-software-engineering-truths-23-文档不是装饰品"></a>
#### 23. 文档不是装饰品

文档不是为了证明你写过什么，而是为了让别人少猜。

尤其是记录“为什么这么做”的文档，通常比解释“代码做了什么”更有价值。

代码能说明系统当前怎么运行，但文档能解释当时为什么做出这个选择。

<a id="philosophy-software-engineering-truths-八最终总结"></a>
### 八、最终总结

软件工程的朴素真理是：

技术只是手段，解决问题才是目的。

优秀的工程师，不是写出多复杂的代码，而是能用尽可能简单、清晰、可靠的方式解决复杂问题。

每一行代码，都是未来要承担的责任。

每一个抽象，都是未来要维护的承诺。

每一个系统，都会在变化中接受检验。

所以，软件工程的本质不是“把代码写出来”，而是：

在变化中持续交付可靠价值。
