架构师最危险的能力:用正确的技术,做出错误的架构决策

0 阅读20分钟

警惕脱离代码与业务的架构设计:真正的架构能力,不是画图,而是做出正确的工程决策

架构设计最大的风险,不是技术选型落后,而是决策者不了解系统现状,却试图用理想化的方案指导真实的工程实践。

一、一个看似合理的架构建议,为什么可能成为项目的负担?

在软件开发过程中,我们经常遇到这样的场景。

某个业务系统需要增加消息通知功能,包括短信、站内消息以及第三方平台通知。

开发团队根据当前需求,准备通过已有的第三方服务接口完成消息发送,并对调用逻辑进行适当封装。

技术评审时,有人提出:

“为什么不引入统一消息中心?现在这样直接调用第三方接口,未来系统扩展怎么办?”

乍一听,这个建议相当专业。

从架构设计的角度来看,统一消息中心确实具有不少优势:

  • 统一管理不同消息渠道,降低业务与供应商之间的耦合。
  • 支持异步投递、失败重试、限流和熔断。
  • 集中管理消息模板、发送策略以及通知记录。
  • 提供统一的日志、指标监控和链路追踪能力。
  • 为后续接入更多业务系统提供标准化服务。

这些优势都成立。

但真正值得讨论的问题是:当前系统是否已经发展到了需要统一消息中心的阶段?

假设项目面临以下实际情况:

  1. 当前只有两个业务模块需要发送通知。
  2. 现有系统并没有统一消息中心。
  3. 业务通知量很低,也没有明显的性能瓶颈。
  4. 本次需求的开发周期只有两周。
  5. 团队缺乏维护独立消息基础设施的人员。
  6. 短期内没有其他系统接入的明确计划。

那么,为这个需求建设独立消息中心,究竟是在改善架构,还是在制造额外的复杂性?

一个简单的通知功能,可能因此演变成:

业务服务、消息服务、消息队列、模板管理、重试机制、配置中心、监控告警以及部署运维等一整套基础设施建设。

原本可以通过合理封装完成的需求,被扩展成了跨服务的系统工程。

这就是架构设计中一个非常典型的问题:

技术方案在理论上正确,并不代表它在当前阶段具有工程合理性。

真正优秀的架构师,不应该只回答“这样设计是否先进”,更应该回答:

“为什么现在需要这样设计?它解决了什么已经存在或者可以合理预见的问题?”

1.jpg

二、架构师最重要的能力,不是掌握多少设计模式

在很多技术团队中,架构能力容易被理解成技术广度。

比如,一个人熟悉微服务、DDD、CQRS、事件驱动、Service Mesh、Kubernetes、消息队列和各种分布式架构,似乎就意味着拥有较强的系统设计能力。

但实际上,技术知识储备与架构决策能力并不能直接画等号。

架构设计的本质,是在现实约束下进行权衡。

软件架构领域经常讨论一个概念:Trade-off,即权衡与取舍。

任何技术方案都有收益,也必然伴随着成本。

例如:

架构方案潜在收益隐含成本
微服务架构独立部署、团队自治分布式通信、运维复杂度、数据一致性
消息队列异步解耦、流量削峰消息可靠性、幂等处理、故障恢复
统一消息中心渠道治理、复用通知能力平台开发、接入迁移、持续维护
Kubernetes弹性调度、声明式部署平台治理、资源成本、运维门槛
DDD明确业务边界、管理复杂领域建模成本、学习成本、组织协作
事件驱动架构降低直接调用耦合事件契约、最终一致性、可观测性

这些技术本身不存在绝对的优劣。

真正决定方案价值的,是业务规模、系统边界、团队能力、交付周期以及长期维护成本。

一个只有几个人维护的内部业务系统,不一定需要复杂的微服务体系。

一个每天处理数百万条通知、拥有大量业务接入方的平台,则可能非常需要独立的消息基础设施。

