软件的复杂性是一个基本特征,而不是偶然如此。从根本上来说,我们可以掌握这种复杂性,但不能消除这种复杂性。
- 对公司来讲,做业务要有利润。利润=收入-成本。在软件开发中,需求解决【提升收入】的问题,设计解决【降低成本】的问题。公式可以改为【利润=需求-设计】
- 研发团队能做的是设计好的方案,【降低成本】。但好的方案不是唾手可得,有难度。为什么有难度?因为软件开发是复杂的。为什么是复杂的,复杂在哪儿?如何掌控?
复杂性来源
- 问题域的复杂性:包括需求及质量要求。需求已经比较复杂了,还要在此基础上考虑质量要求(性能、可用性、安全、正确性、扩展性等)。再加上出方案时“随心所欲”,时间越久问题越复杂。
- 可变性: 需求可以随意变动且不能通过模型预测,即使有一套方法能够自动生成代码,也是基于模型匹配的基础上。但真实世界的需求不可能和目标模型匹配,所以软件行业还是一个劳动密集型的产业。
- 一致性: 因为是劳动密集型产业,团队人数多,管理开发过程就有很大挑战,挑战在于维持设计的一致性和完整性。
- 不可见性: 产品是具体的,代码是抽象的。方案是否合理?代码行数是不是太多?代码质量怎么样?监控告警是否全面?效能如何 等,缺少可视化工具呈现,存在较多盲区。就像装修一样,外面看着挺好,墙背后的各种线、水电气等 都是盲区,诸多隐患没被发现。
如何控制复杂性
1.问题域的复杂性
1.1需求的复杂性
- 问题:
- 需求规则没人知道,只能靠口口相传。
- 分不清用例、步骤、规则,理解需求时揉在一起,不够清晰。
- 不知道如何拆分需求,做不到尽早交付价值。
- 没有想清楚交付什么价值,大部分情况下用例(价值)是不变的,变得是步骤和规则(实现价值的方式)
- 解决1:使用工具沉淀需求。目前互联网公司“流行”的做法是口口相传,沉淀远远不够。沉淀需求对于在技术层面做业务监控、可用性监控等有很大意义。
- 解决2:使用面向对象方法,从业务建模开始找到用例,画出用例图。用例图很好解答了 哪个组织/系统 对谁(涉众)提供了什么价值(用例)三个问题。想清楚价值,可以帮助判断方案能否达成价值以及是否有更好的方案。
1.2技术的复杂性
技术的复杂性在于既要完成需求,又要考虑质量要求,技术复杂性=需求复杂性X质量要求(性能、可用性、安全、扩展性、正确性等)
- 问题1:忽视质量要求。需求已经比较复杂了,设计满足需求的方案可能已经精疲力竭了,哪顾得上质量要求。
- 建议:给时间+模板+把关。
- 问题2:对问题域不够了解,设计的方案偏离真实世界,导致不断“重构”。美其名曰重构,可能是迭代不下去了,怎么保证重构后的不被重构呢?
- 建议1:不急于出设计,在设计之前先 分析 ,先调查研究问题。没有充分理解问题,解决方案很可能是错的。就像面试,没弄懂面试官的问题,回答大概率是错的,做对也是蒙的。
- 建议2:【设计】之前画一下【分析类图】,找出对象及之间的关系,也是可视化的过程,比在脑子里硬想要高效。设计方案以及评审时就不用死盯着内容很多且不精确的PRD了。
问题3:知识爆炸。业务规则及实现要看代码及口口相传。混淆的名词及代词影响正确性和效率。混淆的各种名词,沟通时使用各种代词,“他们” “我们”,涉及的系统多了就跟不上讲的人思路了
建议:增加分析流程,输出精准的、简洁的、可以理解的现实世界的模型。最重要的是分析类图,其次是分析序列图。
- 分析类图:关注数据,从现实世界捕获对系统重要的概念及关系。
- 分析序列图:关注协作,分配职责,完成用例。 讲流程时就不用说他们、我们了,把这个图丢出去就够了。
状态机图:关注时序,对象的生命周期及状态流转条件。
不要觉得这个没用或者很简单,画的过程中一定会有很多疑问:
- 该不该画这个类?这是类还是属性?是哪个类的属性?
- 该不该连线?多重性是什么?
- 是泛化还是枚举?是泛化还是关联?
- 反映了现实世界吗?
- 这个操作该由哪个类完成?
问题4:未能区分真正的复杂还是由“随心所欲”引入的复杂。
既然已经认识到复杂是无法避免的,就不要再引入“随心所欲”的复杂了。扪心自问,平时说的系统复杂,有多少是我们自己引入的?
追求设计出“简单”的方案。“简单”是指对用的人或者对以后简单,而不是思考过程简单。“简单”绝不是忽视复杂性,相反要付出巨大的努力,还大概率做不到。比如交互设计领域,iOS足够简单,四岁小孩都会用,为什么其他公司设计不出来更好的呢?
我们做技术方案也类似:
- 当解决方案很容易想到时,要警惕,是不是草率了,这次简单了,以后是不是麻烦了?
- 当解决方案很复杂时,不要洋洋得意,要反思,是不是有更好的没有找到?或者这样想:让一个更厉害的人来做,是不是能设计出更“简单”的方案。
- 做减法,加法导致东西越来越多,多就会带来复杂性。
把系统做复杂不是本事,做复杂谁都可以,四岁小孩儿随意画的画也能很复杂(图1)
不要图1的复杂,要图2的复杂:结果可以是复杂的,但复杂的结果可以由“简单”的方案演化出来。像大自然、语言、围棋等,由简单的元素就能演化出复杂的结果。
2.一致性
- 要专业。引入的“随心所欲”复杂性,本质是因为自身不专业,下面的例子一定不陌生:
- 相同变量名,不同含义
- 相同含义,不同变量名
- 单位不一致
- 中英文不一致
- 工程命名方式不一致
- 迷惑的变量名
- 工程命名方式不一致
- 变量名与业务语义不一致
看上去都是很小的点,多了会不会有千疮百孔的感觉?如果觉得这些case很小,系统中是否存在更大的不一致?比如注释与实现不一致,实现与设计不一致,设计与真实世界不一致(这可能是最大的不一致)。
- 不要重复:保持一致的有效方式是只有一个。但重复经常发生,导致不一致:
重复的类型 | 解释 | 例子 |
---|---|---|
强加的重复 | 环境似乎要求重复,别无选择 | 代码 vs 注释 vs 文档 周报 vs 周会 MySQL VS Redis VS ES |
无意的重复 | 没有意识到在重复 | 派生信息,存了出生年月又存年龄 类名已经表明对象了,方法名还要加上对象名:XXClass.createXX。 |
无耐心的重复 | 偷懒,因为这样可能更容易 | 重复代码 上面提到的不专业的例子 |
开发者之间的重复 | 同一团队(或不同团队)重复了同样的信息 | 重复的轮子 业务语义相同的常量各团队都定义了一份 |
- 建议:
- 避免破窗:发现一个改一个。就怕习以为常了,没意识到是债务。
- 用业务语言设计、写码,避免出现无业务语义的产物。如果在设计前,画了【分析类图】,可以避免很多问题。
- 第一种重复要慎重,二、三、四要避免。
3.可变性
- 没想到好方法。人是不靠谱的,尽量用工具替代人。是个长期过程,平时多留意可以沉淀出工具的地方。
4.不可见性
- 提升研发效率需要配套相关的可视化工具,如各种可视化监控,各种报表统计等。方便大家了解研发过程、研发结果的各种数据。
总结:
- 利润=需求-设计,设计就是成本。工作时想想是在增加成本还是【降低成本】?目标是做一个“简单”的方案,“简单”符合人性,也符合自然规律。
- 为了做出“简单”的方案,要充分理解问题。在【设计】之前先【分析】,输出【分析类图】【分析序列图】。【分析】是把思考放在前头,由【分析】驱动设计,不是问题驱动,也不是重构驱动。
- 复杂不能消除,只能被管理。避免引入“随心所欲”的复杂,“随心所欲”是不专业的表现。