编码前的思考
软件设计的起点
大部分人打开编辑器就开始写代码。这很自然。代码是可见的、具体的、能运行的。但真正的设计发生在代码之前。
软件设计的本质是把模糊的想法转化为清晰的结构。这个过程需要一套思维框架。
💡 Click the maximize icon to view in fullscreen
从想法到代码,中间缺失的那一层就是思考。
Want-Have-Need 框架
设计任何系统,都可以从三个问题开始:
我想要什么(Want):需求的本质是什么。不是功能列表,而是真正要解决的问题。很多时候,表面需求和真实需求不一致。
我有什么(Have):现有的技术栈、架构、约束条件。这些决定了可能性的边界。忽略现实约束的设计是空中楼阁。
我需要做什么(Need):从 Want 到 Have 之间的差距,就是需要填补的部分。这才是真正的工作量。
这个框架强制你面对现实。它阻止你跳过思考,直接开始堆砌代码。
想要:博客评论系统 有:静态生成的 MDX 博客 需要:在静态内容中注入动态交互
这个"需要"揭示了真正的挑战:如何在构建时和运行时之间建立桥梁。
四个递进的层次
确认了 Want-Have-Need,设计工作才真正开始。这个过程有四个递进的层次。
理解边界
边界是约束。约束不是限制,而是设计的基础。
在静态生成和动态交互之间有一条边界。内容在构建时确定,交互在运行时发生。这条边界决定了架构的形状。
不理解边界,设计就会侵入错误的领域。比如试图在构建时处理用户登录,或者在运行时重新生成静态内容。
定义数据
数据结构是系统的骨架。先定义数据,再考虑功能。
什么是一条评论?它包含哪些字段?评论之间有什么关系?数据如何存储和检索?
数据模型的清晰度直接决定了代码的复杂度。模糊的数据结构会导致混乱的逻辑。
设计函数
函数是操作数据的方式。每个函数回答一个问题:这个数据需要哪些操作。
创建、读取、更新、删除。这是基础。但真正的设计在于:如何定位一条评论?如何处理文本变化?如何确保数据一致性?
函数的粒度和组织方式决定了系统的可维护性。
建立抽象
抽象是隐藏复杂度的方式。好的抽象让高层代码不需要关心底层细节。
表现层不需要知道数据如何存储。业务逻辑不需要知道 API 如何调用。数据访问层不需要知道数据库的具体实现。
分层不是为了好看,而是为了让每一层都能独立演化。
💡 Click the maximize icon to view in fullscreen
为什么顺序很重要
这四个层次必须按顺序进行。跳过任何一步,后果都会在代码中体现。
不理解边界就定义数据,数据结构会侵入错误的层次。不定义数据就设计函数,函数的接口会反复变化。不设计函数就建立抽象,抽象会泄漏实现细节。
设计的过程是从外到内、从粗到细、从抽象到具体。每一步都为下一步提供基础。
复杂度的本质
复杂度来自两个地方:问题本身的复杂度,和不必要的复杂度。
问题本身的复杂度是必然的。行间评论的文本定位就是复杂的。DOM 会变化,内容会更新,浏览器有差异。这些复杂度无法消除,只能管理。
不必要的复杂度是设计不当造成的。混乱的数据结构、职责不清的函数、泄漏的抽象。这些复杂度是可以避免的。
好的设计不是消除复杂度,而是把复杂度放在正确的地方。把问题本身的复杂度隔离在专门的模块里,让其他部分保持简单。
文本定位引擎(复杂):
- 处理 DOM 变化
- 实现模糊匹配
- 处理浏览器差异
评论 UI 组件(简单):
- 调用定位引擎的接口
- 渲染定位结果
- 不关心定位细节
复杂度被封装在引擎层,上层代码保持简洁。
策略与选择
设计不是找到唯一正确的答案,而是在约束下做权衡。
同一个问题可以有多种解决方案。全客户端、混合渲染、完全集成。每种方案都有优势和代价。
选择的依据是:时间、资源、技术能力、未来扩展性。没有绝对的好坏,只有当下的合适。
重要的不是第一次就选对,而是留出调整的空间。好的设计允许方案的演进,而不是一开始就锁死。
设计的终点
设计什么时候结束?当你能清晰回答这些问题:
- 系统的边界在哪里
- 核心数据结构是什么
- 关键操作有哪些
- 抽象层次如何划分
不需要完美,只需要清晰。清晰的设计可以随时调整,模糊的设计只会越改越乱。
编码是设计的延续,不是设计的替代。好的代码源于清晰的思考。
下次开始一个新功能时,试着先问自己:
- 我真正要解决的问题是什么
- 我有哪些约束和资源
- 从这里到那里,中间缺什么
- 数据的形状是什么样的
先想清楚,再动手。
Related Posts
Articles you might also find interesting
依赖注入
依赖注入不是关于框架或工具,而是关于控制权的转移。理解这个转移,就理解了软件设计的核心原则。
从意图到架构
技术方案不是设计出来的,而是从问题中涌现的。理解这个过程,就理解了软件设计的本质。
代码的边界:从项目到包的重构实践
将博客系统打包为 npm 包的完整实践,聚焦边界定义、抽象层建立和务实的取舍决策
适配器模式:对现实的妥协
当 PayPro 要求 IP 白名单而 Stripe 不需要,当一个按秒计费另一个按请求计费,架构设计不是消除约束——而是管理约束。适配器模式不是优雅设计,而是对现实混乱的务实投降。
API 测试各种边界情况
边界情况是系统最脆弱的地方,也是最容易被忽略的地方。测试边界情况不是为了追求完美,而是为了理解系统的真实边界。
文档标准是成本计算的前提
API文档不只是写给开发者看的。它定义了系统的边界、成本结构和可维护性。统一的文档标准让隐性成本变得可见。
调用链路追踪法:从断点到根因
功能失效的背后,是一条完整的调用链路。追踪这条链路,定位断点,才能从根本上解决问题。
离屏渲染:照片捕获为什么需要独立的 canvas
实时流与静态合成的本质冲突,决定了系统必须分离。理解这种分离,就理解了架构设计中最重要的原则。
集中式配置:让 Reddit 组件脱离重复泥潭
当同一份数据散落在多个文件中,维护成本呈指数级增长。集中式配置不是技术选择,而是对抗熵增的必然手段。
约束驱动设计:为何选择内存追踪
在 Cloudflare Workers 环境中实现追踪系统,持久化和内存存储之间的权衡不是技术偏好,而是约束驱动的必然选择。
CRUD 操作
四个字母背后,是数据的生命周期,是权限的边界,也是系统设计的基础逻辑
让文档跟着代码走
文档过时是熵增的必然。对抗衰败的方法不是更频繁的手工维护,而是让文档"活"起来——跟随代码自动更新。三种文档形态,三种生命周期。
双重导出管道的架构选择
在用户生成内容场景中,速度与质量的权衡决定了导出架构。理解两种不同管道的设计逻辑,能够更准确地把握产品体验的边界。
端到端 Postback 模拟测试
真实的测试不是模拟完美的流程,而是重现真实世界的混乱。Postback 测试的价值在于发现系统在不确定性中的表现。
错误隔离
失败是必然的。真正的问题不是失败本身,而是失败如何蔓延。错误隔离不是为了消除失败,而是为了控制失败的范围。
Purikura的页面系统
通过五层分层继承复用架构,实现零代码修改的页面生成系统。从类型定义到页面渲染,每一层专注单一职责,实现真正的数据驱动开发。
重复数据的迁移实践:从 N 个文件到 1 个真相源
当同一份 Reddit posts 配置散落在多个文件中,维护成本以文件数量指数增长。迁移到集中式配置不是技术选择,而是对复杂度的清算。
在运行的系统上生长新功能
扩展不是推倒重来,而是理解边界,找到生长点。管理层作为观察者和调节器,附着在核心系统上,监测它,影响它,但不改变它的运行逻辑。
实现幂等性处理,忽略已处理的任务
在代码层面识别和忽略已处理的任务,不是简单的布尔检查,而是对时序、并发和状态的深刻理解
引入懒加载模式
懒加载不是优化技巧,而是关于时机的选择。何时创建,决定了系统的效率和复杂度。
多厂商 AI 调度:统一混乱的供应商生态
当你依赖第三方 AI 服务时,单点故障是最大的风险。多厂商调度不只是技术架构,更是对不确定性的应对策略。
分布式 Workers 的解耦设计
通过微服务架构和队列系统,实现高可用的 AI 任务处理。从单体到分布式,每个设计决策都是对复杂度的权衡。
队列、可靠性与系统边界
探讨消息队列系统如何通过时间换空间,用异步换解耦,以及可靠性背后的权衡
Studio 系统架构:从状态机到端到端流程
深入 Studio 系统的状态管理中心、组件协调机制和 AI 生成的完整数据流,理解前后端集成的设计逻辑
Context 驱动的认证状态管理
认证系统的核心不在登录按钮,而在状态同步。如何让整个应用感知用户状态变化,决定了用户体验的流畅度。