惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Forbes - Security
Forbes - Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
P
Palo Alto Networks Blog
Martin Fowler
Martin Fowler
T
Threatpost
D
Docker
S
Schneier on Security
M
MIT News - Artificial intelligence
G
Google Developers Blog
L
LINUX DO - 热门话题
J
Java Code Geeks
月光博客
月光博客
博客园 - 三生石上(FineUI控件)
IT之家
IT之家
博客园 - Franky
C
Cyber Attacks, Cyber Crime and Cyber Security
K
Kaspersky official blog
Google DeepMind News
Google DeepMind News
N
News and Events Feed by Topic
V
Vulnerabilities – Threatpost
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
人人都是产品经理
人人都是产品经理
Spread Privacy
Spread Privacy
T
Tailwind CSS Blog
爱范儿
爱范儿
阮一峰的网络日志
阮一峰的网络日志
U
Unit 42
C
CERT Recently Published Vulnerability Notes
The GitHub Blog
The GitHub Blog
Simon Willison's Weblog
Simon Willison's Weblog
NISL@THU
NISL@THU
MongoDB | Blog
MongoDB | Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
H
Heimdal Security Blog
Recorded Future
Recorded Future
云风的 BLOG
云风的 BLOG
SecWiki News
SecWiki News
P
Privacy International News Feed
P
Proofpoint News Feed
O
OpenAI News
B
Blog
腾讯CDC
F
Full Disclosure
Apple Machine Learning Research
Apple Machine Learning Research
T
Tor Project blog
H
Hacker News: Front Page
Project Zero
Project Zero
Hugging Face - Blog
Hugging Face - Blog
C
Cisco Blogs
S
Security Affairs

重归混沌的BLOG

给silly实现了一个ernro模块 | 重归混沌的BLOG 给silly实现了一个ernro模块 | 重归混沌的BLOG 第一次在生产环境使用 Vibe Coding | 重归混沌的BLOG 第一次在生产环境使用 Vibe Coding | 重归混沌的BLOG API 设计的艰难抉择 | 重归混沌的BLOG API 设计的艰难抉择 | 重归混沌的BLOG 十年 | 重归混沌的BLOG 十年 | 重归混沌的BLOG 在Go语言中如何使XML加载内存无限趋近于0 | 重归混沌的BLOG 在Go语言中如何使XML加载内存无限趋近于0 | 重归混沌的BLOG 对跨服玩法中的分布式一致性问题进行简单抽象 | 重归混沌的BLOG 对跨服玩法中的分布式一致性问题进行简单抽象 | 重归混沌的BLOG Go语言逃逸分析之slice和map | 重归混沌的BLOG Go语言逃逸分析之slice和map | 重归混沌的BLOG 谈谈观测 | 重归混沌的BLOG 谈谈观测 | 重归混沌的BLOG 写了个AI Agent服务端 | 重归混沌的BLOG 写了个AI Agent服务端 | 重归混沌的BLOG 谈谈代码设计中“严丝合缝” | 重归混沌的BLOG 一次艰难的线上游戏服务器内存排查经历 | 重归混沌的BLOG 一次艰难的线上游戏服务器内存排查经历 如何基于LanguageServerProtocol来编写lint工具 谈谈游戏服务器中RPC模块的设计 谈谈游戏服务器代码抽象 最近碰到的一个分布式一致性问题 谈谈游戏服务器的自动化测试 对Raft协议的一点理解 使用mmap来学习/proc/pid/smaps 2023(完) 再次实现了一个Lua性能分析器 终于给Silly的定时器增加了取消功能 一次虚拟内存排查经历 游戏服务器分布式数据的一种同步的思路 为silly增加了互斥锁 2022(完) Go语言之闭包篇 一例误用unsafe包引起的内存问题 Go语言之内存篇 初识Go语言 重新抽象图形API 给Lua实现了一个数学库 谈谈跨平台图形API的抽象 寻路和Flocking算法的结合 行为树的一种高效实现 内测过程中Shader出现的问题 彻底解决多国语言 谈谈数据库的选型 再谈Lua热更新(终) 初窥Rust 关于游戏服务器的服务拆分 ECS的初步实现 ECS初探 屏幕空间(SreenSpace)的想象力 一些对辐射度量学的理解 深度缓冲和半透明渲染 Mysql的间隙锁 更新一些GPU相关知识 2020 地形渲染之爬过的坑 Lua5.3 GC源码阅读(5) 实现一个数据库存储队列 再学计算机图形学入门 再谈分布式服务架构 游戏上线一个月后的反思 一次并发Bug 双向链表的三种实现 谈谈随机数的使用 再谈性能优化 2019 Lua中的函数式编程 重构登录逻辑 Unity资源管理(续) 谈谈Unity的资源管理 一次关于Cache的性能分析 历史之2018 DC3算法 移动平台native代码遭遇的坑 从CPU层面谈谈优化 开卷有益(UNIX编程艺术篇) GC竞争问题 通过Mesh投影来实现贴花系统 谈谈我对数据同步的理解 又一个类型提升引起的Bug Lua5.3 GC源码阅读(4) Lua5.3 GC源码阅读(3) Lua5.3 GC源码阅读(2) Lua5.3 GC源码阅读(1) 三角形光栅化时遇到的坑 一次git事故 再见2017 又一个lua调试器 客户端缓存落地方案 Paxos算法 HTTP服务器的特点 一次性能优化经历 关于CPU分支预测 C程序中让两个不同版本的库共存 实现了一个AOI模块 一个高可伸缩的游戏服务器架构 关于网络协议封装的一些新想法
谈谈代码设计中“严丝合缝” | 重归混沌的BLOG
重归混沌 · 2025-03-14 · via 重归混沌的BLOG

