这是一个系列的第一篇。我是一名数据开发工程师,最近在系统地学习软件架构。这篇文章记录的不是教科书式的知识梳理,而是一个学习者从"凭感觉写代码"到"有意识地做设计决策"的过程。
起因:Vibe Coding 的天花板在哪里
我有一个个人网站,技术栈是 Next.js 15 + Payload CMS 3.x + PostgreSQL + TypeScript,完全是 vibe coding 出来的。所谓 vibe coding,就是借助 AI 编程工具,凭感觉描述需求,让 AI 帮你写代码。
网站跑起来了,博客能写了,权限控制也有了,Docker 部署也搞定了。看起来一切都挺好。
但当有人问我:"你的权限控制逻辑写在前端还是后端?"——我答不上来。
当有人问我:"如果将来不想用 Payload CMS 了,换掉它的成本有多大?"——我也答不上来。
代码是 AI 写的,能跑,但我说不清它是怎么组织的。这就是 vibe coding 的天花板——它替代了代码的实现,但没替代人的思维。 具体来说,它没替代从用户角度、架构角度、系统角度去思考的能力,也没替代评估"什么样的实现是好的"的能力。
这让我意识到:vibe coding 的好坏,终究取决于使用它的人的认知。于是我开始学软件架构。
第一课:架构不是画图,是做决策
我最初对"软件架构"的理解是这样的:一堆模块,用箭头连起来,画成一张图。因为我的背景是数据开发,所以脑海中浮现的总是数据中台那种架构图——数据开发模块、数据分析模块、调度运维模块,一个个方块排列整齐。
这个理解不算错,但它只看到了架构的结果,没看到架构的过程。
Martin Fowler 在他的文章 "Who Needs an Architect?" 中引用了架构师 Ralph Johnson 的观点:架构是那些重要的、难以更改的决策。 不是图上有几个方块,而是你为什么选择这样划分方块、为什么让这些方块以这种方式连接。
回头看我的个人网站项目:用 Next.js 而不是 Vue,用 Payload CMS 而不是自己写后台,用 PostgreSQL 而不是 SQLite——这些选择一旦做了,后面要改的成本都很高。这些决策就是架构。
所以架构是一个"动词"——它是人进行设计决策后的产物,那些模块图、分层图只是决策的结果。有了决策,才有了模块和分层。
分层架构:最基础的组织方式
理解了"架构是决策"之后,第一个要学的就是最基础的架构模式——分层架构(Layered Architecture)。
以我的个人网站为例,当一个读者打开博客页面时,发生了这些事:
- 前端发送请求
- 后端接收请求
- 判断用户是谁(认证)
- 判断用户能做什么(授权)
- 根据权限去数据库查对应的数据
- 把数据返回给前端
- 前端渲染页面
这七步可以按职责分成四层:
- 展示层:前端渲染页面、处理用户交互
- 控制层:路由分发、认证、授权
- 业务层:根据具体规则查询数据
- 数据层:跟数据库打交道
分层的核心规则:每一层只跟相邻的层对话,不能跨层直接沟通。 比如前端不应该直接访问数据库,必须通过中间的业务层。
有趣的是,我用 vibe coding 做出来的项目,虽然我没有刻意设计分层,但框架本身帮我维持了一个基本的分层结构:Next.js 管展示层,Payload CMS 管业务和数据访问层,PostgreSQL 管数据存储层。这是框架的价值——它用约束帮你做了一部分架构决策。
耦合与内聚:一枚硬币的两面
我同时在做一个 AI Agent 项目(用 Node.js + Anthropic API 从零构建)。这个项目最初所有代码都写在一个文件里——工具逻辑、安全检查、Review、配置、Agent 循环,全部混在一起。
后果是什么?每次添加一个工具,我都要从头到尾审一遍代码,牵一发而动全身。
这八个字就是高耦合最精准的描述。
耦合(Coupling) 衡量的是模块之间的依赖程度。改动一个地方需要连带改动的地方越多,耦合就越高。
后来我们做了拆分:工具逻辑独立出来,做了插件化的注册机制,每个工具自带自己的 pre-execute 和 post-execute 钩子。拆完之后,加一个新工具只需要写一个新文件,注册进去就行,Core 不需要改,其他工具也不需要改。
拆分的时候,我并不是随便拆的。安全相关的逻辑放在一起,工具相关的逻辑放在一起——每个模块内部处理的是同一类事情。这就是内聚(Cohesion):一个模块内部的元素在多大程度上是为了同一个目的而存在的。
好的设计只有一个标准:低耦合 + 高内聚。
补充一点:像 logger(日志)和 config(配置)这类通用模块,被很多模块调用是正常的,不算高耦合的问题。因为它们极其稳定,几乎不变。依赖不可怕,依赖"经常变的东西"才可怕。
信息隐藏:模块边界该画在哪里
耦合和内聚告诉我们"好设计长什么样",但没告诉我们"怎么拆"。具体在哪里画模块的边界?
答案来自 David Parnas 在 1972 年提出的信息隐藏(Information Hiding) 原则:模块的边界应该由"什么可能会变"来决定,而不是由流程的步骤来决定。
这句话比较抽象,用我的 Agent 项目来说明。
假设有两种拆法:
拆法 A:按流程步骤拆。 Agent 处理请求的流程是:接收输入 → 分析意图 → 选择工具 → 执行前检查 → 执行工具 → 执行后检查 → 返回结果。按这个思路拆成七个模块,每个对应一步。
拆法 B:按"什么会变"来拆。 问自己:将来哪些东西会独立变化?工具会经常增减(所以工具单独做插件化);安全策略随工具不同而不同(所以安全逻辑跟着工具走,用钩子实现);核心循环相对稳定(所以 Core 独立出来,尽量不动)。
我实际选的是拆法 B。每个工具自带自己的 pre-execute 钩子,而不是做一个统一的"执行前检查模块"。为什么?因为不同工具的安全检查会独立变化——WriteFile 需要检查路径安全,calculate 可能什么都不用检查,fetch_url 要检查域名白名单。
如果按拆法 A 做了统一的检查模块,每加一个新工具就得去改那个模块,加一个 if 分支。时间长了又回到了"牵一发而动全身"。
信息隐藏还有一个具体的体现:钩子机制让 Core 被"隐藏"了每个工具的具体安全策略。Core 只知道"在执行前调一下 pre-execute",不需要知道里面做了什么。好的架构让每个模块只知道它该知道的事情,不多也不少。
可以这样记:变化是敌人,模块边界是防火墙。把防火墙建在最可能着火的地方之间。
解剖自己的项目:Vibe Coding 出来的架构长什么样
学完基本概念后,我回头审视了自己的个人网站项目。仅从目录结构就能读出很多信息:
app/(main)/是前台路由——用户看到的页面app/(payload)/是后台路由——管理员用的界面src/payload/collections/定义了数据模型components/放可复用的 UI 组件
前台和后台通过 Next.js 的 Route Groups 分开了,这是一个好的架构决策——不同角色的用户走不同的路由组,布局也各自独立。
但也发现了问题:
问题一:一个空目录。 content/blog/ 里什么都没有,博客内容实际上全部存在 Payload 的数据库里(通过 payload.find({ collection: "blog" }) 查询)。这个空目录大概率是早期打算用 Markdown 文件存储博客时留下的,后来改了方案但目录没删。在架构里,这种旧决策留下的痕迹叫技术债务(Technical Debt)。它不紧急,但会造成困惑。
问题二:管理接口的位置。 seed(数据初始化)和 create-admin(创建管理员)这两个只有开发者自己用的接口,放在了 app/(main)/api/ 下面,也就是前台路由组里。前台路由组的职责是"服务普通用户",管理工具的职责完全不同。这是一个关注点分离不彻底的地方。不过代码里有生产环境禁用保护在兜底,所以风险可控。
架构决策时的三个问题
在学习过程中,我还接触到了评估架构决策的思考框架。做每个架构决策时,可以问自己三个问题:
- 这个决策牺牲了什么换来了什么?(取舍)
- 如果将来要改,成本有多大?(可逆性)
- 最可能变化的部分,有没有被隔离开?(信息隐藏)
用这三个问题审视我选择 Payload CMS 的决策:
- 取舍:我用了跟 Payload 的高耦合,换来了快速交付一个可用的网站。这是便利性 vs 可替换性的取舍。
- 可逆性:换掉 Payload 意味着后端业务接口和数据层全部重写,只有前端展示层能保留。这是一个低可逆性的决策。
- 信息隐藏:Payload 把 API 接口、业务逻辑、数据访问都包在了一起。变化没有被很好地隔离——但对个人项目来说,这个取舍是合理的。
作为对比,用 Docker 部署这个决策的可逆性就很高——不想用了回到直接部署,代码一行不用动。
概念之间的关系
最后把这篇文章涉及的所有概念串一条线:
架构是重要的设计决策 → 最基础的决策模式是分层架构 → 分层是为了实现低耦合高内聚 → 具体怎么划分模块边界由信息隐藏原则指导 → 信息隐藏的依据是**"什么会变"** → 每个决策都需要评估取舍和可逆性 → 旧决策留下的痕迹就是技术债务。
这些概念不是孤立的知识点,它们是一套完整的思维工具。
下一篇预告
这篇文章覆盖的是基本词汇和项目解剖。下一篇我们会进入架构模式的学习——不是背诵"有哪些模式",而是理解每种模式在什么条件下合适、牺牲了什么。
本文是「从直觉到决策」系列的第一篇。这个系列记录我学习软件架构的完整过程——从一个 vibe coder 的视角,把实践中积累的直觉转化为有意识的架构能力。