你有没有想过,你天天用的手机APP,电脑上装的各种程序是怎么开发出来的?
为啥我手机上/电脑上的这个应用时不时就出现个问题?
还有, 我们公司买了一款软件,老板觉得样式/按钮/功能需要做一些调整,正好隔壁邻居老王会弄软件,让他帮忙改一下,但是过了好几天都没动静,看起来也不难啊,为啥这么多天也弄不好?

邻居老张是个程序员,天天加班,听他说公司老板昨天改了需求软件要重新设计,今天忙了一天准备下班,老板又说要改需求,还说加了一倍的人手都延期了。为啥需求 要改?我们建筑院设计好了之后就不开始动手干了吗,怎么还会推到重来?加了那么多人会啥还会延期,都在加酱油吗?

软件是一个虚拟的东西,就像空气一样看不见也摸不着,但是他还非常重要,无论在哪都会有他的身影,如果你有上述的疑问,那么请跟随我一起来探究这些原因。

软件开发需要多人协作
首先,我们来看看下面这张图

这是一张劳动人们割麦子的图,那么,他和软件有什么关系?
呵呵,没任何关系,但是请你考虑一个问题,上图中的农民朋友们在割麦子的时候,需要相互讨论怎么割的方式/技巧吗,需要相互协作割麦子吗?
不管你有没有割过麦子,答案是显而易见的,不需要,只需要在开始工作之前,划分好各自需要负责的区域,然后蒙头干就行了,等张三,李四,王五都干完了,大家都跑到一个角落里坐下汇总下各自的成果,这割麦子,就算是完工啦。
下面这张是 米开朗基罗的代表作《最后的审判》,现保存于梵蒂冈的西斯廷教堂

这张壁画就是《最后的审判》,长度13.7米,宽12.2米。米开朗基罗用了7年时间才完成这幅巨作。
现在设想一个问题,假如请一批艺术家一起来完成这幅作品,比如请10个人,能用0.7年,或者说不到1年的时间完成吗?
首先要的知道的是,米开朗基罗是当时最杰出的的艺术家,想请到跟他同等级的艺术家估计不太可能,所以请来的艺术家水平估计也就是米开朗基罗功力一半甚至不到。米开朗基罗当时加班加点的干了7年,一般的艺术家估计得干个20年。那如果是10个人,是不是2年就能搞定了呢?
一群人完成一幅画和一群人在田里割麦子最重要的区别是前者需要协作,后后者不需要。
对于一幅壁画,需要确保大家的风格统一,整体应该是一致的,你不能张三一个风格,李四一个曲调,那这个壁画就乱套了。
所以,对于一群艺术家完成壁画一个很重要的问题,就是沟通

如果还是感受不出来这种情况,那看看下面这幅图

软件开发就像多人协作完成一幅壁画,拓展运动中的团队配合,很多的时候会出现彼此需要协调,需要彼此依赖对方的成果。
这就是软件开发和割麦子的不同,任务无法简单的分割,所以10个艺术家想在2年的时间就完成这个壁画不太可能,除非他们有着极高的默契度。
好吧,既然配合会出乱子,那一个人搞定行不行呢?
也行,但是现代的软件规模就像上面说的那副壁画一样,规模太大了,即便像米开朗基罗那样的艺术家都需要加班加点的搞7年才能完成,那一般的艺术家干了十几二十年后,估计公司早就倒闭了,老板早就跑路了。

软件的功能和规模会不断变化
下面这张是4500多年前胡佛金字塔的外观,这是目前世界上现存最高的金字塔,位置在开罗。

假设有人说,要把这个金字塔的尖顶改成圆顶的,你会怎么想,一定会认为这人是疯了吧。
这就是软件和建筑的不同,建筑在规划好之后,最终的建筑造型和设计图纸上的一样的,但软件完全不同,最初设计的第1版软件发布之后,就不会变化,等到第10版发布后,再对比第1版,你会发现可能有天壤之别。
软件会想人类进化那样不断的迭代,若干年之后你再对比最初的软件版本,就好像现代人和原始人的差别