架构设计的成熟度,不取决于使用了多少先进技术,而取决于系统复杂度是否与业务复杂度相匹配。

三、为什么长期脱离代码,容易造成架构判断失真?

首先需要明确:

架构师并不一定需要每天编写业务代码。

随着职责变化,架构师可能需要投入大量时间处理跨团队协作、技术规划、系统治理以及长期演进问题。

这些同样是重要的架构工作。

真正危险的,并不是停止高频编码,而是逐渐失去对真实系统的技术感知,却仍然持续输出高影响力的技术决策。

这种问题主要表现在三个方面。

1. 不了解实际依赖关系,容易低估改造成本

设计文档中的系统,往往比真实代码更整洁。

架构图可能只展示三个模块:

  • 用户服务
  • 订单服务
  • 支付服务

但实际代码中,可能存在大量隐性依赖。

例如,订单服务不仅调用用户服务,还直接读取用户数据库;支付状态的修改可能触发多个业务模块;某个公共工具类同时包含订单计算、账户校验和第三方接口逻辑。

如果不了解这些事实,就提出“将订单模块独立拆分成微服务”,看起来很合理,落地时却可能遇到大量障碍。

包括数据库拆分、事务边界调整、接口兼容、历史数据迁移和跨服务调用治理。

因此,决定模块能否拆分的关键,并不是架构图上是否画出了清晰边界,而是代码和数据层面是否真正具备边界。

逻辑上的模块划分,不等于工程上的独立部署能力。

2. 不了解历史约束,容易将技术债误判为设计错误

很多系统中都存在一些看起来不够优雅的实现。

例如:

  • 部分业务逻辑集中在一个大型 Service 中。
  • 多个模块共同依赖同一张数据表。
  • 某些接口保留了过时的参数格式。
  • 系统同时存在新旧两套鉴权机制。
  • 一部分第三方集成仍然采用同步调用。

站在纯技术角度,这些实现可能存在改进空间。

但它们未必都是由于开发人员缺乏设计能力。

有些是历史兼容性要求,有些来自外部系统限制,还有些是业务快速增长阶段做出的合理妥协。

技术债务并不完全等同于错误代码。

它有时是团队主动接受短期成本与长期风险的一种工程选择。

真正需要管理的是:

技术债务为什么产生、当前造成多少损失、未来何时偿还,以及是否值得偿还。

如果不了解背景,只要求团队将所有不符合最佳实践的代码立即重构,很可能造成投入巨大,却没有明显的业务收益。

3. 缺乏运行时认知,容易设计出无法有效运维的系统

系统架构不是静态的代码结构。

它还包含真实运行环境中的性能、资源消耗、故障传播与恢复能力。

例如,引入消息队列之后,架构师必须考虑:

  • 消息投递是否允许重复?
  • 消费者如何保证业务幂等?
  • 消息积压如何监控?
  • 消费失败之后怎样重试?
  • 死信消息如何处理?
  • 消息处理顺序是否有要求?
  • 消息系统不可用时业务如何降级?
  • 如何进行故障演练和数据补偿?

如果设计者只关注“异步解耦”,却没有考虑这些问题,那么所谓的架构升级,可能只是把同步调用的问题转移到了分布式系统内部。

架构的可靠性,不是由组件名称决定的,而是由故障场景下的系统行为决定的。

四、架构师应该如何判断:现在到底要不要引入新架构?

回到最初的消息中心案例。

如果由我来评审这个技术方案,我不会直接要求开发团队建设独立消息中心。

我更倾向于把问题拆成几个层次。

第一层:当前需求是否真的存在架构瓶颈?

先明确现状。

当前消息发送量是多少?有多少业务模块需要通知能力?失败率是否超出业务可接受范围?第三方供应商是否经常切换?

如果没有明显问题,那么可能只需要建立统一的接口抽象。

例如:

interface NotificationProvider {
  send(message: NotificationMessage): Promise<SendResult>;
}

不同通知渠道分别实现该接口:

