代码结构的演进

过年了,各种公众号都在玩拜年,玩红包,甚至在玩喜羊羊,连程序君订阅的一些技术类的公号也不能免俗。作为大年三十还在苦逼上班的程序君,自然不会放过这个绝好的机会写点和技术沾边的文章来填补大家空虚的心灵 —— 因为我深深的知道,你们会边看春晚边想这个歌舞是不是冗余代码,那个小品是不是UT没做好,主持人虽然使用了多核,但使用的脚本一看就是php,性能不佳;来年应该让导演上erlang。。。

为了让你们那无时无刻不在运算的大脑继续保持很好的节奏感,这篇文章依旧会和技术有关;但又不会太techie,看一看,想一想,乐一乐就好。

今天瞎侃侃代码的发展史,走心走肾随你。

言归正传。

纵观整个软件工程的发展史,项目代码规模的不断增大导致了人们一直在寻求更好的代码组织方式,使其适应「笨拙」的人脑的理解能力。

最早的代码估计没有项目的概念,只是一个文件,几张A4纸就能将其表述清楚。这时的代码有最原始的控制结构(jmp,goto),整个程序揉在一起,被形象地称作意大利面条(spaghetti)。

意大利面条式的代码撑不到太大的规模,便超出了人脑所能理解的范畴。为了让代码更可读,从goto中衍生出了更好的控制逻辑:分支,循环(或者递归),以及用于管理目的的 [1] 函数,类 [2] 和模块。代码可以以更清晰,更可控地方式被撰写。

感谢文件系统的诞生,原本处在一个平面上的代码被人们以树状的结构进行管理。功能不相干的代码被放入不同的文件,继而放在不同的目录,于是库或者模块的概念产生了。有的程序员开始专门为其他程序员开发库或者模块。代码脱离了小农时代的自给自足,进化到了商品经济时代的社会化分工。

有了社会化分工,代码的规模开始急剧扩张。为了让规模化的代码编译过程更可控,诞生了make,scons,grandle,mix等工具;为了让代码的修改和追踪更可控,cvs,svn,git等工具又前来救驾。

有了分工,便有了依赖,依赖管理工具便就此诞生。pip,ivy,npm等在其各自的领域中,为开发人员减轻想想就头疼的依赖冲突。

软件工程越大,boilerplate代码就越多。各种framework对此专门优化,把boilerplate扼杀在摇篮里。DSL和metaprogramming让代码越来越专注于business logic,其它一切都「放着我来」。

从撰写的角度来讲,随着各种语言的lisp化,以及主流VM上lisp的亲戚 [3] 越来越成熟,代码的静态可维护性已经不成问题。然而,运行中的代码却依然没有太大的改观。大部分软件,尽管从静态的角度来看,模块化和关注点分离已经做到了足够好,代码与代码之间甚至在物理上都被树状的文件系统隔离,可当其编译运行起来成一个进程后,这种隔离消失了,所有的运行的代码又被统统揉在了一个平面中。在这个平面上,某个角落里产生的exception,有可能把整个进程搞挂。所以为了尽可能让某个局部的错误不至于影响全局,大家一致的做法是defensive coding —— 甭管谁的代码引起的问题,反正问题不能出在我这里,try catch也好,if error check也好,总之宁可错杀千人,不可漏掉一个。

defensive coding不能解决所有问题,那么,运行时有什么类似于文件系统的东西把不同逻辑的代码隔离开来,当defensive coding无法奏效,也可以把exception控制在可控的范围?

threading/multiprocessing可以勉强看作是一种手段,尽管它们的初衷是为了concurrency。不少软件利用multiprocessing,使用经典的supervisor/worker模式(如nginx),把更易出错的worker的exception隔离开来,让软件的robust大大提高。

可程序员们还在呼唤更好的解决之道:既然静态的代码可以用树状的层级结构来管理,为什么运行时的代码不能采用同样的方式呢?

大家的目光停留到了erlang,这个诞生于上世纪80年代,静静躺在不为人知的角落里的语言。它有一种奇怪的结构叫process(下面称actor,避免和众所周知的process混淆),还有一种奇怪的思想叫let it crash。

在erlang中,actor则相当于软件的细胞。若干个细胞结合起来,成为软件的组织;若干组织结合起来,成为软件的器官,然后再结合成整套软件。这种软件的组合模式看上去像是这样:

