PaxLee
PaxLee学无止境
返回列表
小团队系统设计:别过早抽象,先画边界再画关系
技术软件工程系统设计软件架构工程决策小团队实践

小团队系统设计:别过早抽象,先画边界再画关系

发布于 2026年7月31日10 min read

小团队做系统设计时容易陷入「为未来设计」的陷阱,引入过多抽象层。本文提出一个基于边界划分和依赖关系的决策框架,帮助判断何时该抽象、何时该保持简单,避免过度工程化。

小团队系统设计:别过早抽象,先画边界再画关系

上周有个朋友问我,他们正在做一个AI写作工具,最初只接了一个大模型(GPT-4),后来想支持Claude和本地模型,于是团队开始设计一个“通用模型适配器”——工厂模式、策略模式、接口定义、参数标准化……代码写了三周,最后发现实际运行时,三个模型的差异点只有两个:请求格式和响应解析,其他逻辑完全一致。他们花了三周做了一个并不需要的抽象层。

这不是个例。小团队在系统设计时,最容易犯的错不是设计不足,而是过早抽象。我们总担心“以后万一要扩展怎么办”,于是提前引入各种设计模式、分层、中间件,结果代码变得越来越重,每次改动都要跨多个文件,新人上手成本直线上升。

为什么小团队更容易过度抽象?

原因有三:

  1. 经验主义陷阱——团队里有人从大厂出来,见过复杂系统,于是把微服务、事件驱动、DDD等直接套在小项目上,忽略了上下文。
  2. 对未来的恐惧——老板说“我们以后要支持十个模型”,于是设计成十种模型都能插拔,但实际上可能一年内只用到两个。
  3. 工程师的洁癖——代码不够“优雅”就觉得难受,宁愿多花时间也要让类图看起来完美。

但代价是实实在在的:认知负荷增加、调试路径变长、变更成本上升。小团队本来人就少,一个抽象层可能让一个功能从半天变成两天。

我的决策框架:先画边界,再画关系

做过几个产品后,我总结了一个简单的判断方法,叫“边界-关系图”。核心是:先确定哪些东西真的需要独立变化,再决定它们之间如何连接。

具体步骤:

1. 列出所有变化点候选人

把未来可能变化的地方列出来,比如:

  • 不同AI模型的请求格式
  • 不同数据库(MySQL VS PostgreSQL)
  • 不同支付渠道(微信、支付宝)
  • 不同UI主题

注意:这里只列你真正经历过或确认短期内会遇到的变化点,不要列“万一哪一天……”的幻想。

2. 给每个变化点打两个分:

  • 发生率:未来6个月内,这个变化发生的概率。0-10分。
  • 影响范围:如果变化发生,需要改多少个文件/模块。0-10分。

假设“AI模型请求格式”的发生率是8(因为已经在计划接入第二个模型),影响范围是5(涉及请求发送、响应解析、错误处理等)。而“数据库切换”发生率是1(没计划换),影响范围是8。

3. 判断是否需要抽象:

  • 如果发生率低(<4),直接硬编码,等变化来了再重构。
  • 如果发生率高且影响范围大(>5),才值得做抽象层。
  • 如果发生率高但影响范围小(比如只改一个函数),用简单封装即可,不需要工厂模式。

回到AI模型例子:虽然发生率8,但影响范围只有5(因为其他逻辑共享),所以只需要一个简单的配置文件和switch-case,或者一个函数映射,完全不需要适配器模式。

4. 画边界:确定哪些代码属于“一起变”的

使用限界上下文的思想(但不要求完全DDD),把变化点对应的代码区域划出来。比如:

  • 模型调用层:包含请求构造、响应解析、重试逻辑
  • 业务逻辑层:包含提示词组装、结果处理、用户交互
  • 数据层:持久化、缓存

边界的原则是:内部高内聚,外部低耦合。如果“模型调用层”内部的变化不会影响业务逻辑层,那么边界就是有效的。

5. 画关系:决定边界之间的依赖方向

小团队最容易犯的第二个错误是依赖方向混乱。比如业务逻辑层直接依赖模型调用层的具体实现,导致切换模型时业务逻辑也要改。正确的做法是:**业务逻辑层只依赖模型调用层的接口(一个简单的抽象),而不是具体类。**但这个接口只需要定义业务所需的方法,不需要覆盖所有模型特性。

例如:

# 业务逻辑需要的是:
def generate_text(prompt: str, model_config: dict) -> str:
    pass

而不是:

# 过度设计
class ModelInterface:
    def generate(self, prompt: str, temperature: float, max_tokens: int, stop_sequences: list, ...):
        pass
    def stream_generate(self, ...):
        pass
    def count_tokens(self, ...):
        pass

只抽象业务真正需要的,忽略未来可能用到的。

一个可执行的检查清单

每次做系统设计决策时,可以快速过一遍:

  1. 这个变化点未来6个月内是否真的会发生?(如果答案是否,先不抽象)
  2. 如果不抽象,等到变化来时再改,时间和成本是否可接受?(如果可接受,先不抽象)
  3. 抽象后,代码行数是否减少?(如果增加,可能需要重新思考)
  4. 抽象层是否引入了新的依赖或第三方库?(如果是,考虑是否值得)
  5. 团队里每个人都能理解这个抽象吗?(如果只有一个人懂,风险很高)
  6. 测试是否会因为抽象而变得更容易?(如果更难,放弃抽象)

失败的可能性

当然,这个框架不是万能的。有时候过早抽象也能带来好处,比如:

  • 团队经验丰富,能准确预测未来变化,提前设计可以节省后续重构成本。
  • 项目需要快速给客户演示“支持多个模型”,提前抽象可以减少演示时的紧急修改。
  • 抽象层的设计本身是学习过程,能帮助团队理解问题域。

但对于大多数小团队,尤其是不确定市场方向时,保持简单是最安全的策略。我在“往日时光”做AI写作产品时,初期只支持一个模型,直到用户反馈要求其他模型才动手,结果发现改动量比预想的少很多。

总结

系统设计不是堆设计模式,而是管理不确定性。小团队资源有限,更应该把精力放在核心业务逻辑上,而不是为可能永远不会发生的变化铺路。先画边界,明确哪些代码会一起变,再画关系,让依赖方向清晰,最后用最小的抽象解决问题。

下次你设计新功能时,试试先问自己一句:“如果现在不做抽象,等变化来了再改,我承担得起吗?” 如果答案是“能”,那就先别抽象。

PaxLee