程序员员求生指南:关于写程序的二三事
我 是一个热爱写程序的家伙。我的第一台电脑,是13岁时买的Apple II,在那之前,我已经开始到同学家用「小教授二号」学写程序了。高中时我当电脑社社长,带队参加教育部办的全国程序大赛,幸运拿到冠军,大学、研究所唸 的也是相关科系(台大资工/Stanford CS)。工作20年来,一直从事软件相关领域,即使担任主管职务,也一直对技术充满热情。
写程序写了这麽多年,多少有些体会。我把自己对写程序这份工作的心得写下来,希望能给从事相关领域或有志于写程序的人参考。
一、我适合当程序员吗?
程序员,也叫软件工程师、程序设计师,对岸叫软件工程师、程序员。我觉得「程序员」三个字简洁有力,所以就用这个词。
如果你正从事这份工作,恭喜你!这是个热门行业,在可预见的将来,也不会消失。不过也别高兴太早,这一行的技术汰旧换新非常快,必须不断努力学习才行。
一点天份
打开一个空白档案,必须创造出程序。与所有创造性的工作一样,写程序需要某种程度的天份。程序员生产力好坏差别很大,倒不是说一天能写多少行程序 (这可能是最没参考价值的数字了),而是品质有天壤之别。天份很高的程序员,一个抵十个,没天份又不努力的,一天製造的问题可能多于解决的问题,生产力是 负的。具体来说,逻辑推理、抽象思考、创造力、理解力,这些都是相关能力。
当程序员不一定要有多高天份,毕竟像Linus Torvalds(Linux创始者)那样的天才很罕见,但一点天份还是必需的。如果你发现自己写程序、看程序、解bug都很痛苦,半年一年了也不见改善,也许这份工作不太适合你。
一些热情
如果你对写程序充满热情,又有一定的天份,那再好不过。最起码,你有时会沉浸在写程序或解bug的情境中(英文有个词叫“flow”,心流)、不想 被中断,这样就够了。如果你从未出现过这种情境,那麽你可能不会热爱这份工作。不过没关系,世界上不热爱自己工作的人其实不少。如果你能做好这份工作,眼 前又没有更好的选择,继续做下去也没问题。
很多努力
努力是一定要的。当一名好的程序员,要学习的东西太多了,而且不努力很快就会被淘汰(虽然很多工作都是这样),这是入这行前应该要有的体认。
二、程序员基本能力
职业道德
甚麽?写程序也有职业道德?有的,而且还很重要。我说写程序是一门良心事业,因为通常你写的程序只要符合规格、能正确执行,就可以交差了,而你的主 管或同事很难一眼看出程序码品质有问题,例如:在特定条件下会爆掉、滥用複製贴上、采用一些肮葬写法、程序可读性很差、模组之间纠结在一起,等等。
你焊接过电路板吗?要是电路板绕线一团乱、零件歪七扭八、接脚没焊好,你能交差吗?但是写程序可以。因为程序码是一种抽象产品,没有「外观」可以观 察。如果你的团队要求code review,这个问题可以得到某种程度的改善,但仍不能彻底解决。程序员的纪律和职业道德很重要。(关于软件的抽象本质及软件开发的特殊性,我去年写过 一篇英文blog探讨这一主题,有兴趣的人可以参考看看:“Making Abstract Products Makes Things Hard”)
程序语言
程序语言的学习,是程序员最最基本的能力,而且应该至少精通一两种语言。随著程序经验的累积,学习不同程序语言的速度会越来越快,例如从几个月缩短到几週。当然精通一门程序语言,不是几星期、甚至几个月就能达成的,但迅速接手并维护既有程序码,是对合格程序员的基本要求。
通常第一种程序语言学最久,因为很多观念也是第一次学,例如变数、迴圈、阵列、递迴、I/O、网路、多执行绪、物件导向、regular expression、functional programming……。等到学第二种、第三种程序语言,新的观念越来越少,主要在学语言本身,速度就会变快很多。
资料结构及演算法
如果你是本科系毕业,资料结构及演算法应该是必修课。如果没学过,建议花点时间学一下。倒不需要买一本厚厚的书折磨自己,但基本的概念一定要有,例如:
- 资料结构
- 阵列(array)、串列(list)、堆叠(stack)、伫列(queue)
- 树(tree)、二元树(binary tree)、杂凑表(hash table)
- 指标(pointer):这也许不算资料结构,许多高阶语言也不让你用pointer,但是对记忆体、指标要有概念,这是程序员与非程序员的区别之一
- 演算法
- 对以上资料结构的各项操作
- 排序(sort):至少搞懂3、4种基本的排序演算法,例如bubble sort、quick sort、merge sort等
- 搜寻(search):depth-first-search、breadth-first-search、binary search等
- 其它:迭代(iteration)、递迴(recursive)、分治法(divide and conquer)、时间/空间複杂度的基本概念(big O)等
网路上资源很多,Google一下、多写一些程序练习,弄懂以上基本概念,应该就够用了。
网路协定
TCP/IP、HTTP、DNS等这些都是基本的网路协定。不需要到专家程度,但身为一个程序员,除非你的工作与网路完全无关(这种工作应该越来越 少了),否则对这些网路协定的运作应该要有起码的了解。例如你能讲清楚,从你在浏览器输入一行网址到看到网页内容,在网路上发生了哪些事?以前我在 Yahoo面试前端工程师的时候,喜欢问一个问题:请解释cookie是怎麽运作的,结果不少人答不出来。
当然现在的程序开发环境很方便了,各种library一大堆,我们通常不需要自己实做这些底层的东西。但不懂这些东西运作的基本原理,会让你在 debug时被卡住,因为整个网路系统的运行,都是建立在这些基础架构之上。这些网路协定,再过很多年还是会继续存在,花一点时间搞懂这些,我认为很值 得。
除错能力
讲除错能力不太准确,因为除错不是单一能力,而是结合了经验、对程序的了解、对系统架构的了解、抽丝剥茧的能力、直觉,以及各种hands-on能力的综合,就像当侦探一样。台语有句话叫「医生怕治咳,师傅怕抓漏」,差不多就是这个意思。
我在Yahoo工作期间,最刺激的事莫过于排全球on-call了。所谓on-call,就是全球Yahoo网站出包时,你要在最短时间内找出问题 并修复,那真是超级debug。拜託,Yahoo网站那麽複杂、程序码又那麽多,出问题的模组又不是我写的,美国同事都下班了,谁知道怎麽解决?对不起, 那是你家的事,排了on-call你就得想办法解决。功能上的问题还有迹可寻,最棘手的是像系统过载这类问题,爬log、写script、trial- and-error,总之想方设法揪出元凶。
程序员应该具备良好的除错能力,不管程序谁写的。另外,修bug也是一门学问,是采用锯箭法、贴狗皮膏药,还是找到病灶、解决问题背后的问题,就看程序员功力了。
写出可读、易维护的代码
这个要求听起来很合理,不是吗?其实这是最难的。写程序这麽多年,看过多少代码,我跟你说,这个世界上的烂code佔绝大多数,好code只佔一小小部份。我自己也不断在这条路上努力著,到现在也不敢说自己写的code多好。
为甚麽这件事这麽难?我想了一下,大概有以下几个原因:
- 可读性高的代码,通常是用很好的解法,解决了真正的问题
- 你需要彻底了解问题(problem domain)
- 你需要考虑过至少几种合理的解法(solution domain)
- 你需要对程序语言、程序库、既有程序架构和可运用工具很娴熟
- 你要能以简驭繁,而这代表你掌握了更高的东西
- 例如牛顿的F=ma、爱因斯坦的E=mc2,这是神人等级的功力(只是举例说明,写程序不需要到这样)
- 你写程序必须很有纪律,例如:
- 不急著马上写code,先想清楚问题、解法、架构
- 恰到好处的注解,少了不行、过犹不及
- 用心想过的命名(程序界有句名言: There are only two hard things in Computer Science: cache invalidation and naming things.)
- 压抑copy & paste及产出一堆烂code的衝动:
- Bingo! 找到一段code了,看我的copy & paste
- 可以work就很好啦、这麽漂亮的解法……
- 好累,我不行了,先commit code再说
- 在commit code之前,自己再好好review一次(我个人经验,通常这个步骤可以改进程序好几个地方)
- 越容易维护、扩展的代码,代表它的複杂度越低
- 降低软件複杂度是软件工程的最大挑战,软件複杂度就像软件的熵(我的第一篇英文blog “Software complexity is software entropy”,就是讲这件事)
- 必须做到low coupling、high cohesion,而这两件事都很难
- 低複杂度的软件系统,代表裡面各个模组的複杂度都必须更低
- 当你轻易多用了一个外部组件、增加了一个external dependency,你就把它的複杂度整个带进来了,所以要很小心
- 现实环境的因素,导致好的程序码不易产生
- 专案时程的压力
- 程序员经验的限制
- 团队未采用一些最佳实务
- 有决策权的人对软件开发不够了解
代码品质的重要性,每个人都知道,连路人甲都知道。现实的难处在于:第一版的代码,只要能work,品质好坏是很难看出来的;它们的差别,要到系统 后续的运行、维护及扩展才能看出来,然而此时木已成舟,程序只能修修补补继续用下去,最多小幅重构(refactor),直到软件生命週期的结束。
写出好的代码,时间会花比较久、会导致专案时程延后吗?其实并不会,这是能力限定,不是时间限定。写出第一个版本,花的时间都差不多。但后续版本就 差很多了,写得越好的代码越好改。你如果改过那种high coupling的系统,你就知道我说的意思了,那真是人仰马翻,超high的。这种代码要是装在箱子裡,箱子上会标示「易碎/FRAGILE」。
Photo credit: Hsing Wei@flickr CC BY 2.0
写出好的代码并不容易。假如我们从1分到10分给程序码打分数,10分真的很难很难,我自己也做不到。但一般人经过努力,达到6、7分应该是没问题的。如果你想看书,我在这裡推荐一本:《代码大全2》。教人写程序的书中,这是我看过最好的一本了,只是内容比较多,需要时间消化。如果还有兴趣多看,我个人觉得Martin Fowler也写了不少好书。
三、程序员进阶能力
具备以上的程序员基本能力,我想就足以胜任大部份「单兵程序员」的工作了。如果想在技术上更上一层楼,以下是几个我认为比较重要的进阶能力,提供给大家参考。
作业系统
大学修的那麽多课裡面,我感觉对工作最有用的就是「作业系统」这门课了。对作业系统(OS,operating system)的了解,是资深程序员应该具备的。例如:
- Hardware: CPU, memory, I/O devices
- Process, multi-thread, scheduling
- Inter-process communication: signal, socket, pipe, named pipe, shared memory, message queue…
- Synchronization, deadlock, mutex, semaphore
- File system, cache, virtual memory, page fault…
- Real-time system, distributed system
作业系统本身就是一支超大型程序,有著无数前人的心血。加上作业系统的基本概念,几十年不变,所以花点时间弄清楚这些观念,我认为很值得。
资料库
不是每个程序员的工作都会使用到资料库,而且现在不少人用NoSQL存资料。儘管如此,我认为关连式资料库(relational database)还是很重要,不管是MySQL、PostgreSQL、MS SQL或Oracle都好,资深程序员应该至少对其中一种有相当的了解。
题外话,多年程序写下来,我对ORM(object-relational mapping)抱著存疑的态度。网路上有篇文章:Object-Relational Mapping is the Vietnam of Computer Science,应该是反ORM的代表作之一,有兴趣的人可以看看。还有一篇有名的文章:The Law of Leaky Abstractions,讲的是每一层抽象化都或多或少会有漏洞。从leaky abstraction角度来看,SQL已经是一层有洞的abstraction了,而ORM洞更大!(注:写这两篇文章的两个人,刚好就是Stack Overflow的两位创办人,真巧。)
网路安全
网路安全(network security)平时很容易被忽略,因为它费事费工,没有立即效益。但是对网路安全的轻忽,一旦出事,经常导致企业或政府重大损失。这让我想起以前当社 区管委会主委的时候,按消防法规要搞甚麽社区消防编组、演训,还要指派防火管理人,真的很麻烦。安全这种事情就是这样。
有些网路安全议题,是属于系统管理者的范畴,例如DoS (denial of service)、DNS spoofing、man in the middle;有些则是程序员的责任区,例如SQL injection、cross-site scripting、cross-site request forgery等等。此外像验证使用者身份的流程、储存/传送使用者敏感资料的方式,也都与安全有关。资深程序员对网路安全议题及常见攻击手法,应该要有 足够的认识与敏感度,并在开发过程中合理采取预防措施。
程序语言的多样性
程序语言是程序员吃饭的家伙,除了每天工作上用到的,资深程序员也应该接触一些不同的程序语言。例如:
- 函数程序语言函数程序语言(functional programming language)是另一种风格的程序语言,可以挑一个好好学一下。我个人推荐Haskell,但F#、Scala、OCaml、LISP、R、Erlang、Clojure这些也都不错,各有拥护者。实际工作上,不见得有机会使用这些函数程序语言,但好好学一种,可以拓宽自己程序设计的思路。而且现在很多程序语言,包括C++(C++ 11之后)、C#、Java(Java 8之后)、JavaScript、Python、Ruby、Swift等等,都具备一定的functional programming能力,可以运用在工作上。
- 组合语言除非是用C加assembly写硬体相关或compiler/toolchain的人,组合语言在实际工作中很少用到。但我觉得应该了解一下,因为 这是软件的最底层,再往下就是硬体了。我中学时候写过6502、8088,大学上过一堂MIPS组合语言的课,其实还蛮有趣的。写过组合语言,会让你对电 脑如何执行程序更有「感觉」。但是组合语言不用太认真学,因为真的很少用。学个概念、最多写几个小练习即可。
- Shell Script如果你工作中有用到Linux/Unix相关的OS,我建议应该要学一种shell script,例如bash。如果你是ops/service engineer或系统管理者,这应该是必备能力了,不过资深程序员最好也能懂这些。就像vi一样,有些东西已经很古老了,但网路世界就这麽运作著。没办 法在terminal环境工作的人,很多问题处理起来就显得笨手笨脚。
四、非关技术
除了专业技术能力,我再补充一些非关技术的心得。
克制砍掉重练的衝动
在开发过程中,程序员很容易对既有程序码产生一种「这谁写的?砍掉重练比较快」的衝动,包括我自己在内。我想可能的原因有:
- 砍掉重练其实比较容易(拆掉旧屋盖新屋很快,保留这面牆、那扇窗的反而更困难)
- 在自己的地盘当山大王很开心(人都喜欢按照自己的意思来)
- 在系统发展的过程中,很多需求后来才出现,使当初的架构显得捉襟见肘,但在当时其实是很合理的设计
- 上线多年来,程序员处理了很多状况、修复很多bug,因此程序显得没那麽乾淨优雅
- 写程序比读程序容易
- 文人相轻
- (排除以上各种因素之后)当初的程序码真的写很烂
不管怎样,砍掉重练(rewrite)的代价,通常比乍看之下高许多,而且日后维护你的程序码的人,心裡可能同样滴咕「这谁写的,砍掉重练比较快」。Joel Spolsky在2000年写的一篇 “Things You Should Never Do, Part I”,今日读来依然犀利。
小幅重构(refactor)是没问题的,而且可以经常做。
理解不同人的立场
当我们在某一方面懂得比别人多时,容易产生一种傲慢,技术人员也是。在专案开发的过程中,除了技术团队,还有产品/专案经理、主管、客户、使用者等 不同角色的人介入。在技术方面懂得比别人多,并不妨碍我们理解他人的立场。当我们能站在别人的角度看问题,常常一下子就能了解为甚麽事情会这样。例如:需 求改来改去、一开始不讲清楚、进度卡在别的team上面、「请你用最快方式完成」、「先支援这件紧急要求」、没人把我讲的话当一回事……
做到理解他人或是同理心(empathy),其实并不容易,因为每个人都有自己的立场,而人们倾向站在自己的立场看问题。我费了很大功夫,一直在努力修正自己技术傲慢的心态。如果你技术很厉害,又能做到理解别人,那真的很不简单,你所在的团队运作一定更为顺畅。
参与社群,吸收新知,写点东西
不管公司大小,资深程序员若只把触角侷限在公司内部,会越来越封闭。接触外面的社群、吸收专业领域的新知、写点东西累积自己的专业credit,会 让自己成长得更快。现在参与社群的管道很多了,从专业聚会研讨、开源码专案到各种社团,五花八门,不过还是得衡量一下自己能投入的时间。单身的人比较有 空,有家庭有小孩就只能斟酌参与了。
吸收新知是为了让自己保持在敏锐状态,不要变成灭绝的恐龙,但也不用太过焦虑。软件领域变化快速,各种语言、框架、技术不断冒出来,要追新知永远追 不完。如果你时间充裕,可以到处追新知,那很好。若你时间有限,我的经验是:等到很多人都在谈论的时候再去了解一下,也就够了。跟工作有关的,根据自己在 团队中的角色,适度学习即可。
另外,我建议有几年工作经验的程序员,都应该考虑写点技术文章,累积自己的专业credit。这种事情没有看起来那麽可怕,一回生二回熟,包括找主 题、写文章,多试几次就会上手。也不用给自己太大压力,一、两个月写一篇都可以,长短不拘,日积月累,会有收穫的。
程序员之后
写程序能够写几年?每个人情况不同,但无论如何,大多数人不会写一辈子。当了单兵程序员若干时日之后,最常见的角色转换就是先成为Tech Lead带组员(不同公司对这个角色有不同安排方式),此时除了写程序,还要负责带团队、对外沟通、掌控时程、照顾组员、处理突发状况等等。如果公司够 大,公司可能还会提供更多资深技术职位,例如Architect角色。
技术职之外,有的人会走管理职,有的人走专案管理或产品经理,甚至业务、行销都有;如果喜欢走技术,有的人会跳槽到条件更好、发挥空间更大的公司,其实选择不少。如果有心创业或加入创业团队,扎实的技术底子也会令你如虎添翼。
结语
最近学程序设计忽然变得很流行,「一小时学程序」、小学生学程序,好像程序人人都能写似的。当然练习写几行小程序是没问题,透过这些训练逻辑能力也 很好,但真实世界的程序,複杂度远远超出这些沙盒小练习。事实上,随著电脑及网路技术的发展,现在的软件开发比起一、二十年前更複杂了,有时我都很佩服现 在刚出校门的学弟妹们,要学的东西这麽多,他们是怎麽办到的。
洋洋洒洒写了一大篇,其实很多也只能点到为止。希望这篇文章,对大家有一点帮助!
本文文字及图片出自 tw.twincl.com
你也许感兴趣的:
- 一个32岁入门的70后程序员给我的启示
- 写给即将离开校园想成为一名程序员的童鞋们的几句忠告
- 每一位小白程序员都应学会的3项技能
- 想找份更好的编程工作应该学什么?
- 游戏中学会写代码:这些编程学习网站不容错过
- 如何教会非计算机专业的女友写代码
- 美国监狱新趋势——学编程
- 【译论】杰出程序员的秘诀
- 【外评】我是程序员,我很笨
- 【译文】别再装得像你很有名
翻译的也有点。。。:作业系统(OS,operating system)、杂凑表(hash table)、指标(pointer)