如果某个细胞损伤,那就让这个细胞死去,再克隆出一个新的细胞即可,这就是let it crash的思想。细胞如此,由细胞组成的组织,或者器官,也如此。erlang的actor发生异常,如果自己搞不定,就把自己杀掉,由其supervisor重新构建。

(restart one for one)

(restart one for all)

(restart rest for one)

生物体总有些关键的部位是不能损坏的(如心脏,大脑),一旦损坏,生物体也就完蛋了。这部分要保证足够健壮,不会出问题。在erlang里,这被称为error kernel。软件的撰写者要分辨出软件的哪部分是一定不能出问题的,一旦出问题,软件就得crash。除此之外,软件的任何部分(actor)出问题,无非就是这个部分重启的事情。

(error kernel)

于是,运行时的软件不再是一个各种代码揉在一起的平面,而是一棵层层隔离的树。

erlang开启的先河,被scala吸收了过去,构建在JVM和scala之上的akka将这思想传播到了更深远的地方(不是说akka优于erlang - akka还在拾erlang牙慧的路上 - 只是JVM过去二十年在企业的应用要远远广于beam),并且做得更彻底一些(erlang的actor可以选择是否supervise,akka所有actor都会被parent supervise)。

也许未来十年,这将成为软件的主要组织方式 [4]。因为,「永远在线」的互联网,越来越承受不起宕机 —— 哪怕仅仅是几分钟。未来,也许软件需要达到硬件一样的6sigma,尽管这在目前来讲简直是天方夜谭 [5]。

你觉得呢?


1. Don’t Repeat Yourself可以视作代码管理的一种手段

2. functional language没有类的概念,只有函数和模块

3. JVM上的scala, clojure,BEAM上的elixir

4. 我还没讲这种结构下concurrency,deployment的优势呢

5. 也不尽然,爱立信用erlang写的交换机软件达到了9sigma

原文发布于微信公众号 - 程序人生(programmer_life)

原文发表时间:2015-02-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏逸鹏说道

携程:关于反爬虫,看这一篇就够了

编者:本文来自携程酒店研发部研发经理崔广宇在第三期【携程技术微分享】上的分享,以下为整理的内容概要。墙裂建议点击视频回放(http://v.qq.com/pag...

62210
来自专栏北京马哥教育

2018 年 Linux 的 8个发展预测和学习建议

运维行业正在变革?推荐阅读:30万年薪Linux运维工程师成长魔法 转眼间,时间已进入 农历2018 年新年,2018 年又会有哪些新的趋势?OMGUbuntu...

4819
来自专栏杨建荣的学习笔记

DBA和开发同事的一些代沟(四) (r7笔记第36天)

DBA和开发都是两个可爱的团队,如果合作起来,那战斗力可是杠杠的。如果合作不愉快,那就是一件简单至极的事情都能扯皮几天几夜,而且还解决不了。 今天出公司的时候碰...

3598
来自专栏james大数据架构

那些公司网页隐藏的彩蛋

  我们知道美帝电影基本在结尾都会有彩蛋出现 多则会有2个,以预期下一部剧情走向,那在互联网界呢?一般会在与用户直接接触的页面中做文章。   近年来用户体验越来...

2266
来自专栏域名资讯

2017上半年Radix注册局优质域名报告

注册者销售的优质域名已经涵盖优质域名续费成本。

1140
来自专栏资深Tester

一个致命的bug--自负

3268
来自专栏Guangdong Qi

iOS审核拒绝苹果官方原因详解

8952
来自专栏PingCAP的专栏

TiQuery:All Diagnosis in SQL | TiDB Hackathon 优秀项目分享

“距离 Hackathon 结束已经一个多星期了,感觉心情还是没有从激情中平复过来。不过由于我读书少,这时候好像只能感慨一句,黑客马拉松真是太好玩了……”

1493
来自专栏机器人网

小型无人机飞控系统如何组成和设计?

在经历了早期的遥控飞行后,目前其导航控制方式已经发展为自主飞行和智能飞行。导航方式的改变对飞行控制计算机的精度提出了更高的要求;随着小型无人机执行任务复杂程度的...

5003
来自专栏ImportSource

异地双活实践笔记

最近恰好在搞异地双活,以下是一个梳理: 基本概念 1、异地容灾。这仅仅是一个冷备的概念。也就是在平时正常的时候,另外一个机房只是当做备份。 2、异地双(多)活...

3.9K9

扫码关注云+社区

领取腾讯云代金券