01 August 2020

人有三观:世界观、人生观、价值观,三观主要体现了一个人对世界和人生的认识以及自身的追求。

对于程序员来说,可以再单独提炼出一种“观”---代码观。

代码观就是一个程序员对写代码这件事的态度和追求,严格来说,也应该属于价值观的一种。

今天就来聊聊我的代码观。

其实这篇文章我开始起的题目是《码农的自我修养》,这个题目有两层意思:

1. 码农:指的是程序员中的屌丝群体,一直在一线写代码,并且大多是业务代码,智商算不上特别高,像linux、redis、nginx、Bitcoin 这种天才的程序是写不出来的。

2. 修养:主要指人的综合素质,指正确的待人处事的态度。

但是我后来想想觉得不妥,“修养”一词用的有点大了,我的这些观点都是很主观的,不见得是正确的,很可能还是错的,所以还是叫“代码观”准确一些。

PS:我知道有本书叫《程序员的自我修养》,那本书讲的好像是操作系统的底层开发,书名起的并不贴切。


代码观多少跟编程能力、智力和工作经验有一定关系,但关系不大,更多的是一种态度。

举个例子,当有一个需求,老板很着急,直接找到你让你尽快实现,你咋办?

有两种实现方案:

第一种只需要1天时间,但实现方式很恶心,并且后患无穷,就像从电线杆上直接拉根线到你家,可能一两年以后需要极大的人力来弥补甚至会造成重大损失。

第二种实现方式需要1周甚至更长,但是一劳永逸,做好后几乎不需要再管,给人感觉就像空气一样不存在。

让你选,你选哪个?

选第一种,好处大大的,能给老板留个好印象:嗯,这哥们儿靠谱!为日后升职加薪奠定良好基础。况且谁知道一两年后还负不负责这个项目,没准儿都不在这儿干了呢。另外,项目早一天上线,可能确实能够赢得先机,先发优势有时很重要,老板不在乎你咋实现,只在乎什么时候实现,管他黑猫白猫,抓到耗子就是好猫。

选第二种,就没那么出彩了,老板会觉得你干活有点儿慢,还可能因此错失市场机会,至于上线后运行良好不出bug,难道本来不就应该如此吗?

人们又不可能穿越到【选择第一种实现方式的你所在的】平行世界中去做对比,又有谁能体会得到这其中的差别呢?

我觉得这个问题没有唯一的正确答案,至于如何选择,就是一个人代码观的体现。


讨论代码观之前,先简单聊个宏大点儿的话题:生命的意义是什么?

我认为生命的意义有两点:

1. 生命的意义就是延续,首先要让自己活下去,然后是让自己的基因延续下去,世间所有生物,甚至连病毒这种介于生物和非生物之间的物质,都以此为终极目标。凭什么这么说?因为凡是不以此为目标的基因都被大自然淘汰了。

2. 对于人类这种智慧生物来讲,生命还有个特别的意义--对抗墒增。

热力学第二定律告诉我们:熵(描述一个封闭系统中的混乱程度)总是增加的,杯子总有一天会打碎,乐高零件总会丢失,恒星总有一天会毁灭,宇宙总有一天会归于一片热寂-这是宇宙终极演化规律。

然而,在宇宙亿万年的演化过程中竟然出现了高度有序(负熵)的有机生命体,又经过亿万年的进化,竟然出现了人类这种智慧生物,人类大脑之精密复杂简直是宇宙中最大的奇迹。有人说不对啊,熵不是一直增加的吗?怎么还出现负的了?其实这并不违背热力学第二定律,因为人类这种负熵生命体的出现,能够消耗宇宙中更多的能量,反而使得宇宙整体上的熵增加的更多了。所以人类在宇宙中存在的意义就很明显了--对抗墒增,让自己的熵减少,就要变得更加智能,就能利用更多的能量,从而使得整个宇宙的熵增加。

讨论生命的意义属于人生观的范畴,看似有些跑题,其实这才是代码观的基础,我必须首先指明,我的代码观是基于这样的人生观而得出的。

我的代码观分为“道”和“术”两部分,“道”是内功心法,是指导思想,“术”是武功招式,是具体实践。

没有道只有术,做出来的东西就像无源之水,无根之木,结果往往是迅速腐烂,沦为垃圾。

对于码农来说,“道”就是利用“编程”这个武器来对抗墒增,更具体点说,就是把代码写好,符合逻辑,不出bug,减少重复劳动,以此让人类世界变得更有序(整个宇宙变得更无序)。


