系统设计时要避免的 3 个误区
2024年12月16日
系统设计几乎是现代后端与全端工程师,在工作与面试中高频率会要面对的议题;然而,在刚接触系统设计时,很容易踩到一些坑,让最终的设计品质不如预期。
对此,在这篇文章中,我们会讨论一些在工作上或面试中,做系统设计时常见的误区。推荐读者们可以把这篇提到的点,当做一个检核清单,在工作上或准备面试时,可以拿来检核,确保没有不小心踩到类似的坑。
没有确认解决对的问题
由于工程师的职责是要解决问题,最终需要端出解决方案,很多时候会不小心就一头热于「解决」上,而没有先去确认解决的问题,是不是对的问题。
试想下面两种情境,读者们觉得哪一个带来的效益比较高呢?
- 有绝妙的解法,但是解决错的问题
- 解法可能还不是最佳解,但是解决对的问题
相信多数人会认为是第二种状况效益会高。因为第一种状况,即使解决方法本身很绝妙,但解错问题,导致的结果是原本的问题完全没有被解决到。这就像在面试中,提出一个很精妙的演算法,但是因为解错问题,原本的问题的测试案例全都没有通过;又或者在现实工作中,打造出一个看似很厉害的产品,结果没有帮使用者解决问题,无法创造价值,导致没有人买单 (详细见先前我们写过的 技术上很困难,但对使用者没价值的事 一文)。
假如要避免自己踩了这种坑,一个有效的方法,是问自己是否「听到问题后,总会想要直接跳下去解问题?」,如果是的话,很可能就会踩到这种坑。
举个具体的例子来说,假如要设计短影音系统,假如没有多去厘清问题,可能一开始就会想到现有短影音平台的优势是演算法,所以需要有一个更强大的演算法,因此需要有有各种方法搜集使用者偏好,借此调教演算法。
然而,很可能工作上的产品经理或面试中的面试官,不是想讨论传统短影音系统的设计 (例如 BeReal 就提出完全不同类型的短影音概念,不已演算法为主心,且相当受到欢迎),这时如果直接跳到想演算法为中心的设计,就会变成解错问题。
先发散再收敛
假如你发现自己总会想要直接剃下去解问题,该如何有效改掉这个习惯呢? 一个推荐的方法是有意识提醒自己「先发散再收敛」,在没有发散之前,不要下任何定论。
所谓的发散,是去探索问题,具体来说:
- 在工作上,一定要多对产品经理提问,了解产品的愿景与方向、想带给使用者的价值等等
- 在面试中,要先花时间跟面试官提问,至少面试开头前五分钟,应该都要专注于问问题
在探索问题时,推荐要探索目前有的限制。在软体工程领域,很多时候加上限制后,解法会完全不同。如过前期没有先探索限制,很可能导致想完解法后,才发现不符合实践状况,导致必须要重新设计
除此之外,在发散的过程中,要有效辨别极端状况 (edge cases)。毕竟如果只想到正常情况 (happy path),其他人也想得到,这样就没办法体现资深的价值。如果对这个要点感兴趣,推荐读先前我们写过的《考量极端案例 (edge case) 是工程师的基本功》一文。
花太多时间在不重要的细节上
在一个系统中,通常会有很多细节可以讨论,然而在实际工作中,大家能一起开会的时间有限,不该把时间浪费在不是核心的细节上。当讲太多不是最重要的细节,会让讨论变得冗长且失焦。而在系统设计面试中时间更短 (通常是在 45 到 60 分钟左右),如果不拉出重点来讲,而仅是谈一些细碎的面向,非常可能无法通过。
举例来说,如果今天是要设计一个社群媒体系统,这种系统比起其他系统,最常见的问题是,会在极短时间涌入极大量的流量,这是许多其他系统不会有的问题。所以如果在讨论社群媒体系统时,没有去讨论如何处理尖峰时段的吞吐量问题,在面试中可能会被认为没有抓到重点,进而无法通过。
同样地,如果是在讨论金流相关系统,在金流系统中最会被担心的问题,是重复收费或者余额不足却能成功支付,毕竟如果出现这类问题的话,估计没有人敢再用该金流系统。而要解决这问题,在分散式的状况下,如何确保不同节点之间能维持一致性 (consistency)。同样地,如果在面试时被问到这种系统,却没有去讨论一致性问题,很可能就会被认为不知道如何抓重点。
要辨别核心,可以看一些不同的重要面向:
- 一致性要求:上面提到的金流系统外,常见的抢票系统也需要确保同个位置不会被多个人同时买到
- 读写比例:写多于读或读多于写的系统,要关注的面向会很不一样
- 流量:社群网站类型的系统,很可能会在某个事件高峰 (例如 Talor Swift 演唱会) 而有大量的流量灌入
- 效能:交易类的系统,几要有几毫秒的延迟,就可能导致错失交易机会
前一个段落提到的「确保解决对的问题」之所以重要,是因为这样能确保在设计时,能够把时间花在该花的细节上。
假如今天要设计一个库存管理系统,在了解需求后,会理解到因为库存管理多半是 2B 的系统,所以用的人不多,但是对库存精确度的掌握需要很高。在这种情况下,如果没花时间讨论一致性,而是花时间讨论流量优化,将会浪费宝贵的时间,务必要避免。
解法不符合系统的脉络
在系统设计时,符合系统脉络非常重要。很多时候,符合脉络的设计,是好过于「遵守最佳实践」的设计。这是因为真实世界的系统,要考虑的面向很多,而理论上的最佳实践,可能仅在某些条件下适用。因此,推荐不要针对某些问题,在对于脉络了解不够清楚的状况下,就直接选某个看似最佳实践的方式。
举例来说,「因为快取能协助加速与减少运算,于是在任何地方都加上快取」就是个很典型的不考虑脉络的陈述。因为技术的选择背后往往有取舍,没有考量脉络,直接强加,很可能会有预期外的负面效益。
以快取来说,当选择快取意味着会有资料不一致的状况,同时代表多一个快取元件要维护,增加系统的复杂度。在某些状况下,快取的效益能让这些负面的点被接受,但不是所有状况都是。
就常见的电商系统来说,商品资讯可能是适合快取的对象,因为读取量大,快取能帮上很大忙,同时更新频率不高,相对不担心一致性的问题。
但是库存管理的部分,可能就不太适合快取,因为库存的变动频繁,且要追求准确,特别是在圣诞促销等档期时,库存变动又会更快,要避免消费者买到已经没货的东西这种状况,就要尽可能减少不一致,这时加上快取可能就不是个好选择。
阅读更多
如果你对「系统设计要避免的误区」这主题感兴趣,我们在 E+ 有更深入的讨论。有兴趣的读者,欢迎加入 E+ 成长计划。
本文为 E+ 成长计划的深度内容,截取段落开放免费阅读。欢迎加入 E+ 成长计划阅读完整版本 (点此了解 E+ 的详细介绍)。