class SmsProvider implements NotificationProvider {
  async send(message: NotificationMessage) {
    // 短信供应商适配逻辑
  }
}

class EmailProvider implements NotificationProvider {
  async send(message: NotificationMessage) {
    // 邮件供应商适配逻辑
  }
}

业务层不直接依赖具体供应商,而是依赖一个稳定的通知抽象。

这样既能控制当前复杂度,也保留了未来替换供应商和扩展渠道的空间。

需要注意,这个示例只解决接口依赖问题,并不意味着已经实现了可靠投递、事务一致性或消息治理。

第二层:未来扩展是否具有明确依据?

假设未来几个月内会有十几个业务系统接入,通知发送量预计快速增长,且需要统一管理模板、发送记录和渠道策略。

这时,建设统一消息中心就具有更充分的业务依据。

但也不一定需要立即创建独立微服务。

可以先将通知能力抽象为模块,在单体系统内部统一管理。

随着业务增长,再逐步演进为独立服务。

这种方式通常被称为渐进式架构演进。

它强调:

先建立清晰边界,再根据实际需求决定部署边界。

模块化不等于微服务化。

一个边界清晰、依赖可控的模块化单体,在很多阶段都比过度拆分的分布式系统更容易维护。

第三层:引入新架构的收益能否覆盖长期成本?

对于独立消息中心,需要计算的不只是开发工时。

还应该考虑:

  • 建设成本: 服务开发、接口契约、数据设计、迁移接入。
  • 运行成本: 服务器、数据库、消息基础设施和监控资源。
  • 维护成本: 版本升级、故障处理、性能优化和安全修复。
  • 协作成本: 接入规范、跨团队沟通和接口兼容管理。
  • 机会成本: 投入基础设施建设后,业务需求可能被延后。

这里可以引入 TCO(Total Cost of Ownership,总拥有成本)的思路。

架构成本不是上线当天的成本,而是整个生命周期中的持续投入。

如果新增架构带来的长期收益无法覆盖这些成本,就应该重新审视建设必要性。

2.jpg

五、真正专业的技术评审,应该输出决策,而不是口头建议

很多技术评审效率低,并不是因为团队缺乏技术能力。

而是因为评审过程没有建立明确的决策机制。

评审会上经常出现这样的表达:

“这里应该用 Redis。”

“这个场景最好加消息队列。”

“为什么不用微服务?”

“这部分可以通过 DDD 重构。”

这些建议可能有价值,但它们本身不是完整的架构决策。

真正专业的评审,至少应该回答五个问题:

  1. Problem: 当前要解决的核心问题是什么?
  2. Context: 存在哪些业务、技术、人员和时间约束?
  3. Options: 有哪些可行方案,而不是只有一个理想方案?
  4. Trade-offs: 各方案的收益、风险和成本分别是什么?
  5. Decision: 最终选择什么方案,为什么这样选择?

在实际项目中,可以使用 ADR(Architecture Decision Record,架构决策记录)来沉淀关键决策。

例如:

ADR-001:通知能力建设方案

背景

当前系统存在短信与站内消息通知需求,短期业务规模有限,暂无统一消息基础设施。

候选方案

方案实施方式优势风险
A业务直接调用第三方接口开发成本低耦合明显,扩展困难
B建立统一通知模块成本适中,便于扩展需要维护接口抽象
C建设独立消息中心统一治理,跨系统复用开发及运维成本较高

决策

当前阶段选择方案 B。

通过接口抽象隔离第三方供应商,并集中管理通知发送逻辑。

暂不采用方案 C 的原因

当前缺乏跨系统复用需求,独立部署的收益不足以抵消建设与维护成本。

重新评估条件

当出现多个独立系统重复建设通知能力、需要统一渠道治理,或者现有通知模块无法满足可靠性要求时,重新评估独立消息中心的必要性。

注意,这些条件是触发评估的信号,而不是达到某个数量就必须拆分的硬性规则。