要写好代码,我觉得有三个词必须刻在脑子里,写代码时要不断提醒自己:逻辑、收敛、抽象。

符合逻辑是写代码的基本要求,逻辑错误是硬伤。

一部电影的剧情可以天马行空,但逻辑必须符合电影自身的设定,这叫自洽,否则就是有硬伤,无论拍的多么惊险刺激,特效多么逼真,演员多么大牌,在我心目中的评价都会是负分。好的例子比如《黑客帝国》,比如《超时空接触》,逻辑都非常严谨自洽。反面例子比如《独立日》--人类写个电脑病毒能入侵外星飞船,比如《普罗米修斯》--人类科学家在外星面对未知生物的脑残作死表现等等,这还是有逻辑硬伤的电影里拍的不错的,还有更多垃圾电影我连看都看不下去,就更记不住名字了。我最讨厌的题材就是有关时空穿越回到过去改变现状的电影,这种情节无论如何都是有悖论的,我认为这是导演或原著作者江郎才尽想法枯竭的表现(《哈利波特》里就有这么一集)。顺便说一句,有个国产动画片《超级飞侠》,动画做的不错,但情节太烂了,每集都是满满的逻辑硬伤,瞎编一通,我每次看都想吐(别问我为什么要看)。

说跑题了,拐回来,有逻辑硬伤的电影可以不看,代码如果有逻辑硬伤就会出bug,轻则影响用户体验,重则造成重大损失。

所以码农的脑子要清楚,别跟一团浆糊似的,一是一,二是二,不要把两件不相干的事混为一谈,举个例子,当定义“状态”时,要想清楚这种情况到底算不算是一种状态?你很可能需要增加的是另一个字段,而不是一种状态。

不同层面的东西,也不要混到一起考虑,不同层面干不同的事,边界清晰,各司其职,TCP/IP协议的分层思想就很值得借鉴。

收敛的含义就是汇聚于一点,好的代码可以做到任何功能上的增、删、改都只需改动一处代码即可,如果你发现为了增加个新功能需要在代码中到处搜索,改动点多达十几个几十个,或者项目中到处都是重复的代码,说明这不是一个好的代码。

抽象是更高层次的收敛,指从众多的具体事物中,抽取共同的、本质的属性,舍弃个别的、非本质的属性,从而形成概念。抽象可以大大降低代码复杂度,提高代码可复用性,这一点经常写业务代码的PHP码农们应该好好学习借鉴一下Java的编程思想,比如Java的Spring框架,依赖注入和面向切面,体会一下什么叫高度的抽象。总的来说,抽象能力对先天智商要求很高,不是什么人都具备的。


下面是我在开发过程中总结的一些经验和观点,想到哪儿写到哪儿。

如何修改别人的代码?

假如一个项目之前是别人开发的,现在交到了你的手上,有个新需求要做,我建议先不要急着上来就改,好好看看原先的代码,试着理解一下最初的开发者的设计意图,很可能会发现这个新需求最初的开发者已经提前设想到了,并且在代码中预留出了一些配置和方法,只需对现有代码稍加改动就可以实现新的需求,如果代码没看明白就稀里糊涂的开始改,往往会造成巨大的混乱。

用好间接层

码农圈子有句名言:计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决,这简直是解决复杂的问题的法宝。但是我觉得这是一把双刃剑,要慎用。很多码农都不喜欢看别人的代码(我也一样),所以当接手了一个旧项目,要加新需求时,往往会想:这项目太复杂了,我懒得看,加新需求简单,我管你里面咋实现的,我在外面再给你包一层不就完了吗?这样虽然解决了问题,却使这个系统的复杂程度又高了一个量级,更加难以维护。

例如我在一个项目中对接了众多的供应商接口,这些供应商的接口返回信息五花八门,所以必须定义一组统一的error code,让各供应商接口的报错信息映射到这组统一的error code上,这组error code我已经定义好了,并且在一个最重要最常用的接口上实现了这个error code的映射,后面要做的工作不过是把这个error code扩展到其他接口上,并在原有基础上增加新的error code即可。可是后面接手的人并没有深刻理解这组error code的用意,当某些接口报类似于“售空”这种错误时,没有返回事先定义好的error code,而是随意返回了一个什么数字,这样就产生了歧义,后面的很多混乱都与此有关。再后来,产品经理提出一个新的需求:为所有接口制定一个统一的error code。如果是我,我会告诉产品经理这个error code映射我们早就定义好了,不用再定义,但是开发者没有在原有基础上进一步完善,而是采取了“增加中间层”的方式来解决,又定义了一组新的error code,很多地方都要为这个新的error code重新做映射,这样就造成了更大的混乱,以至于后面都无法维护了。所以我说增加中间层这种方法要慎用,用不好得不偿失,我经常花很长的时间看别人的代码,试着以对方的思维模式理解代码(看代码也需要“同理心”),我还试着模拟对方的风格写代码,尽量不破坏原有代码结构,往往看代码要花1-2天,动手改的时候也就十分钟,改动不过三两行。

