不忘初心,坚持匠心
1. 提出问题优于解决问题
很多时候,公司里的一些矛盾就来自于工程师和产品经理之间,比如我们常常会说产品经理不懂技术,需求提得不够专业。但我们作为工程师也可以想一下,我们是不是应该把自己的位置再往前挪一点,去看看用户到底有哪些困惑,然后提出一个合理的需求去解决它;或者我们自己去体验一下用户的场景,然后提出一个全新的问题并解决它。
“The mere formulation of a problem is far more essential than its solution, which may be merely a matter of mathematical or experimental skills. To raise new questions, new possibilities, to regard old problems from a new angle requires creative imagination and marks real advances in science.”
这是爱因斯坦说过的一段话,大致意思是:我们解决一个问题的时候,常常只需要用到一些数学以及实验的能力就可以了,但提出一个新的问题,以一种新的角度去看待旧的问题,是需要用到我们的创造力才能够做到的,而这恰恰是真正推动科学进步的一部分。
“提出问题”难于“解决问题”。程序员尝试学会如何从用户的角度发现困难,提出需求问题,适配用户场景。不仅仅是一个解决问题的人,而是提出问题的人,不断地思考什么样的需求的问题能让我们的产品更先进
在大厂里面强调 "技术驱动", 并不是说将代码写到极致,写到完美,而是强调“用技术的视野驱动自己交付有价值的产品给用户”,本质是通过技术的手段让业务更好,解决“他们”(这里的用户很重要)复杂的问题,而不是通过技术卓越思考业务问题,这样容易陷入一个“堆积技术”的误区,导致迷失业务本身的方向
2. 决定“不要什么”比“要做什么”更难
人性是贪婪的,在做功能或者架构设计时往往什么都想做,包括我自己在做设计时,对于非功能性需求就更是这样了。
所有的书、教程都在讲:可用性、高的性能、高的扩展性、高的可维护性……这些非功能性需求仿佛成了一个公共的列表,所有的架构都要满足这些需求。我们回过头来仔细想想什么是架构。其实在很多层面上,架构是一种 tradeoff,一种权衡和平衡。作为一个架构师,你才最应该是那个说不的人
在现实中,有很多东西是不可兼得的。比如产品是尽早发布,还是把所有功能都加上,发布一个完美的产品;再比如一致性和性能之间的 balance,我们是选择强一致性,还是选择性能等等。所有架构师都非常熟悉的 CAP 原则,其实本质上就是一个关于 balance 的准则
因此,作为架构师,我非常推荐大家在做架构设计的一开始,就去确立一些做事的原则。比如数据一致性优先级最高,再比如尽早发布基础功能版本的优先级大于延迟发布完善功能产品等。当出现矛盾的时候,我们就可以利用这些原则来进行取舍。
“Deciding what not to do is as important as deciding what to do.”
引自乔布斯的一句话,意思是决定不做什么和决定做什么同样重要。人们认为专注意味着对你需要专注的事情说 Yes。但并非如此,专注意味着你要对其他 100 个好主意说 No,你必须谨慎选择。相比已经完成的工作,他对那些没有完成的工作一样感到自豪
3. 非功能性需求决定架构
在很多人心目中,做架构的第一步是收集需求,把各种需求都收集上,这个架构的目的就是要满足这些功能性需求的,毕竟最终产品是要为用户服务的
事实并非如此,一个好的架构,其实是由非功能性需求决定的,而不是由功能性需求决定的。你会发现,一个功能可以有无数的架构方案来实现,但你为什么最终选择了某个方案,其实是由非功能性需求来进行筛选的。
大家非常清楚什么是非功能性需求,包括性能、伸缩性、可扩展性、可维护性等,甚至还包括了你的团队结构,你团队的技术水平,你对发布周期的要求等等,通过所有这些需求来筛选可使用的方案,最终找到一个合适的架构。所以,非功能性需求是非常重要的,甚至可以说是在你的架构设计中起到决定性因素的。
架构设计完之后,少一个功能性需求,我们很容易就能看出来,未来也可以加上去,它对你的架构不会有本质上的影响。但如果我们忽略的是某一种非功能性需求,那在未来这可以说是一种灾难性的麻烦,很有可能你就需要重写了。比如你架构中的数据一致性问题无法解决,或者在设计的时候没有充分考虑性能问题,这样,所有的功能性的实现其实都没有意义。基本就是 Refactor 了,甚至不应该叫 Refactor,要叫 Recreate 或者 Rewrite,等于你要完全重写整个架构
实际上在架构领域,大家对这点也是有共识的。比如下图中这个 Micro-Kernel 的架构模式来自《面向模式的软件架构》的第一卷,它一大特点就是有比较好的可扩展性,同时通过 Plugin 之间的隔离,能够提高系统的可用性。
微核心架构也叫插件化架构,插件化的思想实际上就是将基础核心模块封装,让那些不通用、定制的逻辑用插件的方式快速扩展,并且互相隔离互不影响,极大的增强了可扩展性,也是一种“逻辑分层”的应用
4. “简单”并不容易
“简单”并不容易。很多架构师都会提到保持简单,keep the simple,但很多时候我们会混淆简单和容易,简单是 simple,容易是 easy,我们是 keep it easy,而不是 keep it simple。
正如乔布斯所说,简单有时候要比复杂更难,需要你对问题、事物的研究非常地深入,你才能找到真正简单的方法。简单其实是蕴含着一种巧妙在其中的。例如我们熟知的布隆过滤器,是一个十分简单的高效重复数据过滤算法,它就非常巧妙地解决了一个问题。
如果你想把一个事情做简单,你需要做很多深入的工作,比如对于架构的简化,很大程度上来自于我们对于技术、开发过程,以及不同业务场景的深入理解,而不仅仅是这个架构写起来好不好写。
举个例子,我们来回顾一下软件生命周期中各个阶段的成本消耗占比。
可以看到,在整个软件生命周期中,成本消耗最高的并不是设计、编码这些阶段,而是维护阶段。也就是说,如果你让维护变得简单,这会是最有性价比的。
// TODO 完善例子
真正的简单是来自于不容易的,就像那句话说的,It's hard to simple,It's easy to complex,简单是很难的,复杂反而是很容易的。
5. 永远不要停止编码
这一点非常重要,对一个架构师来说,要永远记住自己是一个程序员。
作为架构师,我们可能设计了一个非常 high-level 的架构,但代码是软件的最终实现形态,每一个程序员在架构落地过程中的实现,都可能会影响架构的最终呈现。另外,如果你放弃编码,最大的影响不是说你代码的技术落后了,或者是敲代码变慢了,最大的影响是你会逐渐丧失对编程的敬畏,忘记作为程序员的感受,特别是编码过程中的那些痛苦所在。你会有一些不切实际的幻想,做出一些不切实际的设计,这才是最大的问题。
大家都知道的 Java 之父 James Gosling,他在 Amazon 的职位是 Distinguished Engineer,level 相当于 SVP,而他依旧在坚持编码,每年的代码量是非常惊人的,常常会超过 10 万行。
总而言之,作为一个架构师,一旦你开始放弃编码,那你一定要非常小心,因为你可能正在走向一条不归路,一条为大家设计一些充满幻想但又较为虚无的设计的不归路。
6. 风险优先
**我们为什么要做架构设计?**在我看来,架构设计最主要的功能就是转化、降低、避免整个开发过程中的风险。而架构师很大的一个职责就是在早期识别出系统可能存在的风险,并通过你的设计来转换它、去除它。
我们常说的原型方式,或者架构切片的快速迭代方式,其实也是从另一个角度在早期尽量去测试风险,去测试我们的架构能不能解决相关问题,尤其是那些非功能性需求实现的风险,这些风险往往没有功能性需求这么容易在初期被发现,但修正的代价通常要比修正功能性需求大非常多,甚至可能导致项目的失败。
比如敏捷开发,很多人认为敏捷开发就是更快地开发出一个产品,然后快速地 deliver 到市场上,其实这只是敏捷的一部分。另一部分很重要的是,如果一个项目要失败,也要快速地失败,绝对不要把风险放到最后,这也是一种敏捷。
这里再给大家推荐一本书《Just Enough Software Architecture(恰如其分的软件架构)》,这是最近非常流行的一本架构书籍,书中强调,架构设计的目的就是为了化解软件实现中的风险。如果你项目中所有的风险都可以通过未来重构来解决的话,那你根本就不需要进行架构设计,直接等着重构就可以了。这也是我非常赞同的观点,风险优先。
7. 从“问题”开始,而不是“技术”
作为技术人员,我们非常乐意学习一些新技术,并且学了之后,我们还会非常有热情去应用这个技术。我经常会有这样的感觉,感觉在某一时刻被某个技术上身,特别想去实践它,以至于忽略当前手上的问题用这个技术来解决是不是最合适的,不知道你有没有相同的感觉。
冷静的时候,其实我们每个人都知道,要从实际出发,从需求出发,从用户的问题出发,而不要从技术出发,但在实际工作中,我们却常常不自觉地忽略这一点。就像手里有了一把锤子,看到什么都是钉子。
当时团队里有一个工程师,他维护的是一个非常简单的服务,就是一个利用 MySQL 作为数据存储的简单服务。后来一个他对当时新出的 DynamoDB 产生了兴趣,并学习了相关知识。然后就发生下面的事:
- 使用 DynamoDB 替换了 MySQL,这是一个噩梦的开始。
- 很快发现 DynamoDB 并不能很好的支持事务特性,在当时只有一个性能极差的客户端类库来支持事务,而由于采用客户端方式,引入了大量的额外交互,导致性能差别达 7 倍之多,效率非常之低。
- 这时候,这个同学就改用了当时在 NoSQL 领域广泛流行的最终一致技术,采用了一个消息队列来实现,这样每一个数据存储对象的改变都会发布一个消息,如果关心这个改变的业务逻辑,就会订阅这个消息,然后改变其他相关的对象,从而实现最终一致。如果过程中出现错误,就会利用消息队列的重试机制。
- 接着发现 DynamoDB 无法提供 SQL 那样方便的查询机制,为了进行数据分析就采用 EMR/MapReduce Job 来完成。
大家可以看到实现一样的功能,但是复杂性大大增加,维护工作也由一个人变成了一个团队。如果让我总结一下这个故事的话,可以说是我们对技术的热情让事情变得复杂,是我们对技术的热情把生活搞得没有那么美好,也让自己的工作更加烦恼。
8. 过度繁忙使你落后
对于 IT 人而言,忙碌成了习惯,加班常挂在嘴边,“996”似乎也变成了公司高效的标志。
但有时候我们需要反思一下,有多久没有在业余时间看和技术相关的书了。我之前在公司也问过这个问题,百分之百的人回答我,下班后已经很晚了,回到家基本上没有时间再看书,刷一下手机,就可以直接睡觉了。
这是一个非常值得我们去思考的问题。作为一个技术人,如果你不更新你的知识,或者繁忙让你没有时间更新知识,那会有什么样的结果呢?
给大家分享一个有意思的现象,我遇到过不少程序员,有之前的同事,也有自己的朋友,他们换了一份工作,一开始进入那家公司的时候跟我说,“这个公司我不是特别看好它,我了解一下它的技术,就准备换家公司。”过了两三年我再问他,“你怎么还在这,还没跳走。”结果他回答我,“我看现在的招聘形势不大好,不太好动。”
干了几年倒对公司越来越“忠诚”了。实际情况是,在一个公司没日没夜地干了几年,没有留一点学习时间给自己,忙碌的工作导致他没有时间更新知识,再想回到市场上的时候,却发现自己已经落伍了,连跳槽的能力和勇气都失去了。
在这个高速发展的时代,如果因为过度忙碌,导致你没有时间学习和更新自己的知识,那必然会让你落后。
即使你不跳槽,呆在同一家公司里,公司的业务不断发展,数据量会越来越大,用户需求会越来越刁钻,你要面对的问题和场景也会越来越复杂,如果你长期不更新知识,掌握的技能没有发生变化,你会觉得越来越难以应付,最终只能通过不断地加班来应对。
另外还有一种可能是,你不更新知识,不深入思考,那么很大概率,你所创造的技术和业务丧失了领先性,没有领先优势,只能被动紧紧跟随竞争对手,而紧紧跟随就意味着你只能加班。试想一下,你要是都领先同行业五年了,还会在乎通过加班来早一个月发布吗?
这其实是一个恶性循环,你花越多的时间去忙碌,就越没有时间去学习,去提高自己的工作技能,就只能靠加班来追赶,结果就更忙碌,更没有时间学习,最终成为一个井底之蛙,陷在恶性循环里无法挣脱。
我是一个健身爱好者,练过健身的朋友都知道,光靠锻炼是不行的,营养的补充和锻炼同样重要,你得专门吃一些蛋白粉、补剂之类的。而且越到后面,营养的重要性就越高,至少能跟锻炼达到 50:50 的比重。
个人技术成长其实也是一样的,锻炼就好像实践,营养就好像学习。人们常说 practice makes you perfect,但光 practice 是不行的,还需要坚持学习。
我们在一个领域工作了一段时间,比方说三五年之后,会对这个领域的业务越来越熟悉,解决问题越来越顺手,但相应的,能学到的知识和技能也就会越来越少。有些人会说这是进入了舒适区,要逃离舒适区,换一个领域,我倒觉得不必如此。本质上来说,换一个领域其实是促进你进一步学习一些新的知识,你在原来的领域也可以这么做。你可以有意识地摆脱那种麻木,挤出时间来重新学习,然后即使做的是相同的事情,也可以用不同的方式更好更高效地完成它。你会发现,即使在同一个领域,你也完全可以做和别人不一样的事情。
所以,每个技术人员都要保证充足的学习时间,否则很容易成为井底之蛙,从而陷入前面提到的低效循环。
最后用一句话来跟大家共勉,不忘初心,坚持匠心,谢谢大家。
部分引用自:十年架构感悟