这样一来,评审就不再是不同技术偏好之间的争论,而是形成了一份能够复盘、追踪和修订的工程决策。

优秀的架构治理,不仅要记录“做什么”,还应当记录“为什么做”以及“为什么暂时不做”。

六、一个成熟的架构师,必须具备四种技术视角

架构设计需要技术广度,但仅有技术广度远远不够。

在我看来,成熟的架构师至少需要同时保持四种视角。

1. 代码视角:理解系统真实的实现边界

能够通过阅读核心代码、依赖关系和数据库结构,识别模块之间的实际耦合。

知道哪些模块容易修改,哪些接口存在兼容风险,哪些改造可能引起连锁反应。

不一定每天提交代码,但不能长期脱离代码。

2. 业务视角:理解技术决策背后的实际需求

架构不是独立于业务的艺术品。

不同业务对一致性、可用性、性能和交付速度有不同要求。

例如,营销通知允许一定程度的延迟,而支付结果通知可能需要严格的状态核对和补偿机制。

即使使用相同的通知基础设施,也不应该简单地采用完全一致的可靠性策略。

技术方案需要服务于业务目标,而不是让业务迁就某种技术理念。

3. 运行视角:理解系统真实的生产行为

能够阅读监控指标、分析调用链路、参与故障复盘,并理解系统在高负载和异常情况下的表现。

一张架构图只能描述系统预期如何工作。

生产环境的日志、Trace、Metrics 和故障记录,才能帮助团队验证系统实际上如何工作。

4. 成本视角:理解复杂度带来的长期代价

每增加一个组件,就增加了一部分维护责任。

每拆分一个服务,就可能引入新的部署、通信、鉴权和故障边界。

每引入一种新技术,就需要考虑团队学习、人才储备、技术支持和升级兼容成本。

一个复杂架构能运行,并不意味着它具有合理的投入产出比。

优秀的架构师不仅知道如何增加能力,也知道什么时候应该克制复杂度。

七、AI 编程时代,架构师更不能脱离实际工程

随着 AI Coding Agent、代码生成和自动化开发工具的发展,软件工程正在发生明显变化。

过去,一个系统从设计到编码,往往需要大量人工实现。

现在,借助 AI 工具,开发者可以更快地生成代码、搭建原型、完成接口封装,甚至自动执行部分测试和重构任务。

但这也带来了新的架构风险。

代码生成成本下降,并不意味着系统复杂度和长期维护成本也会同比下降。

例如,AI 可以快速生成一个看起来完整的微服务项目:

  • API Gateway
  • 服务注册与发现
  • Redis 缓存
  • PostgreSQL
  • 消息队列
  • Docker Compose
  • Kubernetes 部署配置
  • OpenTelemetry 链路追踪

从代码结构看,方案可能相当完整。

但这些组件是否真的必要?

团队是否具备运营它们的能力?

异常路径是否经过验证?

生成的代码是否符合现有系统的安全边界、数据模型和部署约束?

这些问题仍然需要人进行工程判断。

AI 可以加快实现速度,却不能自动消除错误架构决策带来的后果。

在 AI 辅助开发场景下,架构师的职责反而更加重要:

从单纯指导如何编写代码,转向定义系统约束、验证工程质量、控制复杂度和管理架构演进。

架构师需要确保生成的代码与真实系统兼容,而不是因为生成速度变快,就允许不必要的架构复杂度快速累积。

同样,AI 可以帮助分析依赖、检索代码和提出改造方案,但它给出的结论仍需要经过真实环境验证。

代码可以快速生成,工程责任却无法一键生成。

八、如何避免成为脱离工程实践的架构师?

与其规定架构师每个月必须提交多少行代码,不如建立持续接触真实系统的工作机制。

因为提交次数和代码行数并不等于架构能力。

更加有效的做法是:

第一,持续参与关键代码评审

重点关注核心模块、跨服务接口、数据库变更和基础设施相关代码。