保持吝啬

我觉得在写代码方面,应该做一个“吝啬鬼”。

磁盘能不写入就不写入,外部接口能不调用就不调用,循环能少一次是一次,代码能少写一行就少写一行,内存是很宝贵的,不要什么都往redis里面扔,数据库能不访问就不访问,如果需要查询,只找出你需要的字段,如果有不需要的text字段,就不要“select *”。

做任何事之前,都应该考虑一下对性能的影响,比如循环里面都干了些啥要特别注意,本来可以查一次数据库就行了,结果把同样的查询放到了循环里,循环一千次就查一千次,又没有缓存,数据库不挂才怪,当系统变得复杂,开发人员一多,一层包一层时很容易出现这种情况,所以对于重要接口要做压测和性能分析,找出系统瓶颈。

还要时常问一下自己这么做是不是可持续的?比如我发现有些人记个日志什么的,总喜欢把有用的没用的东西一股脑写到存储里(很可能还是没分表的MySQL),一个大json能有1-2兆,就图个省事儿,弄不好遇到访问高峰网卡都能给打满了,即便当时没事儿,2年以后数据量就上亿甚至几十亿,占用存储空间能上T,查询慢,无法备份,磁盘空间不够用等各种问题就暴露出来了,再想清理就是个大麻烦。我觉得总抱着“先这样,以后再说”的想法是不对的,不对未来造成叠加影响的事可以以后再说,造成叠加影响的还是应该一步到位,不给自己挖坑,也不给别人挖坑。大家应该都听说过奥卡姆剃刀理论,简单来说就是无非必要,勿增实体,应该让一切都在掌控之中,简单优雅,我不喜欢失控的感觉,失控就是熵增。

数据库表的设计

上学的时候都学过,设计数据库要讲究符合范式要求,有第一范式、第二范式、第三范式什么的,实际工作当中发现满不是那么回事,生产环境用的表不满足范式要求的比比皆是,有些是业务发展了,最初的设计不满足要求了,在原有基础上打补丁造成的,有些是为了查询效率故意设计了一些冗余,还有些是设计时就没想好或者就是偷懒,数据库不满足范式会带来各种依赖问题,我的看法是能满足还是尽量满足范式要求吧,对于面向用户的,查询量大的表,为了性能考虑,可以有些冗余。像对账之类的系统,对数据一致性要求非常高,对时效性要求不高,就该严格按照范式要求来。

以前设计过一个订单扩展表,字段有四个:ID、所属订单ID、字段名、字段值。相当于字段名是灵活可定制的,设计初衷是想把一些临时性的、不需要检索的、部分订单特有的数据放在这个表中,免去频繁修改表的麻烦,因为这个设计确实很省事,最终导致这个表被滥用了,甭管有用没用啥啥都往里扔,我最初预估平均一个订单插4-5条数据顶天了,可实际上平均一个订单插入了17条数据,有时一张订单要往这个表里插入六七十条数据,这个做法显然是不合理的,但不是所有人都能认识到这点,这也说明人类要对抗熵增是很难的,现在想想把此类数据放到基于文档的Mongodb中或许是个更好的选择。

做后台

做后台管理的机会不是一直有,所以一定要珍惜。

为啥呢?因为后台管理通常不会配专门的产品经理,所以自由度比较高,有一定自由发挥的空间,比较好玩儿。

越是可以自由发挥,越是得讲究。

有很多现成的前端框架,比如Bootstrap,样式啥的都帮你设计好了,直接拿过来用,做出来的东西就挺像那么回事儿的。

按钮颜色得讲究,蓝色代表最常用的操作,白色用来显示一些信息,红色代表危险操作,绿色代表可以放心点的操作,灰色代表操作禁用,不能随便滥用。

开发后台最重要的是自己用着要舒服,看着要顺眼,比如表格每列的宽度、字体间距、标题大小、字体样式、颜色等要恰到好处。

有很多图表js框架,做出来的效果很炫酷,显得高大上,可以拿来用。

