核心问题

如何判断一个设计“别扭”?

工程品味不是玄学。

它是大量模式识别后的判断力:

这个设计现在能跑,但以后会疼。

有品味的工程师不只是知道怎么实现,还能提前感到:

  • 名字太宽
  • 抽象太早
  • 边界太漏
  • 参数太多
  • 状态太散
  • 错误模型不统一
  • 规则放错地方
  • 新需求会很难落位

核心句:

品味是对未来维护成本的提前感知。

品味来自经验,但可以训练

很多人说:

这个设计不优雅。

但如果说不出原因,就只是偏好。

工程品味要能落到判断:

  • 它违反了哪条边界?
  • 它制造了什么耦合?
  • 它隐藏了什么规则?
  • 它让什么变化变难?
  • 它把复杂性转移给了谁?

能说清楚原因,品味才从审美变成工程判断。

别扭信号 1:名字太宽

危险名字:

User
Manager
Handler
Processor
Context
Metadata
Data
Info
Util

这些名字不是绝对不能用。

但如果核心领域里充满它们,说明系统还没真正命名。

例如:

CourseManager

它可能在做:

  • 创建课程
  • 发布课程
  • 编辑课程
  • 管理访问
  • 发送通知
  • 生成报表

品味好的工程师会问:

它到底管理什么?

别扭信号 2:参数太多

函数参数太多,常说明边界不清。

createAccess(
  accountId,
  courseId,
  organizationId,
  subscriptionId,
  purchaseId,
  source,
  validUntil,
  sendEmail,
  auditReason
)

这可能说明:

  • 缺少一个明确 command object
  • 函数承担太多职责
  • 不同场景被硬塞进一个入口
  • 调用方知道太多内部细节

更好:

grantAccessFromPurchase(purchaseId)
grantAccessFromSubscription(subscriptionId)
grantAccessFromOrganizationAssignment(assignmentId)

或者:

grantCourseAccess({
  accountId,
  courseId,
  source,
  sourceId,
  validUntil,
})

别扭信号 3:布尔参数

布尔参数经常让调用难读。

publishCourse(courseId, true)

true 是什么?

  • 是否发送通知?
  • 是否跳过审核?
  • 是否立即发布?
  • 是否记录审计?

更好:

publishCourse(courseId, { notifyStudents: true })

或者拆成不同动作:

publishCourse(courseId)
publishCourseSilently(courseId)

布尔参数常常是隐藏分支的味道。

别扭信号 4:到处都是特殊分支

if (organization.type === "enterprise") ...
if (course.source === "coupon") ...
if (user.role === "admin") ...
if (payment.provider === "stripe") ...

特殊分支不一定错。

但如果同一个判断散落各处,说明缺少合适边界。

例如:

if (payment.provider === "stripe")

可能应该被收进:

paymentProviderAdapter

而不是让全系统知道 Stripe 的存在。

别扭信号 5:可选字段太多

type Relationship = {
  accountId?: string
  courseId?: string
  organizationId?: string
  purchaseId?: string
  subscriptionId?: string
  role?: string
  permissions?: string[]
  metadata?: Record<string, unknown>
}

大量可选字段说明:

你可能把多个不同概念塞进了一个类型。

品味好的工程师会问:

  • 哪些字段总是一起出现?
  • 哪些组合非法?
  • 是否应该拆成多个类型?
  • 是否缺少 discriminated union?

别扭信号 6:规则和数据分离过远

例如:

type Subscription = {
  status: string
  currentPeriodEndsAt: Date
}

然后到处写:

subscription.status === "active" &&
subscription.currentPeriodEndsAt > now

规则散落,系统会腐烂。

更好:

isSubscriptionActiveAt(subscription, now)

品味好的人会把重要规则放到可信位置。

别扭信号 7:抽象比问题更难懂

坏抽象:

AbstractActorResourceRelationResolver

如果理解抽象比理解业务本身还难,它可能错了。

好的抽象应该降低理解成本。

例如:

CourseAccessGrant
AccessPolicy
OrganizationMembership

这些名字来自领域,而不是来自工程炫技。

别扭信号 8:新需求没有自然落点

当产品说:

新增兑换码访问课程。

如果团队第一反应是:

这个要改哪里来着?

说明系统缺少结构节奏。

好的系统应该有自然落点:

course-access
coupon
grant source
access policy tests

品味就是能感到“这个需求应该落在哪里”。

品味和克制

工程品味不只是知道什么时候该抽象。

也包括知道什么时候不要抽象。

例如两段代码相似,但概念不同:

createPurchase()
createEnrollment()

此时复制可能比抽象更好。

品味好的工程师不会为了消除表面重复,制造深层耦合。

核心句:

品味的一半是克制。

品味检查清单

看到一个设计时问:

  1. 名字是否具体?
  2. 抽象是否来自真实变化轴?
  3. 参数是否过多?
  4. 布尔参数是否隐藏分支?
  5. 可选字段是否暗示多个概念混在一起?
  6. 业务规则是否有可信位置?
  7. 新需求是否有自然落点?
  8. 调用者是否知道太多内部细节?
  9. 这个设计是降低理解成本,还是增加理解成本?
  10. 它现在能跑,但未来会在哪里疼?

小结

  1. 工程品味是对未来维护成本的提前感知。
  2. 品味不是偏好,必须能说清工程理由。
  3. 名字太宽、参数太多、布尔参数、特殊分支、可选字段过多都是别扭信号。
  4. 好抽象应该降低理解成本。
  5. 新需求没有自然落点,说明系统缺少结构节奏。
  6. 品味的一半是克制,不为了表面重复制造深层耦合。