代码评审的目的不仅是发现编码问题,也包括识别正在形成的架构风险。

第二,定期检查系统依赖与技术债务

结合静态代码分析、依赖扫描、架构适应度函数(Architecture Fitness Functions)等手段,持续验证模块边界。

例如,通过自动化检查限制跨模块非法依赖,防止原本清晰的架构在日常迭代中逐渐退化。

第三,重要技术选型必须完成概念验证

对于引入成本较高的新技术,优先构建 PoC(Proof of Concept)。

不仅验证功能能否跑通,还要验证性能、兼容性、故障恢复、部署流程及团队使用成本。

第四,参与重大线上故障复盘

故障往往会暴露设计文档中难以发现的问题。

例如超时传播、资源争抢、依赖服务雪崩、数据库连接耗尽,以及缺乏有效降级机制。

这些信息能够反过来修正架构假设。

第五,建立可追溯的架构决策机制

对于重大技术决策,使用 ADR 等方式记录背景、候选方案、取舍和后续验证结果。

当业务条件发生变化时,允许对历史决策进行重新评估。

架构不是一次性的设计成果,而是随着业务和系统变化持续调整的决策过程。

九、不要把“不写代码”当成原罪,也不要把“懂架构”当成特权

讨论到这里,需要避免走向另一个极端:

认为只要架构师不再承担日常开发任务,就一定不具备架构能力。

这种判断同样不准确。

大型组织中的架构师,确实需要将大量精力投入到技术战略、跨团队协作、平台治理以及系统演进路线的制定中。

要求他们像普通开发者一样持续承担大量业务需求,未必是最合理的职责安排。

真正应该关注的,是一个人的技术判断是否仍然建立在充分的工程证据之上。

一个长期不直接编写业务代码,但持续深入代码库、参与关键设计、理解生产系统、能够推动复杂改造落地的人,依然可能是一位优秀的架构师。

相反,一个每天都在写代码,却无法理解业务边界、系统成本与长期演进的人,也未必具备系统架构能力。

因此,问题的关键从来不是岗位名称,也不是代码提交数量。

而是:

一个人是否还能够准确理解系统的真实状态,并对自己的技术决策承担工程责任。

3.jpg

十、结语:好的架构设计,往往体现在知道什么不应该做

软件工程中有一种常见误区:

把架构复杂度与技术水平直接关联。

似乎引入的组件越多、设计模式越丰富、系统拆分越细,就越能证明技术实力。

但真正成熟的工程实践,往往恰恰相反。

能够用一个清晰的模块解决问题,就不必急于拆分独立服务。

能够通过合理抽象隔离变化,就不必提前建设庞大的基础设施。

能够通过简单、可靠的方案满足当前需求,就不必为了所谓的技术先进性增加系统负担。

当然,这不意味着永远选择最简单的实现。

如果业务规模、可靠性要求和组织协作已经形成真实压力,就应该主动推动系统演进,而不是以“保持简单”为理由回避必要的改造。

架构设计既要避免过度设计,也要避免长期欠设计。

两者之间没有放之四海而皆准的公式,只能依靠充分的信息、明确的约束以及可验证的工程判断。

所以,我认为衡量一位架构师是否优秀,不应该首先看他掌握了多少技术名词,也不应该只看他画出了多么复杂的架构图。

而应该看他能否回答三个问题:

为什么要改变?

为什么选择现在改变?

为什么这种改变值得付出相应的成本?

如果回答不了这些问题,再漂亮的架构图,也可能只是一份脱离现实的技术设想。

如果能够回答清楚,那么即使最后选择的是一个看似普通、甚至不够“先进”的方案,它仍然可能是当下最合理的架构决策。

架构的价值,不在于让系统看起来多么复杂,而在于用可控的复杂度,持续解决真实的业务问题。


技术观点:架构没有绝对的最佳实践,只有建立在真实约束、明确目标和工程证据之上的合理决策。