前几天修复了一个历史遗留 Bug,和同事讨论时他提到了一个词——“严丝合缝的设计”

这个词几乎瞬间击中了我,因为它与我过去坚持的设计理念完全一样,不过我将其称为“设计正确”。 但过往经验也证明,这类“设计正确”反而制造过不少 Bug,彼时我将问题归咎于“抽象不完善”。

支持这一理念的典型论据是:人体的各器官就是严丝合缝合作的。作为万物之灵的人类,值得我们从中汲取灵感。

现在重新审视这个问题时,我发现过去的理解过于片面了。

疫情期间学到的医学常识给了我新视角:人体各器官的合作并非绝对的严丝合缝,而是充满冗余和容错机制。最简单的例子,你把手指上的血管割破了,并不会造成你整个手指的缺血。

同样,若必须依赖“完美抽象”才能避免 Bug,这样的“严丝合缝的设计”真的是理想方案吗?


《程序员修炼之道(第二版)》中反复强调的核心观点是:代码应“易于修改”

无论是设计模式还是编程准则,终极目标都是应对需求变化。无法灵活调整的设计,终将沦为技术债务。

而我曾经的“设计正确”理念,本质是落入了契约式编程(Design by Contract)的陷阱

当时的逻辑是:A 模块调用 B 模块时已确保参数合法,因此 B 无需验证输入

尽管我早年就意识到“设计闭环”的重要性,但在实际编码时,“性能强迫症”仍驱使我走向极端。

近年来随着对性能执念的淡化,我开始反思:这种追求“设计正确”的代码真的易于修改吗?答案显然是否定的。

精密代码如同多米诺骨牌阵列,看似严谨,却可能因一处微小改动引发系统性崩溃。过去踩过的坑已充分印证了这一点


从“高内聚,低耦合”角度来重新审视这个问题。

若 B 模块要求调用方保证参数合法性,本质上已经形成了隐性耦合。用设计原则来描述,这违背了最少知识原则(Law of Demeter)

根据多年经验,实现“高内聚,低耦合”的关键只有一条:

设计模块(函数/类/服务)时,永远不要假设调用方会遵守任何约定

无论外部如何调用,模块内部必须维持自身数据一致性。

这既是我曾提过的“设计闭环”,也可称为防御式编程容错冗余

原则看似简单,实践却困难重重。

一个典型的开发场景是:先实现调用方 A,再开发被调用方 B。

当编写 B 时,由于已知 A 的实现细节,开发者会不自觉地利用这些信息优化 B 的性能

这恰恰是坏的味道的开始——尽管短期运行高效,却埋下长期隐患。

假设需求变更后,需要用新模块 C 替换 A:

由于 C 与 A 的相似性,开发者容易忽略细微差异。若 B 的实现依赖了 A 的某些隐性特征,便可能催生极难发现的边界 Bug。

更棘手的难题在于:容错冗余的粒度如何把握?函数级、类级、模块级还是服务级?这需要开发者基于场景反复权衡,而每个人心中的天平刻度未必相同。

我自己现在的准则是:

  1. 模块/类级/服务级:必须严格校验输入边界和状态约束,防止预期之外的调用弄乱内部数据。
  2. 函数级:可根据情况相信传入的数据,这也有利于我们放心大胆的对于函数进行抽象,而不必担心性能的销耗。

ps. 函数的抽象绝不仅仅是代码复用,甚至可以说根本不是为了代码复用——抽象的本质是建立认知边界。只有深刻理解这一点,才有可能做出更好的抽象决策。