后台管理项目还是个很好的前端框架试验田,有精力的,react、VUE还是什么最新的玩意儿,都可以拿来玩儿。几年前我学AngularJS就是通过做后台熟悉的,可惜现在不流行了,也没这个精力了。

PHP

PHP程序员现在不好找工作了,我觉得一个主要原因是互联网的发展已经过了最初的黄金期,那个靠短平快就能赚大钱的时代,互联网主要的流浪入口被几家寡头控制了,大厂动辄几万人十几万人,对系统稳定性,标准化要求更高,所以java这种标准化做的非常好的语言就重获新生了。PHP非常灵活,容易上手,开发速度快,这个在当年是很大的优势,现在却成了劣势,举个例子,PHP中的数组就是个筐,什么都能往里装,管你是一维二维还是n维,是数字还是字符串还是对象,太灵活了,太好用了,哪儿像java还得搞什么泛型,可bug也往往都会出在这些地方,PHP的弱类型容易让代码变得不严谨,出错的几率要比java高,开发PHP程序必须时刻提醒自己不要把PHP提供的便利性过度使用。还有人说PHP性能不行,其实大多数应用场景也够用了,另外一些高并发的场景,PHP也有swoole这种异步非阻塞的框架,底层用C写的,性能和go比起来也不差,咋说呢,现在觉得用什么语言并不是特别重要的一件事,编程语言都是大同小异的,重要的还是思想。

逻辑分离

开发中要注意业务逻辑和非业务逻辑分离以及核心业务逻辑和非核心业务逻辑的分离,比如在查询价格或者提交订单时需要记各种log,记log属于非业务逻辑,直接写在业务逻辑的方法里倒是省事,但是对原有业务代码的侵入性很大,类似这种代码多了,业务逻辑的方法就会变得很臃肿,分不清哪些是核心业务逻辑了,这种情况应该想办法把它们解耦,比如用个观察者模式什么的,spring框架有个面向切面编程的思想我觉得特别好,像织毛衣一样把不同逻辑编制到一起,又互不影响,特别值得借鉴。

核心业务逻辑和非核心业务逻辑也需要分离,不要因为一个不起眼的非核心业务逻辑导致核心业务逻辑走不通挂掉了,非核心业务逻辑不可用时要能自动降级,影响响应速度的非核心业务逻辑可以改成异步非阻塞方式(送到消息队列里跑)。

函数的参数的设计要符合逻辑

一个方法或函数的参数设计应该是有讲究的,比如有个 getList()方法,第一个参数是 params,是一个数组,用来指定查询条件,这些查询条件都是用来生成sql查询语句用的,params 中就不应该再放其他含义的参数,比如你想增加一个参数 noCache, 用来指定是否从缓存中读取数据,如果把这个 noCache放到了params 里面,就把 params的含义搞乱了,所以合理的做法应该是再增加一个参数,这样还有一个问题,假如以后又想增加一些参数,这些参数也是和查询无关的,并且大多数的时候不需要特别指定,只需有一个默认值就行了,如果都加在方法的参数列表里面,会使参数列表越来越长,所以我通常都会加一个 options 参数,用来存放这些可选项,开发中你必须心里很清楚哪些参数要放到 params里,哪些要放到options里,哪些要单独成为一个参数(例如查询的起始位置、返回的最大记录数、返回的字段名就适合单独作为一个参数),不要搞混。

还有一种情况,一个方法最初就是传入参数,返回结果,比如:

    public function getSomeValue($val)
    {
        ......
        return $someValue;
    }
    

后来,你想返回一些方法执行过程中的中间数据,用于问题排查,日志记录、统计监控等,但是因为这个方法已经用在了很多地方,不能轻易改变返回值的数据类型和结构,怎么办呢?这是我会增加一个引用参数,比如 $debugInfo ,用来存储这些信息:

    public function getSomeValue($val, &$debugInfo = [])
    {
        ......
        $debugInfo = ['info' => 'test'];
        return $someValue;
    }
    

通常我是不喜欢使用引用类型的变量的,因为容易出一些难以排查的bug,只有上述情况是个例外,但是使用这个$debugInfo时,你必须很清楚这个变量仅仅是用来排查问题、记录日志的,不应该参与到任何业务逻辑当中,可是实际使用过程中,我发现有人把这个参数当成了和业务逻辑密切相关紧密耦合的一个参数,这样就违背了设计初衷。还有一种情况是把这个参数当成了垃圾场,什么数据都往里扔,这个变量存储了巨量的数据,只要是变量肯定要占用内存的,什么都往里存肯定要增大服务器开销,这就违背了上面提到的要“保持吝啬”的原则,但仗着服务器内存大,这些细节很少有人会在乎。

