为什么你的代码结构正在拖慢 AI?聊聊 Vertical Structure

0 阅读6分钟

image.png

什么是Vertical Structure

feature-based structure

domain-based structure

vertical slice / vertical structure

前端里也常和 Feature-Sliced DesignDDD 风格组织 放在一起讨论

核心意思是:不要先按“技术类型”分目录,而是先按“业务能力”分目录。 这类做法在大型前端和 monorepo 里越来越常见。Nx 官方文章也明确把它和传统“按技术职责分层”的 horizontal approach 对比,建议围绕业务域来组织代码,并强调这样更清晰边界、更利于长期维护。

  • 反对按类型(components/utils)组织代码
  • 建议按功能/业务垂直划分
  • 声称更利于 AI 工具理解和维护简述

简述

1. 什么叫“按类型组织” vs “按功能垂直组织”

按类型组织(horizontal)

src/
  components/
  hooks/
  utils/
  services/
  types/
  pages/

问题是,一个业务功能的代码会被拆散到很多地方。 比如“订单列表”功能,可能会分散在:

  • components/OrderCard.tsx
  • hooks/useOrders.ts
  • services/orderApi.ts
  • utils/orderFormat.ts
  • types/order.ts

这样做的坏处是:你想改一个功能,要到处找。


按功能/业务垂直组织(vertical)

src/
  features/
    order-list/
      ui/
      model/
      api/
      lib/
      types/
    checkout/
      ui/
      model/
      api/
  entities/
    order/
    product/
  shared/
    ui/
    lib/
    api/

这里“订单列表”相关代码尽量都放在 order-list/ 下面。 也就是:先看这是哪个业务,再看里面有哪些技术实现。

Feature-Sliced Design 官方文档也是这个方向:先按 slice(业务切片)组织,再在切片内部用 ui / model / api / lib 这些 segment 继续分。

feature-sliced.design/docs/get-st…


2. 为什么有人反对纯 components/utils 这种组织方式

不是说 components/utils 完全错,而是它更适合:

  • 小项目
  • demo
  • 业务很简单
  • 团队人数少
  • 生命周期短

一旦项目变大,这种结构会有几个典型问题。

第一,业务边界被打散

技术类型目录会把“同一个业务功能”的代码拆开。

开发者看到的是“组件、hook、工具函数”,而不是“支付、订单、登录、用户资料”。

这会导致:

  • 改需求时,需要跨多个目录跳来跳去
  • 新人很难快速理解“某个业务到底由哪些代码组成”
  • 删除一个功能时,很难知道能删到哪里

Nx 也在讲这个点:从 layered/horizontal 走向 domain-oriented structure,目的是让代码围绕业务能力形成清晰边界,并且这些边界能相对独立演进。

nx.dev/blog/archit…

第二,shared/utils 很容易变垃圾场

很多团队最后会出现:

  • utils/index.ts
  • components/common/
  • services/base/
  • helpers/

表面上“复用”很多,实际上常常是:

  • 谁都能往里塞
  • 依赖方向混乱
  • 很多工具其实只被一个业务使用
  • 时间长了没人敢动

Feature-Sliced Design 专门强调要控制 shared 的范围,并通过公开 API 和依赖方向来避免“所有东西都互相 import”。

feature-sliced.design/blog/fronte…

第三,跨团队协作时 ownership 不清楚

按业务垂直拆以后,通常更容易看出:

  • 订单谁负责
  • 支付谁负责
  • 用户体系谁负责

Nx 文章里也提到,业务域边界往往会映射团队边界,这和 Conway 定律是相通的。


3. “Vertical Structure” 到底好在哪

好处 1:改一个需求时,影响面更集中

比如“优惠券在结算页展示逻辑要改”,你大概率先去:

features/checkout/

而不是满项目搜:

  • components
  • hooks
  • services
  • utils
  • constants

这会明显降低“找代码”的成本。


好处 2:更容易做边界控制

垂直组织不是只换个目录名,它真正有价值的是:

  • 同一个业务的内部实现尽量封装
  • 外部只通过有限公开接口访问
  • 依赖方向单向清晰

Nx 明确建议用项目/库边界和自动化规则来守住这些边界;FSD 也强调 slice 的 public API 和受控依赖。


好处 3:删除、迁移、拆分功能更容易

如果未来要把一个模块拆成子应用、微前端、独立包,垂直结构更容易迁走。

Nx 的文章就把这一点讲得很直接:先把单体应用按业务域 modularize,之后无论拆成多个 app 还是 microfrontend,成本都会小很多。


好处 4:更容易理解项目

更容易理解:

  • 登录
  • 支付
  • 订单
  • 个人中心

而不是先理解:

  • hooks
  • utils
  • ui
  • services

因为业务域本身就更贴近产品语言。


4. 为什么有人说“这更利于 AI 工具理解和维护”

这个说法有一定道理,但要说清楚:这是一个合理推断,不是已经被严格证明的通用定律。

像 Copilot、Cursor 这类工具,本质上都依赖:

  • 代码上下文
  • 文件结构
  • 命名
  • 边界信息
  • 指令/文档

GitHub Copilot 官方文档明确说,它可以帮助理解代码库的内容、结构和功能,并且它的效果依赖上下文。GitHub 还专门有关于“为 Copilot 提供上下文”的文档。

docs.github.com/en/copilot/…

Microsoft 的 Copilot 最佳实践也建议:给 AI 提供项目架构、模块边界和约定,这样它才能更好处理架构级改动。

github.com/microsoft/v…

Nx 也提到它在编辑器里给 AI 提供 workspace metadata,让模型获得更具体、更贴近项目边界的上下文。

所以,从原理上说:

  • 如果目录就是业务边界
  • 文件命名清晰
  • import 关系干净
  • 每个 feature 自包含

那 AI 更容易推断:

  • 这个功能在哪
  • 改动该落在哪
  • 哪些文件相关
  • 哪些依赖不该碰

这个推断是成立的。

“更利于 AI 理解”不等于“只要 vertical 就一定更强”。

AI 效果还很依赖:

  • 命名是否清楚
  • 是否有 README / ARCHITECTURE 文档
  • 是否有模块边界约束
  • 是否有生成器/规范
  • 是否有大量历史烂代码
  • 文件是不是过大
  • 公共层是不是失控

换句话说,坏的 vertical structure,也可能比好的 type-based structure 更难让 AI 理解。

更准确的说法应该是:

垂直结构 + 清晰命名 + 稳定边界 + 明确约定,通常更适合 AI 工具在大项目里做理解、检索和局部修改。 但这更像“工程实践上的高概率优势”,不是绝对定律。


5. 来源依据

1)Nx:按业务域组织代码,强调边界、可维护性和演进性 Nx 官方文章明确对比了 horizontal 与 vertical/domain-oriented structure,并给出为什么业务域边界更清晰、更易维护。

nx.dev/blog/archit…

2)Feature-Sliced Design: 前端 里很系统的一套 feature-first 方法论 它不是简单“建 feature 文件夹”,而是:

  • 分层
  • 分 slice
  • 限制依赖方向
  • 使用 public API 这些都是为了让前端随着业务增长还能稳定维护。

feature-sliced.design/docs/get-st…

3)GitHub Copilot / Microsoft 文档: AI 依赖结构化上下文与架构信息 官方文档很明确:AI 对代码库的理解依赖结构、上下文、模块边界和项目级说明。

docs.github.com/en/copilot/…