构建金字塔的材料是硬邦邦的石头他是不可变的,所以金字塔造完了整个过程也就结束了,4500年之后都不会变。
而构建软件的最基本材料就像水和空气一样,会不断变化和流动,也因为这个原因,在设计软件的时候就需要考虑到未来可能出现的变化。
想象你用过的软件,比如QQ,打开网页去逛淘宝,京东(淘宝和京东后面是一堆的软件支撑的),他们最初的样子,跟现在的样子是不是差别很大。
现在的QQ和第一版的QQ对比,功能明显是多了太多太多。软件的变化来自于使用者,也就是用户。人类的进化来自于外界环境。
使用者在使用软件的过程中提出了各种意见和建议,于是软件的设计者们根据这些建议就不断修改软件,十多年之后QQ的界面就变成了如今的样子。

没有文档很难读懂 很多人不知道,没有文档/注释的软件很难读的懂,读懂一行软件的代码并不像小说那么轻松,它有点像读一首诗词,想读懂他就必须仔细揣摩作者的意思,也就是一个字一个字的啃下每个代码,仔细分析其含义,才能明白这个东西是干啥的。 比如下面这段,来自北宋诗人苏轼的一首诗

十年生死两茫茫,不思量,自难忘。千里孤坟,无处话凄凉。纵使相逢应不识,尘满面,鬓如霜。
夜来幽梦忽还乡,小轩窗,正梳妆。相顾无言,惟有泪千行。料得年年肠断处,明月夜,短松冈

第一眼看上去,是不是要费点劲,揣摩一下才能理解呢,软件中的代码也是这样的
所以当你捧着一本厚厚的古文书籍,如果旁边没人指点,网上也没资料,想要快速搞懂这个书是讲啥的,可没那么容易

这时候文档,注释的作用就体现出来了。文档就像这本书的摘要指南,把书中的每个章节都详细的介绍了,整个的框架和脉络都理清了。代码中的注释相当于一个特别难懂的文言文,加了白话版本的说明,于是你就可以立刻秒懂是什么意思了。
现在有这么一个要求,修改书中的某个章节内容,将原先描写雨中漫步的情节,改为欣赏日出。时间和动作都有改动,变化不小。不过借助文档你可以很快能定位到具体是哪个章节,借助注释,你就能很快了解原先的话是什么意思,很快就完成了这个要求 。

如果没有文档和注释,虽然也能完成这个要求,但时间肯定要多好几倍。
你可能会说,难道没有那种很厉害的人吗,可以不借助文档注释什么的,轻松读懂代码。 这种人是有,但毕竟是少数,计算机执行的逻辑和人类大脑处理的方式不太一样,确实有一些天才的科学家可以像读散文一样阅读计算机的代码,但是对于大多数普通人来说还是很困难的。
下面这个是经典的八皇后问题的解法,国际象棋中的皇后,可以横向,纵向,斜向攻击这个方向的棋子,八皇后就是要在棋盘上按照特定的位置放置8个皇后,确保他们不会相互攻击

八皇后的程序实现如下,这里用到了计算机很擅长但并不适合人类理解的一种编程方式–递归,大家可以感受下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class Solution(object):
    def solveNQueens(self, n):
        """
        :type n: int
        :rtype: List[List[str]]
        """
        result = []
        def DFS(columns,xy_diff,xy_sum):
            if(p == len(columns)):
                result.append(columns)
                return
            for q in range(len(columns)):
                if(q not in columns) and (p-q not in xy_diff) and (p+q not in xy_sum):
                    DFS(columns+[q], xy_diff+[p-q], xy_sum+[p+q])
        DFS([],[],[])
        return [ ["."*i + "Q" + "."*(n-i-1) for i in col] for col in result]

既然写程序这么困难,又要求很快速完成需求,这就迫使人们不得不将工业化带到软件行业中。
有了工业化的标准,大家做事情就统一了,但又带来了一个问题。
前面我们用《最后的审判》这个壁画举了列子,其隐含说明开个一个软件如同艺术创作一般,现在却要求流水线工业化作业。
在一个软件团队中,要充分考虑到最大化的利用人,今天张三生病了,明天李四请假了,后来王五干脆辞职出去旅行了,为了保证后继有人,团队必须继续招人,而招来的人想快速上手,必须按部就班的来,也就是各种标准,这样新来的人才能快速融入。
于是软件开发就变成了 流水线作业加上艺术创造的混合体,想象前面说的那10位艺术家,给他们限制了各种条条框框之后,按照工业标准流程去完成壁画。
工业化流水线作业的工作这个世界上很多,艺术创造这个世界上也有不少,但把他们两者结合起来,这世上恐怕真很少见。