路径依赖

维基百科的定义:路径依赖是指给定条件下人们的决策选择受制于其过去的决策,即使过去的境况可能已经过时。 路径依赖被经济学采用,以解释在制度变迁中偶然性的作用,以及制度变迁对初值的敏感性。

有个很经典的例子可能很多人都听过:美国航天飞机燃料箱两旁的火箭助推器的宽度是由2000年前牵引古罗马战车的两匹马的屁股的宽度决定的,这是为什么呢?不知道的可以在网上搜一搜。

这些年的开发经历,发现路径依赖的影响实在太大了,很多奇奇怪怪的设置可能都源于最初的一个轻率的决定,所以为了避免日后很多的遗憾,做决定前还是要慎重。

其他

变量/函数/类/表的命名要力求准确,一看就知道是干什么的,不要有歧义,比如如果是 is开头的方法就应该返回 true 或 false,否则就应该换个名字,不要用is开头,用英文单词不要用汉语拼音,更不要用拼音首字母。

保持简单,避免过度设计,一些简单的业务逻辑层层封装,使用各种设计模式也没太大必要,当下应该只管建造可运行的最简化系统(但不等于不设计)。

能声明成 private 方法的就不要声明成 public,public 不要省略,理由不用我再多说了吧,当你想修改一个方法的参数和返回值时就知道了。

曾经在公司的代码钩子检查里面有个规定:单个方法不能超过50行。规定的出发点是好的,防止出现难以维护难以阅读的超长的方法(我曾经见过1000行的方法),事实证明这个规定对于底层框架级的代码来说是够用的,对于业务代码就稍微苛刻了点(后来改成100行就足够用了)。对于这一规定,就分成了两类人,一类是发现代码超过50行了先尽量写得紧凑些(比如一行可以有多个数组元素的赋值),还是不行就对方法进行拆分,当然要多花一些时间,但是是值得的。还有一类人的做法是把多条语句(注意是以分号分隔的语句)放到同一行里,这和把多个数组元素放一行里是有本质区别的,多个数组元素之间是逗号分隔的,都是在给同一个数组赋值,逻辑一致,不会影响代码阅读。多条语句就不同了,比如我在代码库中随便搜了一些代码各位感受一下:

        //没错开头有个右大括号,是上一条if语句的结束
        }$surcharges = $one['RateInfo']['Surcharges'];$surcharges_included = 0;
        continue;}$_list[$hotel['Id']['text']]['room_list'] = $_room_list;
        if (empty($one)){continue;}$rateInfo = $one['RateInfo']['DailyRates']['DailyRateDate'];
    

这种写法让一行代码执行了多个逻辑,都是不相干的逻辑,虽然不影响程序执行,但非常影响人类阅读,有时不留神只看到前半句,后半句漏掉了,就搞不清一个变量是怎么突然冒出来的,上传代码一时爽,维护代码要骂娘。这个50行的规定出于好心,在这里却起了反作用,让代码更加难以维护了。

要记住代码不仅是给机器看的,还是给人看的,无论规定多么严格,不把多条语句写在同一行里都应该是最后的底线。

应该添加必要注释,无论是代码中还是git commit中,git提交是的说明不应该是一水儿的 “update”之类。

确定无用的代码就该直接删除,不要仅仅注释掉。

不要有Magic number,用 const 给每一个常量一个定义,不然1年以后别说别人,可能连你自己都不知道这个数的含义是什么。

一个方法里有很多嵌套的if,很多嵌套的foreach,很多switch case,肯定不是什么好代码。

多知道点底层知识肯定有好处,比如以前一个同事推荐过一篇文章,介绍计算机系统中各种操作所花费的时间的量级,cpu执行一条指令要0.38ns,假如把这个时间放大到1秒,那么其他操作相应需要多久?其中内存寻址需要4分多钟,cpu一次上下文切换要一个多小时,从SSD磁盘读取1M顺序数据要花1个月,一次磁盘寻址要花10个月,互联网上走一个来回要12.5年,心里有了这些概念,在干一些事情时心里就要掂量掂量值不值得。

