软件行业待了这么多年,抽空 总结一下,毕竟好记性不如烂笔头。另外一个,想的明白,说的清楚,才能写的准确,也是检验一下知识,同时也是系统化一下。
软件的定义
回归初心,看看软件的定义。第一个印象是从学校来的:
软件 = 程序 + 文档
程序 = 数据结构 + 算法
这个定义太朴素,太简单化。 只是从轮廓外面描述软件的特点, 软件是由可以运行的程序,以及客户可以看懂的如何操作的文档组成。 但是也抓住一个特征,软件是给人用的。
再从wiki上面看看软件是如何定义的,
Computer software, or simply software, is a part of a computer system that consists of data or computer instructions, in contrast to the physical hardware from which the system is built.
这个定义软件是计算机系统的一部分,是数据和计算机指令组成和计算机硬件不一样。 这个定义也是从外部看的,和上面的定义没有什么不一样。
对于自己的理解,软件对外而言,就是一个工具,通过解决特定问题给用户提供价值。其实也是一个商品。 对内而言,软件本身就是思维固话,由概念和逻辑组成。所以就有下面这个定义。
软件, 是一个工具,商品或者服务,通过解决特定问题给用户提供价值,其本身是思维的固化, 主要包含概念和逻辑组成。
这个概念包含两件事,软件是给客户提供服务的,软件是否成功取决于是否解决真正问题,满足用户的期望。 不管是开源软件,还是商业软件,这个是很关键。在软件行业里面,简单来说,Do right thing。这个在软件开发中,就是需求分析,找对正确的问题。 看到太多失败的案例,技术即使很好,但是客户不认可,只能付出东流。
另外软件是一个服务或商品,一定需要考虑软件本身的成本。所以在软件开发中,投入产出比(ROI),一定一个需要持续关注的点。
另外一个就是软件本身是思维活动的固化,是对现实世界的认识在计算机的映射。通过将现实问题理解抽象,在计算机里面变成概念和 逻辑。设计和编程就是将思维活动固化的过程,也就是软件的实现。 简单来说,Do thing right。
从万米高空俯视,讨论软件开发就是两个视角,一个是关注领域问题和价值,另外一个关注实现。
Do Right Things
Do Things Right
软件的特点
随着计算机的普及,软件已经无所不在,已经软件吞噬世界。那我们来看看软件有哪些特点:
- 软件是逻辑产品,是思维的固化
- 复杂度是软件最大的敌人
- 软件质量,贯穿一软件整个生命周期
- 软件产品是持续演化
- 软件开发中的协作,是软件的关键
软件团队协作是软件开发中一个重要特点。软件功能越来越复杂,不可能有几个人几杆枪就做出来。一个复杂软件,需要不同角色不同技能,持续投入才可能开发出一个产品。期间不同人之间必然需要沟通协作,这个也是软件开发的常态,也是软件复杂度的一个重要来源。所以也单独列出来作为软件构建过程中的一个特点。
1. 软件是逻辑产品,是思维的固化
-
软件是大脑思维活动的一个固化。实现软件的过程,就是将思维以概念和逻辑的形式固化下来。思维活动的一些特点,比如无法衡量,抽象,因人而异这些特点都被软件继承下来。同时,一个概念的变化,对于其他概念或认识有指数般的影响,如同蝴蝶效应一般。
-
思维无法衡量,导致传统的工作量的衡量-可以基于计件计时来衡量,但完全没有办法对应到软件开发里面吧。 你很难说一天写100行代码的人比写一行代码的人的产出高。这个时候以结果导向更好一些。如果将it工作如同传统工作那样计时或者计代码量,那是一个悲催。
-每个人的思维方式都不一样。每个人因为自己的知识经验阅历利益都不一样,对事物的认识也不可能尽相同。即使是同样的人,不同时间段,用的思维的方式也会发生变化。
不同人的思维活动不一样。不同人对于同一个问题有不同的看法和解。传统的生产可以细化到细节,按部就班新的那样子,所有人的认识都统一,一刀切的方式,软件完全与之不同。不同的认识抽象,或许都对,只是侧重点不一样而已。
-软件是抽象的。软件的思维是基于概念和推理,而不是基于事物的表象(外观,声音,图片等)。不懂软件的人,形容软件是“看不见摸不着而且能量还不小”,很难理解软件是如何工作的。所以在软件的整个生命周期,一直都有可视化的需求。 USM中用户和需求的全景图,项目管理的甘特图和燃烧图(burndown chart),数据处理的流程图, demo中的原型图,架构中的5大视图,代码的类图和时序图,jenkins 上的印象深刻的blue-ocean以及曾经起名的“乐高脑图”。可视化的需求可以促进软件沟通理解,贯穿于软件整个生命周期里。
-
软件的发布成本接近于0。曾经发布为安装包,以光盘的形式发布,到网盘直接下载, 以及到现在的按需下载,甚至现在云上面拿来就用无需下载,以及现在持续发布。 这个过程将软件逻辑产品的属性发挥到极致。
-
软件只有逻辑折旧,没有物理折旧。逻辑不会如同物理客观事物磨损折旧,但是会随着时间变化更新,环境变化法规更新,认识的提升,都会对软件产生变化。每一次变化,软件不能及时更新升级,那么就是一次折旧。随着变化的积累,软件价值较少,最后沦为无用之物。这个过程就是逻辑折旧,后面软件演化会涉及到。
-
软件需要一个运行环境。 软件是需要一个运行环境,最常见的就是计算机。物理的东西都会有折旧或者损坏,比如硬盘读写寿命,网络的不稳定,以及本身可能会烧掉。如此等等潜在的折旧会导致的异常失败,这些和软件本身的逻辑问题交织在一起,会大大增加软件的复杂度。软件开发一直追求将开发环境隔离,减少环境问题。从mental machine的管理,到VM,现在Docker ,以及到 对待环境由宠物到牲口的而变化(pets vs cattle),以及Infrastructure as a code理念 , 都是对于干净运行时环境的持续追求。
2. 复杂度是软件最大敌人。
下图感受一下复杂度。
image.png一个普通大小的软件里面概念数量种类状态联系远远要超过上图。那么复杂度有哪些影响?
Complexity is the enemy of flexibility. It entangles us in unintended consequences. It blocks our attempts to change. It hides potential defects, making it impossible to be sure our systems will function correctly. Performance, transparency, security — all these highly desirable attributes leak away in the face of increasing complexity.
人月神话-焦油坑-任何动物爬进去后再也出不来.png复杂度是灵活性的敌人。它让我们陷入不可预期的结果( 想想regression). 它是阻碍我们去尝试修改,拒绝修改和害怕修改(想想遗留代码)。 它隐藏潜在的bug, 使得不可能保证软件功能正确(想想测试用例无限的)。 性能,透明性,安全--所有这些我们期望的属性,随和复杂度的增加,这些变得遥不可待。 在<<人月神话>>里面讲这种状况描述为,焦油坑。 陷入里面,挣扎折磨,是一种煎熬。
复杂度都是哪里来的?
本质复杂度和偶然复杂度
现实世界本身就是复杂的。这个是本质复杂度。 事物的种类多, 数量大,状态多,关系复杂,之间会有交互,数据信息互换。 现实已经很复杂,理解起来已经有挑战,但是对于理解这些系统的人,知识视角 的不同,对于信息理解的偏差,以及沟通时候造成的误解更进一步增加挑战。现实世界是变化的,软件也需要更新的,这些概念和逻辑发生变化,同时需要参与软件开发的人的大脑里面的认知模型也要变化。
技术实现也是复杂的。把对现实世界的理解映射到计算机数字世界,本身也是复杂的。 这个过程中技术选择,表达抽象思维的过程,其中还需要处理技术本身问题,如空指针,内存释放不正确,堆栈异常,并行竞争,硬件环境也会有bug和出错等等,问题进一步复杂了。
随着时间的推移,记忆的丢失和人员的变化,使得复杂度进一步提升。随着时间流逝,组织团队的人员流动,一些信息丢失了。只留下仅有的可以工作的代码和没有及时更新的文档,这个时候去添加新功能修bug,需要先了解代码反推之前的信息和决策,这个是一个逆向工程,难度比刚开始的问题难度又有了一个数量级的提升。
实际的情况可能更糟糕,语言文化思维方式的不同,组织文化沟通个人技能的差异都会使得问题更加糟糕。
应对复杂度
降低复杂度和提高处理复杂度的能力两个方面入手。
降低复杂度,抽象,分解,信息隐藏是常用手段;“高内聚低耦合”, 独立演化,软件可测性的追求;代码的本身复杂度,代码结构,包类方法测试命名方法,clean code;这些降低复杂度。 强大的工具IDE,标准化,复用,环境标准化,自动化,快速反馈工程实践...... 软件生命周期的各个阶段,每个阶段的方方面面都来提高控制复杂度的能力。复杂度控制住,那么随之而来是提高软件开发效率,和软件质量。这一部分太多理论和实践。后续继续讨论。
复杂度的列表 +1表示提高控制复杂度能力或者降低复杂度;-1反之
实践 | 复杂度增加 或减少 |
---|---|
需求是否实例化 | + 1 |
软件架构图是否清晰 | +1 |
团队对于软件架构是否理解 | + 1 |
cvs | + 1 |
自动化build 工具 | + 1 |
项目中第三方依赖管理 | + 1 |
开发环境是不是一步搭建 | + 1 |
本地是否可以调试 | + 1 |
是否有单元测试 | + 1 |
代码是否经常重构 | + 1 |
是否追求clean code | + 1 |
是否有CI | + 1 |
是否有CD | + 1 |
对于环境态度(pets vs cattle) | + 1 |
是否docker来作为发布的形式 | + 1 |
项目中开发语言没多一个 | - 1 |
直接依赖库多一个 | -1 |
软件架构中多一个元素 | -1 |
代码量每增加10000行 | - 1 |
团队是否review | +1 |
团队是否 retrospective | + 1 |
团队是否regular learning or sharing | + 1 |
......
欢迎大家补充
没有根除软件复杂的银弹
现实问题是复杂的,当然技术本身实现也会带来复杂度,协作开发也会带来额外的复杂度。即使有再好的的语言,库,框架,架构,这部分其他两部分复杂度也不会降低。如同人月神话,在软件整个世界里面没有银弹。
3 软件质量,贯穿一软件整个生命周期
软件质量重要性无需多说。一次一次重大事事故都是一次又一次的提醒我们。最常谈到的软件质量问题就是bug,是软件的行为达不到实际的期望。
Software will change the world, but bug will destroy it.
但是软件这质量有两个视角,一部分是用户可以感知到底外部质量,比如正确性,健壮性,可靠性,性能等外在属性; 另一个视角是开发人员感知的内部质量,比如项目一键编译,模块的划分接口,代码的结构,类方法的命名大小,这强调代码可读性, 可维护性,所谓 clean code。其实就是软件内部和外部质量。 外部质量差,功能或性能达不到客户的预期。内部质量差,开发者体验差;还有-另外一个说法,技术债。从长远来看,内部质量肯定会影响到外部质量。
软件质量是贯穿到软件的整个生命周期中,从需求,设计,实现,测试,部署,环境,运维和修复。
bug的传递.png软件bug的成本,其实和变化一样,发现的越晚,修复成本越高。在最终交付给客户的产品或者服务时发现的bug, 比引入阶段发现的bug,那影响和修复成本不知道要高多少倍。想想汽车的召回,与如果这些问题在设计时候发现,其成本何止几十倍。
不同阶段修复的成.png每一个阶段,但是引入的bug的数量也是不一样的,这个和我们直觉一样。
不同阶段bug的数量.png
每一个阶段都会引入潜在的bug,不同阶段引入bug的数量也是差异很大,其成越靠后越高。那么提高软件的质量需要从全局考虑,每一个阶段都有不同的方法。
- 需求沟通的bug
这个阶段需要团队对的问题和价值本身理解,并考虑不同需求的优先级。这个是软件的启点和基础。
DesignThinking,是一个理解问题确认需求的框架,对于一个全新的产品是一个不错的选择。
基于USM(user story mapping) 和 BDD开发结合起来是一个不错的选择。感觉这个是 sbe( specific by example)的理念的落地。
这个阶段不但要考虑要解决的问题是不是一个真正的问题,不但需要考虑对于客户或者用户,同样需要考虑企业的战略。毕竟商业软件是公司来赞助的。
-
设计中的bug
设计是承上启下,向上满足需求功能和非功能的属性。向下需要指导实现。 同时设计时要考虑软件的测试性。一个可测试的软件,说明其依赖清楚,容易被隔离,容易创建,被调用和验证。那么一定就是一个高内聚低耦合的,那是一直追寻的属性。如今流行的微服务架构,对于每一个服务也是比之前的单体应用可测性要好一些。 -
开发中, 尽可能避免bug
这个阶段bug引入最多,那么需要在开发阶段尽可能的减少避免引入bug。这个阶段是开发人员最关注的的,有很多工程方法,有流程。比如
两个人 pair programming( 基于团队的mobprogramming), code review, code inspect, static scan ,
Unit test, TDD,以及将一切都自动化集成起来的CI和CD pipeline,从而将bug扼杀在开发的阶段。这是性价比最高的。当然不要忘记人的因素。流程工具是有用,但是最有效的当然是优秀的人和团队。不是说上面的方法流程工具没有作用,而是说软件最终的质量还是要靠人。否则所有的流程工具都会留于形式。 比如看到过为了提高单元测试的覆盖率,居然还有没有断言的测试这么个解。
-
测试中, 尽可能发现所有的bug。
常规的测试金字塔,让一切问题尽可能靠下面测试自动化,让一切尽早发现反馈。好的测试需要速快,稳定,帮助位定问题。
但是也不能忘记探索性测试(ET)。 自动化的测试路径是固定的,ET很好的弥补这点。 软件有一个比较好的质量基础,那么基于探索测试,发现软件里面不寻常的路径,组合。这是自动化不能代替的或者成本太高承担不起来的。
各种测试都有,其相互重叠肯定有的。勿忘初心我们是提高软件的质量,同样也要考虑软件是一件商品有成本的。基于每种测试的特点,考虑其性价比(ROI)。 -
运维中, 尽可能早的发现警报。
传统软件发布是一个安装包,最终软件运行在用户的环境上。对于问题,查看log或本地环境尽量重现,抑或远程支持,这个都是成本很高的。尤其是哪些不能重现的问题。现在如果服务上云,软件运行的环境可以自己掌控,对于问题本身直接可以重现。但是影响也是严重的。 这个就需要运维团队,尽早发现问题,减少影响。 监控有三个方面,运行环境的监控,应用程序的黑盒监控(cpu,memory,request response time,jvm heap size等 ),以及log的信息。 尤其是log里面的warning 和error一定要去留意,尤其是error。如果不留意问题容易扩大化,所以尽早发现,早日采取行动,扼杀在摇篮中。 -
如果发现bug,尽可能快的修复。
修复bug,这个是开发人员的基本功。但是快速修复,这个是一个挑战。常见的四部修复法(本地重现,定位,修复,回归),但是对于云上面应用程序,或许不能直接用debugger。 那么只能借助log查找原因。这个时候log的重要性无需置疑。
考虑到高可用性,如果真的有问题一定要快速修复。这个一个大的挑战。
另外两个办法就是对比(TDD, 折半查找,git blame)以及还原法(这个不常用,但是有时候会有奇效)。 -
对于bug,做RCA(Root -Cause -Analysis)
这个是一个回顾和反省的学习的过程,而不是寻找背锅侠。频率不需要高,但是很有效果。这个也是系统学习的一个过程。 从晓梅那学到(10w + 5 hat)的框架是一个不错RCA框架。
传统的软件质量是由测试部门负责,希望在发布前将所有的,尽可能多的bug都发现。传统软件,或者一些发布之后难以修改或者造成的损失很严重的(比如航空航天以及嵌入式的软件),依旧前调在软件前尽可能多的发现大多数bug。 如果软件本身可以持续发布,同时有问题很快发现和修复,同时对于用户体验没有那么差,那么速度和质量可以折中,那么对于这种大规模的发布前去测试,完全是没有必要的。 这个时候去QA化,不是说软件不需要测试,而是将软件质量从更高的层面来团队负责。
4 - 软件需要持续演化
软件是一个逻辑产品,而逻辑不会随着时间物理磨损而折旧, 软件也不会物理折旧, 但是软件会随着问题本身的变化而需要改进,这是软件逻辑折旧。 如果软件需要延延长寿,需要软件本身随着时间可以演化,支持新的变化。
同样功能也会演化,新的需求,新的认识,新的法规,新的安全,等等入错之类都会需要适应新的变化。 软件要解决的问题会随着外部世界的变化而变化。 比如说之前是桌面版,变成C/S版, 又变成网页版,紧接着又是手机版,可以看到语音版,VR或AR 版, 以后或许人机对话。 用户消费方式一直放生变化。或者变化是杀死软件的最后一根稻草。
对于软件变化是必然的,所以对以产品的维护是肯定的。从整个生命周期开开,软件的开发成本,软件的运营成本,以及软件维护成本,维护成本不会低的。软件里面的技术债,就是对于软件内部质量而长时间付出的成本。考虑到一个大的项目,如果其成本随着时间越来越高,维护成本与获取利润慢慢持平。长久以往,这个项目没有大的改动,会被淘汰。
5. - 软件开发中的协作,是软件的关键
软件越来越复杂,分工是协作是必然的。 上几百人几十个团队做一个产品,如何协调,分工,沟通都是挑战。尤其是软件行业里面,这样一个知识密集行业。Scale scrum team 常用的两个模型: LeSS 和SAFe.
Conway‘s Law,软件的架构和团队的架构是相互反映。一个产品分成模块,由不同的team来完成; 模块之间要通讯,之间定义接口交换数据,而这些接口是由团队之间沟通和定义。 那么软件最终的架构和团队的架构存在某种映射关系。 在那本DDD书籍里面,团队之间的关系可以微妙影响到软件的架构。比如一个团队对于另外一个团队的代码不放心,会加一个防层:)
团队协作,实际上对于个人的表达展现演讲能力由很大要求,当然也是机会。
完美软件不存在的,但是这个是我们追求的。那么如何将发布的软件做到团队最高水平,需要管理技能,更需要好的文化好优秀的人一块努力。团队一块努力,学习共同成长。之前看到mobprogingng,或许是一个理想的方式。
技术和商业的关系
软件开发有两件事,解决正确的问题和实现正确的问题, 其实就是商业和技术之间的关系。
商业上的决定强势于技术团队,这个往往是现实的常态。如何商业的成功,会更加给技术团队以压力。技术有一定的弹性,如果压力过于大,技术上会有某种折中。但是往往这种折中长期来看会有副作用(又是技术债),影响代码的可读性和可维护性。 如果后面不以纠正和补偿,那么技术会报复商业上的成功。 比如代码越来越难维护,越来越难于理解,质量越来越差,最终使得产品不可用或者甚至崩溃, 商业上的成功不可持续。 当然以技术主道的产品,不去考虑市场那也是不可持续的,比如Docker。
对于一个技术人员而言,一个成功的产品,必须具备两个因素,商业上的成功和技术上的创新。比如google的搜索引擎和广告发布系统。否则都不可持续。
网友评论