需要大量测试 软件开发完了是不是就能用了? 呵呵,当然不是。这后面还有大量的测试工作需要做。
假设小明设计了一个公交车刷卡系统的软件,现在正是投入使用

过了一段时间,市领导决定对10岁以下小孩乘车免费。于是小明修改了程序(软件是可以不断修改的嘛),再安装到公交车上继续使用。
但很快市民们就发现问题,很多小孩无法乘车了,这些孩子都是10岁整。这?小明很快查到了原因,原来是判断有误,应该是: 年纪 小于等于 10岁,但小明写成了 小于 10岁,于是一大批小孩就无法乘车了。
小明修改了代码,安装到公交车上继续运行。
可是,好景不长,又有新的问题了,这回是国外的不到10岁的小孩不能乘车。还有一个小孩虽然只有8岁但身高1.9米 ,超过了儿童1.2米标准也不让乘车。接着一个猴子拿着公交卡说自己明明不到10岁却不能乘车。
后面两个例子虽然比较极端,尤其是最后一个过于滑稽,但真实的软件世界中确实是有很多极端情况出现,这些软件运行了很多年,时间久了就会碰到各种问题,还有一些极端的情况,另外也有一些人为的恶意攻击。
但不管怎么样,软件都需要处理这些异常的行为,否则软件就会像下面这样了

不好意思,放错图了,应该是变成下面这样

我们必须经过大规模严格的测试,才能保证软件的健壮性
不过随着软件的规模越来越大,需要测试的地方就越来越多
因为软件是多人协同开发完成的,张三可能会依赖李四的功能,而李四又要依赖王五的
比如要开发一个数据线接口,李四跟张三说,我做的那个数据线接口啊,就一点点大啊,很窄的,你不要做的太大了,免得插不进去。
我们在现实中也经常会听别人说,往前走一点,就走一点就到了,这话是没问题,可是用在开发软件中就不行了,张三必须废除准确的知道李四做的接口有多大才行,可不能是一点点,李四理解的一点点到张三这里可能就是一点点点了,所以必须严格统一标准。
长度0.3厘米,宽度0.2厘米,这样就没问题了。

当然,光是做到上面那样还不够,还需要大量的测试。 假设开发完一个功能需要1个小时,那么补充文档,注释,再详细测试完保证这个功能没问题可能需要花费3小时。
如果要把一群人开发的小功能整合到一起变成一个大功能,编写文档/注释,再充分的整合测试,那么就需要9个小时。
这一前一后就是9倍的时间,所以一个简简单单的功能完成之后,还需要大量的辅助工作才能保证最后交付的软件是可靠的。
最后说说什么样的软件是好软件呢?
请看下面这幅图,这是坐落于伊斯坦布尔的圣索菲亚教堂

这座教堂建造于532年,仅用5年时间便完工。作为拜占庭帝国最辉煌时期的象征,在余后1500年的时间里经历了无数次战争的洗礼,更是加冕了数位拜占庭皇帝。 好的软件要足够健壮任他风吹雨打依然屹立不倒,好的软件要像一件艺术品一样让人流连忘返。

总结
本文,我们探讨了软件开发中碰到的一些难点
软件开发就如同艺术创作,但因为现代社会快速发展的需求,导致开发时间必须大幅度提高,于是软件行业就变成了一个工业化的行业。
又因为读懂代码并不是一件容易的事,相关的说明文档,规范流程一个都不能少。软件中的逻辑错了一点可能会导致千差万别,轻则结果不对重则完全不能用。
开发了软件其实没有你想象的那么容易。

最后,用一个段子来结尾吧,据说有一家公司开发了一款软件,可惜非常难用,也很难维护,里面的设计更是相当糟糕,维护这款软件的人走了一批又一批。
有一天来了以为新员工,经过测试发现了软件的一个问题,然后想修改,突然看到程序中有一个注释,上面写道:这里有坑,千万别改动!
新员工想,这错误不是很明显吗,改动一下会怎么了呢?我改改然后运行一下试试。 3年后,这款软件依旧没能再次运行起来。