还有的知识不见得在工作中真的能用上,主要是满足一些好奇心,比如很多支持高并发的软件底层用的都是epoll,像nginx、swoole什么的,为什么epoll可以支持很高的并发?epoll和select的区别是什么?要充分理解这个可能得亲自用c什么的写个demo,把select和epoll通信都实现一遍,等理解了以后就会觉得心理很充实,再看那些程序也不觉得有多神秘了。

不过说实在的,每个人智商是有限度的,有些知识可能达到了我的理解能力的边界,这就需要很长时间的冥思苦想才能理解,而且即便当时明白了,如果不反复温习,把它固化到脑子里,过一段时间很容易忘,再次理解又要花挺长时间。还有一些知识,完全超出我的理解能力(比如深度学习),我就只能望洋兴叹了,我最羡慕的就是那些智力超群的人,不论啥事一点就透,真是人比人得死,货比货得扔。

不要信任任何人的输入,多做边界条件检查

网上有很多黑客,羊毛党,天天盯着你的漏洞撸羊毛,所以对于用户输入要特别注意,防篡改,防SQL注入,防xss。

即便是只给公司自己人使用的后台也不例外,后台系统尤其需要注意输入数据的边界检查,在产品形态、前台交互上尽量减少出错的可能,不能让任何人输入明显不合理的值,特别是和钱打交道的地方,这方面是有血的教训的。

《重构》

很久以前,我给小组同事分享过《重构-改善既有代码设计》这本书的读后感,这本书中的很多观点都和我不谋而合,我上面说的很多内容都能在这本书中找到相应的论述,下面我再从这本书中摘出一些我认为说的很对的部分,让大家再温习一下:

何为重构?

名词:对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。

动词:使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。

编写软件时有两顶帽子:添加新功能和重构,应该不断在两顶帽子间切换。

何时重构?三次法则:事不过三,三则重构。

为什么重构有用?

* 难以阅读的程序,难以修改。 -- 应编写容易阅读的代码

* 逻辑重复的程序,难以修改。-- 所有逻辑都只在唯一地点指定

* 添加新行为时需要修改既有代码者,难以修改。--新的改动不会危及现有行为

* 带复杂条件逻辑的程序,难以修改。--尽可能简单表达条件逻辑

想重构,怎么对产品经理说?

99.9%的产品经理是“进度驱动”

最终的建议:不要告诉PM

理由:受进度驱动的PM要我尽可能快速完事,至于怎么完成,那就是我的事了。我认为最快的方式就是重构,所以我就重构。

“债务”和“利息”

把未完成的重构工作形容为「债务」。很多公司都需要借债来使自己更有效地运转。但是借债就得付利息,过于复杂的代码所造成的「维护和扩展的额外开销」就是利息。你可以承受一定程度的利息,但如果利息太高你就会被压垮。把债务管理好是很重要的,你应该随时通过重构来偿还一部分债务。

重构与性能:先编写构造良好的程序,再进行性能优化。

代码中的坏味道:

* 重复代码

* 过长函数

* 过大的类

* 过长参数列

* 发散式变化(一个类收多种变化的影响)

* 霰弹式修改(一种变化引发多个类相应修改)

* .....


以上啰嗦了这么多,并不不涉及什么高深的技术,有些是常识,有些是非常主观的认识,我也承认在工期的压力下,能够坚持做到这些还是挺难的。回到本文开头的那个问题,两种方案,如果让我选,我选哪个?我这个人比较喜欢中庸之道,不喜欢走极端,但也不是没有侧重,上面写了那么多,各位也应该能看出,在效率和质量方面,我是更注重后者的,如果让我选,如果时间还算充裕的话,效率vs质量,我大概是3/7分,如果时间紧张,就4/6分,质量占6成,不能再低了。

非常遗憾的是,绝大多数人并不重视我所看重的这些,大家看到的都是目标是否达成,至于过程,无所谓了。只有真正写代码的那个人,才能体会到良好的设计真的可以事半功倍,还有一种美感,其他人都感受不到,因为这件事是无法衡量的,你不可能穿越到过去,把这项工作从来一遍,然后对比出来怎样做才是最好的。你做的再好,别人也无法知道做的不好时是什么样子,你做的再差,别人也无法知道做的很好时是什么样子。面试的时候,面试官也不会问到这些,短短1-2个小时,不可能真的了解一个人。所以大部分时间,你只能是出于一种自娱自乐罢了,一种完美主义倾向,还有就是一种责任感,对自己负责,对别人负责。

码农这个职业在中国还是个青春饭,再过若干年,我不见得还能从事这一职业,写这篇文章,算是对自己码农生涯的一个总结,能力有限,认识有限,多多包涵。