作为软件工程师,你希望从工作中获得的是:稳定的薪水、参与好项目的机会、好工作的跳板或只是和其他程序员成为好基友。这里的“高效”,我指的是按时完符合要求的项目的能力。经历过不少软件编写工作后,我相信以下实践会帮助你学会“高效”,同时提高专业声望、拉长职业寿命,和获得个人满足。
1. 理解你的需求
成为高效程序员的第一步是,保证时间的合理分配。没有什么比将时间花在完全没有前途的工作上更浪费的了。
尽快开工
尽快完成一个直观的系统。这意味着先创建界面,无论是程序界面还是用户界面,然后生成内部功能的存根代码(如果有必要的话)。这么做便于“客户”查看,通过执行用户界面或编写程序界面的代码,可以发现最初代码存在的矛盾或遗漏。甚至在第一次交付以前,你有可能会注意到问题或可改进的地方。
有一个经典观念认为,如果你提前设计好所有东西,那么之后你要做的就只剩写代码了。如果你之前做过完全相同的项目,那么这个说法当然正确。但如果不是,你很可能会陷入死角,也就是你只是在猜想或执行一个可疑的假设。
很早以前在一家无线网络的新公司工作时,我们开了两个月的会来设计一个将在6个月内发布的无线门户和网关。最终,我们厌烦了开会,开始编写代码。头两周内,我负责的部分与原始设计不符,两个月后的第一个无线连接测试表明,我完全误解了无线协议。
这不是说设计是没必要的。但在一定程度上,设计只是一种猜想。设计应该通实执行来确认,并且早执行总是比晚执行好。
即使原始设计是充分的,只要你发现可以调整的地方,你就要改进它。硬件产品、建筑和大型软件项目等会受到僵死的“预制”的损害,但对于软件,你可以在项目早期提炼项目要求,然后制作适合的界面。但是,这必须尽早完成。
尽早开工有利于树立你的职业形象。如果能向你的老板展示一些成果,他会很高兴的。另一方面,提早开工有助于缓解焦虑。
经常交付
一旦你完成一些可用的东西了,不要只是把它留作“概念实证”。要让其他人试执行它、看看他们的反应,然后让它指导开发过程的优先次序。观察人们如何使用你的软件,这是无可代替的方法。客户问卷调查和焦点研究可能会提供一些有用的意见,但有可能会让开发者的设计决定和特点被客户牵着鼻子走,这是一种冒险。
特别是要尽快将软件交付QA人员,经常提交成果,最好是按预定的时间间隔。让他们测试每天的成果,或至少是每周的成果也是好的。这会让QA人员觉得自己全程参与项目开发,从而培养职业责任感,更乐意发现和报告问题。最需要优先解决的是导致产品失效的问题,如崩溃或死循环——让问题尽可能涵盖多方面,熟悉整个产品,这样才有可能提早发现设计问题。
在一个小型3D软件公司,我负责移植从SGI出品的龙头产品到Windows NT。6个月后,移植没完成,倒有了崩溃的倾向,我很不情愿地将第一轮成果交付测试团队。幸运的是,因为漏洞太多,QA经理坚持要我立刻解决导致测试人员无法有意义地使用程序的问题。如果是我自己测试,我应该会忙于看起来更困难更重要的核心3D问题,可能会怠慢看来起比较普通的问题,如用户界面、载入-保存功能和与计划支持的硬件之间的兼容性。
程序员常常不想过早将代码交付测试人员——他们不想听到自己已经知道的漏洞;而测试人员极有可能不想测试基本上行不通的东西。但测试人员的工作就是找到这些问题。如果程序员想尽快看到成果的话,应该把漏洞报告当成好东西。
2. 把工作当真
将软件放在尽可能接近完工的状态下运行。你永远不知道你什么时候得演示系统、发送评估备份或甚至交付。
使用真实数据
如果你只用作为着冰山一角的样本数据作测试,那么,你的程序可能一撞上真实数据的大冰山就沉了。
我曾参与开发一种用于评估先进的半导体绝对值的供应链管理产品。跨过交付这道大坎后,我们接到消息说他们输入的第一批真实数据仍然在处理中——已经两天了。我同情主程序员,他不得不在管理人员和客户的催促之下忙活了两周。很高兴遇上这事的人不是我。
使用正式版本
记住,用你自己的机器创建的东西不是正式的。
在最近的一个游戏开发项目中,我负责用户界面,我陆续从QA那接到报告说有些颜色不对。最后,我发现问题只出现在交付版本中,另一位程序员使用专门的主机调试工具找到了漏洞。结果竟是一个我在两个月前犯下的愚蠢错误,没有指定初始颜色值。调试版本总是选择特定的默认值,但是交付版本会更改,最终结果是不太确定的。如果我注意经常地运行交付版本,我会立刻发现问题的,而不是损失大量的时间。
经常合并
及时将你的代码并入主代码库中——你拖得越久,这项工作就越累。
我曾与一名程序员共事,他觉得每天数据库中出现的所有新代码和数据变化都“很麻烦”。确实,这让所有其他程序员每天都要花一定时间合并,他才能够只扫视一下代码和数据就开始运行一些不错的独立样本。但每一次阶段性交付时,我们都要花好几天再次把单独的代码接到当前的代码库中,有时候甚至得拖延交付或冒着损失整个项目的风险。
将你的代码与正式版本分开意味着程序员不能评估你的代码,以及测试员不能尽早发现漏洞。可能你并不想其他人挑剔你的代码,但早发现问题总是比晚发现好的——所以,忍了罢。
3. 理解你的代码
生活中充满了奇妙的神秘,但你的代码可不适合出现这些神秘。你不必知道你的车怎么工作的——如果引擎发出奇怪的声音,把它交给汽车技师就好了。但换成是你的代码,如果连你都不知道它是怎么运行的或出了什么错,那就没人知道了。
有自己的编写风格
我童年时的钢琴教师是这么评价我和我姐姐哥哥的:“你姐姐的时间感强,你哥哥的键盘打得不错。”然后他停顿了一下说:“你嘛,嗯,你很努力。”
编程是一种有些人能做有些人不能做的事,但还有一些人则是天才。虽然我有过多年的练习,钢琴还是弹不好;虽然我那么喜欢打球,水平仍然一般般。但我确实认为我有编程和写作的天赋。不要吃惊,我认为好程序就像好散文。散文和代码都是文本,有语法、句法、拼写和语义。对于大多数写代码的人和写作的人,有这些就够了,但好作家和好程序员还要有一种美感,他们的作品在结构和风格上是有特点的,往往能借此识别出作者。
许多Windows程序员都感到好奇:为什么坏脾气的老Unix/Mac/Amiga/Lisp程序员对Win32/MFC/.NET很不满,但如果所有应用界面都来自Microsoft,你可能就不知道还有什么东西是更好的。
复制粘贴
风格化编程的反面是复制粘贴。从什么地方复制一些可能有用的代码,稍作调整,合并,重复,然后就大功告成了。你的软件简直就是大杂烩。
离开一家公司的几个月后,一位前同事电邮问我,他复制粘贴了十页的代码组成一个算法,为什么运行不了。我实在不知道怎么回答了。如果你不能解释你自己的代码应该是怎么运行的,你还指望谁来拯救你?
我甚至在诊断自己从样本代码复制粘贴过来的代码时也犯过难。从复制粘贴开始新代码是合情理的,但你不能因为看起来能运行就放手不管了——你得返回去看看你是否读懂了每一条,根据自己的目的理清代码。
清理代码
保持你的房子/公寓/房间整洁的最好办法就是每天花一点时间清理它,或至少每周清理一次吧。如果等到住所乱到一定程度才打扫,那么这麻烦就非常大了。除非你雇个清洁工。
假设你没办法奢侈到雇一个人每天帮你清理代码的程度,那么你就应该定时地检查你的代码、清理累积的死代码、淘汰过时的注释和错误的名称,否则你必定会得到一份不敢拿出来见人的代码。如果你不觉得丢不起人,好吧,你行。
我指导过的一名程序员总是向我报告,她的代码“完成”了。这是管理者乐意听到的话,却让我非常抓狂。她的代码从来没有做完——你得调试它、维护它、改进它,直到它彻底没问题。
问题?注释?
有些人认为编程是一门手艺活,也有些人认为编程是一项工程。更经常的是,它是一门考古学。你挖掘代码的沉积物,想知道这些奇怪的人工产品是用来干什么的。为后来人着想一下,留点线索吧。
我问之前提到的那位程序员“完成”注释了没有。结果是,一个函数名称为“GetData”的注释居然是“Gets data”。这不只是废话——简直是侮辱。什么数据?什么格式?来自哪里?更不要提像服务器不可用或传送中断时会怎么样这种小细节了。
将你的代码做成文档,以防有人随时要拿来用。可能要用的人就是你本人——想想如果不这么做,你得重新访问代码多少次啊?
与之前的一个老板合作时,他叫我浏览一段没人有时间看的代码。一开始,我认为它很糟,不知道写的都是什么东西。之后我慢慢摸索出来这段代码是干什么的,所以我勉强同意它不算太糟。最后我终于认出这货竟是我两年以前写的。教训:多留点注释。
当你写代码时,记得注释,而不是等着出现什么方便的清理短语——注释你的代码,让它甚至可以清楚地反映你在编写时的想法。你可以成为自己的编写伙伴。
现在你可以用javadoc和doxygen等生成漂亮的HTML或来自源代码注释的其他格式化的文件。理想的情况是,你每天晚上做的就是doc生成的部分,可以通过你的内联网获得。
注意警告
无视编辑器和运行时间警告会害到你自己。有“警告”就有原因。
我曾做过一个基于Unix的应用,它不能成功地连接某些函数。我们通过在运行时再次连接这些函数解决问题。六个月后,当我们执行一个干净的新版本时,我们才发现原来我们关掉了能提醒我们未知连接漏洞的警告。在供应商的斥责下,我们将连接问题解决了。但结果是,原来我们只要通过重新排列库就能连接上了。
提高编辑器的警告水平,注释代码以及记录创建和运行时间的警告信息,最好包括解决警告的标准,这样你就会知道是否解决问题或忽略问题。
4. 优化编程
带着目的写代码
复制粘贴代码的人的另一个极端是,只是为了让代码看起来更漂亮(至少对他们而言)而改变代码。虽然有编程审美感是值得赞扬的,但改变代码以便让你觉得漂亮只是浪费时间(无用的冒险)。浏览并改变别人写的代码,让它看起来更漂亮,真是让人生气。
我有一个挑剔的同事,浏览我们的代码库时将所有的附加语都删除了。如果他只是清理了入门级员工写的代码,那可能没人会说什么,但那些附加语是我们团队的技术领导写的,他可是我们公司最出色的人物之一。
不要搞破坏
“代码重构”现在十分流行,但程序员往往以为它是指代码清理或重新设计。这个技巧是指重新组织代码,同时不破坏其他东西。如果你以改进的名义破坏已经存在的功能,那么你的意思就是:要么你的时间比其他人的时间金贵,要么你不破坏就不会整代码。
我有一个特别讨人嫌的同事,他决定重新执行我们系统中的解析器,但结果让代码变成其他所有人都不知道怎么写了。我让他恢复原状,之后发现代码能编写了,却不能运行了—–问他怎么回事,他说“应你的要求”,他移除了整个解析器。真没团队精神。
保持代码运行需要一些耐心和额外的工作——你勤奋地回归测试你的工作,在将函数添加到新代码中时,你可能需要暂时留住老代码和界面。但对于所有与这个代码库有关的人来说,这是必须做的。
找到瓶颈
人们总是谈论“最佳”,但这不是一个正确的词。我们极少将最佳作为目标——相反的,我们的目标是改进和权衡以达到足够好的表现。
在谷歌的电话面试时,我被问到如何在一组有序的数字中搜索某个数字。显然,提问的人是在问二进制搜索法。但在现实生活中,我可能会做出“错误”的选择——从头找到尾。如果程序表现足够好了,还花两倍的时间写两倍的、必须维护和调试的代码,那是毫无意义的,特别是如果那段代码并非程序的瓶颈(我严重怀疑如果那个数据是瓶颈部分,你居然还会将它线性排列)。
如果你确实需要在程序的速度或空间方面达到最佳,折腾除了瓶颈以外的其他任何部分都只是浪费时间。
5. 自我管理
你可能对你那位讨厌的老板有各种抱怨,你的抱怨可能没错。所以你得成为你自己的管理者。即使你的老板人不错,他也不会站在你背后告诉你该写什么、怎么写才会快(尽管我肯定许多老板恨不得这么做)。
估计时间
程序员不能提供有用的时间估计,这是出了名的。但我认为这是无理指责,因为管理层往往作出更差的预测,并且程序员的警告往往被无视(这可能是所有工程的共同灾难)。但是,合理的时间估计对于按时完成项目仍然是关键的。
在一个商业软件项目中,我的有些同事居然乐得忘了产品交付日期——有人问是否已经交付了,另一个人才很惊讶地发现,日子已经过去好几天了。
更糟的也更普遍的是,程序员能给出的时间估计是“只需要几天。”每次我听到这话,或者我自己说出这话,我都感到害臊。
一家图像软件公司的总裁想让产品支持VRML(那时它是下一件大任务),包括我们将在两个月内发行的产品也支持VRML。他可能想到(他是正确的)我会拒绝开始新项目,所以他问了另一个工程师,得到了他想到的回答:“只需要几天。”两天后,我告诉总裁,我们刚刚浪费了他和我的两天时间,因为有两百多个更重要的漏洞要修复,他认为我的理由算是充分。(后话:VRML没有太成功。)
另一位程序员完全没有时间估计的概念。但没有必要完全拒绝时间的模糊属性——毕竟只是估计,事实上你应该避免太确切。如果你是一名有经验的工程师,你就知道你以前做类似的工作需要多长时间,如果你不是,那你就问问有经验的人。
我有一个聪明的朋友,经常被指派去开发实验原型,他问我:“你怎么估计时间?”我认为这是一个反问句,但甚至纯研究人员也要估计时间。有人支付他们,希望得到结果,即使它是许多演示样本或某段时间发表的文章。
如果你确实估计不准需要多少时间,那么你就不是做这项任务的合适人选。
有时候程序员不情愿承诺时间是因为他们害怕保证。确实,这个世界没那么美好,经理会在时间上跟你讨价还价,竞争对手可能用严苛或不切实际的安排来挤兑你,希望你失败。在你承诺时间后,你就悲剧了,你别想得到任何你希望的结果。
我曾有个老板问完成时间后会追问一句:“你保证?”但问他硬件条件和其他相关事宜时,他会说:“我尽量。”
我能说的只有,抓紧时间以及给出现实估计。任何让步都应该根据实际的介于产品和资源之间的交易。要根据假设、相关事宜和资源做时间安排,找个地方写下来,这样以后你就不用麻烦你不太给力的记性了。
计划进度
在决定上哪去以前,你不会跳上车的,对吧?你在开车时心里可能就有路线了。相同地,在你开始用电脑写以前,你应该知道你今天想完成什么,有一些想法了。
每天都会遇到分心的事,所以你不可能总是完成你想完成的事。与那些将软件工程团队当作自动贩卖机的人的想法相反的是,有些任务不是一天就能完成的。所以想想你到周五要完成什么,如果你完成了,那么周末你就可以好好过了。
6. 不断学习
一名社团足球队成员曾经问我,我们每天束紧防滑钉练习,你们“C语言编程的秘密是什么?”如果存在这样的秘密的话,我肯定会在晚间电视节目上宣传如何靠房地产发财。对不起,没有捷径——你必须学习、练习和犯错。你不一定得依靠团体训练或学校教育——有许多国立的和当地的专业团体、书籍,当然还有网络。
编程是科学
编程被称作“计算机科学”是有原因的。无需正规的计算机科学教育,任何人都可以轻易地开始编程(可能太容易了)。特别是,那些学过其他工程和理科的人,可以非常快地上手编程,然后以此谋生。但对于高效地处理重大任务,你必须知道软件的固有功能和限制、识别前提,这样你才不会白费力气地做重复的工作。你不必知道所有事,但你应该至少粗略地了解许多领域,必要时能做一些额外的研究。
例如,创建了新文件格式的人应该知道一些关于编辑器的事。我不是指所有代码生成的优化如循环展开,而是基本的问题和各种编辑的短语和大部分指定标记和语法的重要性。今天,大多数人会默认地使用XML,那是件好事,
但在那之前,一般是粗略地写一些文本格式,指向一些生成的样本文档作为文件,之后其他写了另一个解析器的人会补上一些在文档中阅读的东西,但不是全部。在出了差错的情况下,你有两种方式推卸责任——要么读者不行,要么作者太差。无论怎么样,更受欢迎的产品会赢。
我对3D图象行业最不能容忍的事情之一是,过多的文件格式不明。当我执行一个3D作品的OBJ文件解析器时,我测试的每份导出作品都生成明显不同的文件,比如空白和换行不同。与之形成对比的是,我的一个初出茅庐的同事用语法和词法分析器设计了一个新游戏交换格式(现在,这不再是什么大不了的事了—-大多数新图象文件格式好像都是基于XML的)。
只会将简单的脚本和用户界面放在一起的程序员和可以处理实际问题的程序员,如果说这二者有什么区别的话,那就是对复杂计算的理解能力,如算法怎么影响问题的大小。每一位程序员都应该知道基本的复杂性术语和对常见问题的复杂程度有常识性认识。
我的第一份工作是计算机辅助半导体设计,涉及许多可扩展性的问题,包括一些NP-complete问题(非常难处理)。但是,每次看到在线性时间中不能解决的问题,和我们自夸可能意味着大部分是线性时间的“线性”算法,有些工程师会兴奋地说:“这是旅行商问题!”(旅行商问题,即TSP是一个有着重要工程背景、在图论中的典型组合优化问题,已被证实是一个NP完全问题。也就是,如果一个旅行商不得不到几个城市做生意,怎样走最短的路线使他一次到达这几个城市。)
免费啤酒、自由讨论、免费软件
好吧,其实没有免费啤酒;但现在程序员过得还不错(尽管经济衰退和外包业惹争议)——毕竟你需要的东西网上教程、讨论组上都有,还有免费软件可以用。你要解决的只有硬件和宽带问题。
7. 尊重
高效软件工程师的要求之一是,被认真对待。你必须得到你的同事和老板的尊重,至少出于你的技术能力、对自己的工作有主导权、对他人有一定影响力。
愚蠢问题
真的,这个世界上存在许多愚蠢的问题。提出一个聪明的问题会增加别人对你的尊重,但这是一项技术活。一个揭露未解决的事的好问题会让别人看到你深刻的内涵,你敏锐的思维。要求说明关于技术参数的问题,显示了你阅读和发现问题的能力。
如果你的问题没有得到答案,可能是问题本身有误,所以不要再重复发问了。换一种方式提问,带上更多细节或背景。如果被提问的是你,或花时间回复新手问题的是你,你会感谢上述考虑的。
能与技术支持人员保持良好关系,这是让我对自己都感到骄傲的事。但我确实记得一件往事,那时我抛出一个问题:“几周前提出来的那个问题是怎么回事?”你可以想象别人是多么恼火地回答——“你说的怎么回事是指什么,并且,你说的是什么问题?”
粗鲁无礼是没有回报的,特别是如果你是要求免费指导或咨询讨论组。即使你是在支持协议的保护之下发问,激怒了你的技术顾问对长期合作也会很不利。
我曾经向臭脾气的新人们解释为什么他们的问题有问题或者什么是他们从一开始就做错了的,真是太累人了。现在,我给你快速生效的傻瓜过滤器——“我想知道的只是……”或果断无视。
让所有人知道你读了文件和谷歌搜索了该问题。除了避免回复必然的“RTFM”(RTFM意为:去读该死的指导手册。当你需要信息或者解决问题时,在请求对方帮助之前,应该花一些时间尝试自己去寻找需要的东西。)和“Google is your friend”,都显示了你做足了功课,那些帮助的人不必搜索相同的资源。如果你确实指望他们为你搜索那些资源,那你的意思就是,你的时间比他们的金贵,你在谋杀他们的时间。
白痴答案
如果你要表现得你知道自己在说什么,那么你确实应该知道你到底在说什么。工程师的交流有时候更多地是炫耀自己的知识而不是提供信息(如果你也能这么做,那我向你致敬)。这往往无益于求职面试,面试官其实是假借“发现你是怎么想的”的幌子,向求职者抛出空洞的问题。当然,如果求职者有一点自知之明的话,也可能产生出乎意料的结果。
有一位技术总监打电话面试我,要我概述C++编辑的结果堆栈框架,并且口头答复他。我一步一步地打草稿,每次我给他正确的答案,他都反过来要我说一个错误的答案,以便我们可以仔细检查为什么那个选择不管用。我不知道我这么写是不是在彰显我有多聪明或他有多聪明。
作为一名工程师,你不能太倚重钱财和长相——信誉才是你的资本。所以如果你犯错了,就坦率承认吧。
我有幸与一名资深工程师共事,他从来不犯错。当他的Java代码在多重处理器系统中崩溃时,原来是出现了大漏洞。当我拿代码指出UI代码不支持多线程运行时,他坚持说只有一个线程。当我列出代码中的7条线程(我能找出的)时,他同意不应该保留这么多线程,并且最好修改一下。但他还是按老样子编写代码——他没有修复任何漏洞,他只是用更多代码掩盖了漏洞。
最后,一个节省时间的建议:不要纠结于愚蠢的争论。愚蠢是会传染的。