程序员的制胜技

978-7-115-61156-7
作者: [土耳其] 塞达特·卡帕诺格鲁(Sedat Kapanoğlu)
译者: 谈楚渝
编辑: 李瑾

图书目录:

详情

本书专注于介绍项目开发领域的实战方法和高效范式,共 9 章,从预备理论知识开始,按照业务开发的真实流程详细阐述了以往开发的经验误区,并结合实际的.NET 和 C#代码,给出经过大量项目检验的解决方案。 本书绝对不是市面上随处可见的技术手册。作者用他独特的幽默感和数十年的软件开发经验,将软件开发的实战故事一一道来。 正如作者所言,无论你是非科班出身的开发者,还是已经入行几年的开发“上道人”,本书都能对你有所裨益。

图书摘要

版权信息

书名:程序员的制胜技

ISBN:978-7-115-61156-7

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。

版  权

著    [土耳其]塞达特·卡帕诺格鲁(Sedat Kapanoğlu)

译    谈楚渝

责任编辑 李 瑾

人民邮电出版社出版发行  北京市丰台区成寿寺路11号

邮编 100164  电子邮件 315@ptpress.com.cn

网址 http://www.ptpress.com.cn

读者服务热线:(010)81055410

反盗版热线:(010)81055315

版权声明

Original English language edition published by Manning Publications, USA. Copyright © 2022 by Manning Publications. Simplified Chinese-language edition copyright © 2023 by POSTS & TELECOM PRESS CO.,LTD. All rights reserved.

本书简体中文版由Manning出版社授权人民邮电出版社独家出版。未经出版者书面许可,不得以任何方式复制或抄袭本书内容。

内容提要

本书专注于介绍项目开发领域的实战方法和高效范式,共9章,从预备理论知识开始,按照业务开发的真实流程详细阐述开发中的经验误区,并结合实际的.NET和C#代码,给出经过大量项目检验的解决方案。

本书绝对不是市面上随处可见的技术手册。作者用他独特的幽默感和数十年的软件开发经验,将软件开发的实战故事一一道来。

正如作者所言,无论你是非科班出身的开发者,还是已经入行几年的开发“上道人”,本书都能对你有所裨益。

译者介绍

这里本该有一份详细、绝妙的译者介绍,

可惜空白太小了。

译者序

说来奇怪,在翻译工作还没完成,甚至是刚刚开始的时候,我就在心底写好了译者序。可当心里的腹稿变成文字的时候,如释重负的感觉当然有,但更多的感觉是,这一刻我等待得太久了。

可临了给自己一个表达的空间时,忽又觉得千言万语,停于笔端。全部的念想都只化为希望自己所做的这些工作能够对得起这些文字,对得起读者,就足够了。至于更多的话,也只是对原作的无关注脚而已。

我一直很喜欢王小波的一句话:“一个人只拥有此生此世是不够的,他还应该拥有诗意的世界。”对我来说,翻译,以及了解新领域的开发知识,就是在向这个诗意的广阔世界前进。希望读者通过阅读、学习、工作也同样能够到达这个世界。用本书作者的话来说,就是“in zone”。

本书介绍的相关经验和知识都是作者由真实开发工作得来的,是接地气的,是为了尽可能高效解决问题而存在的。有些观点的激进程度或许会超出你的想象,例如推荐你重复造轮子、不要使用注释、不要修复bug等(一系列初看必定能在社区里激起波澜的标题),但相信我,去仔细看看相关标题下的文字,你一定会有一种拨云见日的感觉。期待你大呼过瘾。

在你开始阅读本书之前,我还有一些提醒要给到你。由于语言环境的差异,本书存在大量的英语语境的“包袱”和引用。这些东西在中文互联网中或许早就有中文演绎的版本,可能让你猛然发现,原来其出处竟然是在这里。对你来说这或许是一个全新的发现。希望读者在阅读的时候,能够善用搜索工具去了解其相关背景知识。编程语言本身就是一种舶来品,勇于进入英语语境的圈子里,对你的开发之路会大有好处。也希望我能借本书带你开启一扇你之前无暇顾及的门。

本书的翻译风格尽可能地还原原著的文风,有些内容为了追求还原,可能会较贴近于直译,这实属妥协之举。

耐心看完本书,你一定会有所收获。

感谢我的家人,在我翻译本书的这段时间里,能够体谅我的繁忙,在许多我想要拖延的时刻,给予我鼓励,让我继续下去,谢谢你们。

感谢人民邮电出版社编辑李瑾、郭媛,没有你们的细心审校和耐心沟通,翻译工作的推进不会这么顺利,翻译成果也不会有保障。

感谢杨源、夏世桐、binghe2402,感谢你们点睛的翻译修改意见。

由于译者翻译经验和水平有限,译本难免存在疏漏及不妥之处,敬请各位读者斧正。

你可以通过微信hacksamurai找到我,期待与你的交流。

谈楚渝

作为非科班出身的程序员,我尝试过(除读书之外的)各种方法让自己变得更专业。例如,随意塞几个数字到内存中,看看除了计算机运行中止之外还会发生什么;在烟味弥漫的办公室里熬夜加班;高中时,偷偷在大学实验室工作,直至午夜才溜出校园;阅读二进制文件,只盼着那些字节码能让我醍醐灌顶,理解代码如何运行;文档缺失时,背下操作码,挨个尝试可能的参数组合,寻找某个函数的正确输入。

2013年,我的朋友阿齐兹·凯迪(Aziz Kedi)在伊斯坦布尔开着一家书店,他约我写一本书,内容是我的软件开发经验。那是我第一次考虑写与我专业相关的书。但没过多久他就关了书店,搬去伦敦生活,于是我也只能作罢。

但我一直在想:我可以给那些刚入职场的新人写一本书,让他们能够在拓宽视野的同时,弥补经验不足。新人对于软件开发的理解往往来自他们学过的课程、既有观念和他人的实践经验。新入行的程序员自然会视之前积累的知识为核心投资,不舍得弃而不顾。

某一天,我决定慢慢写这么一本书。我给这本还没写出来的书命名为Street Coder,并开始写下那些能帮助新手开发者工作更轻松的零碎点子。这些点子不必是最佳实践——只要你不反对,它们甚至可以是糟糕实践。只要能帮到开发人员更好地思考面临的难题就足够了。文档里写了不少内容,后来我就将它抛诸脑后了。不过有一天,我接到一通来自伦敦的电话。

这次不是阿齐兹·凯迪。他大概正忙着写剧本吧。我相信我写这段文字时,他也还在写另一套剧本呢。来电的是Manning出版社的安迪·沃尔德伦(Andy Waldron)。他问我:“您想写本什么书?”一开始我没什么想法,于是打算反问他,给自己争取一点时间:“您是怎么想的?”我咕哝了几句,然后忽然反应过来,想起之前我做的那些笔记。

本书内容来自我在专业软件开发世界中学到的东西。踩过许多坑之后,我获得了一种务实、接地气的观念——把软件开发当作一门手艺来对待。本书传达了我因这些经历而产生的变化,应能帮助你取得事业上的领先优势。

致  谢

没有我的妻子京于兹(Günyüz),就不会有本书。在我忙着写作的时候,她挑起了所有担子。谢谢你,宝贝,我爱你。

感谢安迪·沃尔德伦,是你激发了我的创作热情。这段创作经历算得上有意思。即便我曾经因为你偷偷溜进我家改我的稿子而指责你,但你的确很有耐心且善解人意。我欠你一杯酒,安迪。

感谢我的内容编辑托尼·阿里托拉(Toni Arritola),我所有关于编程图书的写作知识都是从你那里学来的。感谢贝姬·惠特尼(Becky Whitney),你总是很有耐心,就算对我书中那些一团糟的部分也能保持好脾气。我得申明,那些都是安迪写的!真的!

感谢我的技术审校弗朗西丝·布翁滕波(Frances Buontempo),你给我的技术反馈是那么到位。同样也感谢奥兰多·门德斯·莫拉莱斯(Orlando Mendez Morales),是你让我敢对书里的代码能够正常运行“打包票”。

感谢我的朋友穆拉特·吉尔金(Murat Girgin)和沃尔坎·塞维姆(Volkan Sevim)。本书还未成形的初稿是你们最早帮我审校的。记得你们还向我保证:如果读者认识我的话,那些书里的笑话一定能逗得他们笑出声来。

感谢计算机科学家唐纳德·克努特(Donald Knuth)允许我在书里引用你的作品。能得到你的回应,哪怕只是一个“好”字,我也倍感幸运。同样还得向弗雷德·布鲁克斯(Fred Brooks)说声谢谢,是你提醒我版权法里有合理使用的相关条款,不用为此每天打电话来请求许可,也同样不需要在凌晨3点闯入你家,把警察都招过来。哎,别急,弗雷德,我这就走!也感谢莱昂·班布里克(Leon Bambrick)让我没顾虑地在书里引用你的文字。

感谢MEAP的读者们,尤其是吉哈特·伊玛莫格鲁(Cihat Imamoglu)。虽然我跟你没有私人交情,但你还是给了我这么多深刻的反馈。当然,也感谢Manning出版社参与本书出版的编辑们:Adail Retamal, Alain Couniot, Andreas Schabus, Brent Honadel, CameronPresley, Deniz Vehbi, Gavin Baumanis, Geert Van Laethem, Ilya Sakayev, Janek LópezRomaniv, Jeremy Chen, Jonny Nisbet, Joseph Perenia, Karthikeyarajan Rajendran,Kumar Unnikrishnan, Marcin Sęk, Max Sadrieh, Michael Rybintsev, Oliver Korten,Onofrei George, Orlando Méndez Morales, Robert Wilk, Samuel Bosch, SebastianFelling, Tiklu Ganguly, Vincent Delcoigne, and Xu Yang。本书因为有你们的建议才变得更好。

最后,我想感谢我的父亲,是你教会我“自己制作玩具”。[1]

[1] 这里的“玩具”可以理解为一种象征,表示解决问题、创新和自主探索的能力。—译者注

关于本书

本书力图通过解决有名的范式、讲解反模式、提供一些看上去不是那么完美但依然能在实际工作中大有用处的实践经验等方式,填补软件开发工程师专业经验之不足。本书的目标是用质疑和实践的思维模式武装你的头脑,让你懂得,除了上网搜索和动手输入之外,创造软件还需要付出其他代价。除此以外,本书还会介绍一些能让你节省时间的日常操作。总的来说,本书的目标是成为固有观点的“破局者”。

谁该读本书?

那些非科班出身、还需要加深对软件开发范式和最佳实践理解的编程新人,以及有几年经验的程序员,是本书的目标读者。本书例子使用C#和.NET,熟悉它们会对你的阅读有所帮助,但是本书会做到尽量不受编程语言和框架的影响。

本书框架:路线图

第1章介绍了实战程序员的概念,即具备专业经验的开发者,并阐述能帮助你成为这种人的要素。

第2章讨论了理论在实际软件开发中的重要性,以及你为什么应该关注数据结构和算法。

第3章阐述了在许多情况下,有些反模式或坏实践实际上有其用武之地,甚至在很多情形下比其他方案更为适用。

第4章介绍了单元测试的神秘世界。虽然看起来它在项目开始时才显得比较有用,但确实能帮你减少代码量和工作量。

第5章讨论了重构技术,探讨如何轻松安全地进行重构,以及何时避免重构。

第6章介绍了一些基本的安全概念和技术,并展示了针对最常见攻击的防御措施。

第7章展示了一些行之有效的优化技术,建议过早优化,并提供了一种解决性能问题的有效方法。

第8章介绍了使代码可扩展性更强的技术,讨论了并行机制及其对性能和响应能力的影响。

第9章介绍了处理缺陷和报错的最佳实践,具体表现为鼓励不处理报错,并给出了编写容错代码的技术。

关于本书代码

本书包含的大部分代码用于说明概念,所以可能缺少在实际项目中的实现细节。异步社区提供了几个项目的完整代码,你可以在本地运行它们并进行试验。其中有一个例子比较特别,因为这个例子迁移自.NET框架。对于特定项目,在非Windows操作系统的计算机上可能无法直接创建。本书对其他平台的替代解决方案文件同样放在配套资源中,因此,请放心,正常运行代码没有问题。

本书包含许多源代码的例子,有些是列出编号的代码清单,有些是普通文本。在这两种情况下,源代码都用等宽字体,这样可以将其与正文分开。有时,部分代码会被隐藏起来,以突出显示与前面步骤不同的代码,例如当一个新功能被添加到现有代码行时。在许多情况下,原始源代码已被重新格式化。我们添加了换行符并重新修改了缩进,以适应书中可用的页面空间,但在极少数情况下,即使这样做空间也不够,便在清单中使用行延续标记(➥)。此外,在文本中描述代码时,源代码中的注释通常会从代码中删除。部分代码清单有注释。

关于作者

塞达特·卡帕诺格鲁(Sedat Kapanoğlu),一名自学成才的软件开发工程师,来自土耳其的埃斯基谢希尔。他曾入职美国华盛顿州西雅图的微软公司,担任Windows核心操作系统工程师。

塞达特是其家中5个孩子里最小的。他的父母是从南斯拉夫移民到土耳其的波斯尼亚人。

塞达特创建了土耳其最受欢迎的社交平台——酸字典(Ekşi Sözlük)。在20世纪90年代,他活跃于土耳其的demoscence,这是一个国际数字艺术社区,其主题是利用代码生成图形和音乐。

关于封面插图

本书封面人物头像插图的名称为“Lépero”,意思是“流浪汉”。这幅插图摘自《墨西哥的民事、军事和宗教服装》(Trajes civiles, militaresy religiosos de México),这本书出版于1828年,作者是克劳迪奥·利纳蒂(Claudio Linati,1708—1832)。利纳蒂是一位意大利画家和石印师,他在墨西哥制作了第一台石印印刷机。这本书描述了墨西哥社会的民事、军事和宗教服饰,是最早印刷的关于墨西哥的彩版书之一,也是第一本由外国人编写的关于墨西哥人的书。该书包括48幅手工着色的石版画,并对每幅版画进行了简要描述。书中丰富多样的画作生动地提醒我们,200年前,墨西哥的城镇、村庄和社区在文化上是多么与众不同。那时人们彼此隔绝,说着不同的方言。无论在城市还是乡村,只要看人们的衣着,就很容易看出他们住在哪里、从事什么职业及过着什么样的生活。

后来,着装方式发生了变化,而当时那种非常丰富的地区多样性也逐渐消失。现在从着装上很难区分不同大陆的居民,更不用说那些不同城镇或地区的居民了。也许我们用文化的多样性换来了更加多样化的个人生活——快节奏的科技生活。

在计算机图书封面千篇一律的时代,Manning出版社用图书的封面,通过像这样的收藏插图重现了两个世纪前各地精彩、丰富的生活,来表达计算机行业的创造性和主动性。

资源与支持

资源获取

本书提供如下资源:

本书源代码;

本书思维导图;

异步社区7天VIP会员。

要获得以上资源,您可以扫描下方二维码,根据指引领取。

提交勘误信息

作者和编辑尽最大努力来确保书中内容的准确性,但难免会存在疏漏。欢迎您将发现的问题反馈给我们,帮助我们提升图书的质量。

当您发现错误时,请登录异步社区(https://www.epubit.com),按书名搜索,进入本书页面,点击“发表勘误”,输入错误信息,点击“提交勘误”按钮即可(见下图)。本书的作者和编辑会对您提交的错误信息进行审核,确认并接受后,您将获赠异步社区的100积分。积分可用于在异步社区兑换优惠券、样书或奖品。

与我们联系

我们的联系邮箱是contact@epubit.com.cn。

如果您对本书有任何疑问或建议,请您发邮件给我们,并请在邮件标题中注明本书书名,以便我们更高效地做出反馈。

如果您有兴趣出版图书、录制教学视频,或者参与图书翻译、技术审校等工作,可以发邮件给我们。

如果您所在的学校、培训机构或企业,想批量购买本书或异步社区出版的其他图书,也可以发邮件给我们。

如果您在网上发现有针对异步社区出品图书的各种形式的盗版行为,包括对图书全部或部分内容的非授权传播,请您将怀疑有侵权行为的链接发邮件给我们。您的这一举动是对作者权益的保护,也是我们持续为您提供有价值的内容的动力之源。

关于异步社区和异步图书

“异步社区”(www.epubit.com)是由人民邮电出版社创办的IT专业图书社区,于2015年8月上线运营,致力于优质内容的出版和分享,为读者提供高品质的学习内容,为作译者提供专业的出版服务,实现作者与读者在线交流互动,以及传统出版与数字出版的融合发展。

“异步图书”是异步社区策划出版的精品IT图书的品牌,依托于人民邮电出版社在计算机图书领域30余年的发展与积淀。异步图书面向IT行业以及各行业使用IT的用户。

第1章 初入行当

本章主要内容:

行业现状。

谁是实战程序员?

现代软件开发面临的问题。

如何用实战手段解决你手头的难题。

其实我真的够幸运,在20世纪80年代就编写了我的第一个程序。我只需要打开计算机,花1秒钟时间,写两行代码,输入RUN。哇!整个屏幕铺满了我的名字。我立即被代码的威力震撼到了。两行代码就这么厉害,如果我写4行、写6行,甚至写20行代码呢!多巴胺在我9岁的大脑里喷涌,横冲直撞。就在那一刻,我想我算是迷上了编程。

如今的软件开发比当初复杂几个数量级。20世纪80年代,用户交互简单到只有“按任意键继续”,而且人们时不时还会纠结“任意键”在哪里。没有窗口设计,没有鼠标,没有网页,也没有用户界面设计,没有什么库、框架、内存管理,更别提移动端设备了,统统都没有,有的只是命令集和不能改动的硬件配置。如今这种“质朴”已不复存在。

我们建立这些层次的抽象是有原因的,没有人是“受虐狂”(Haskell[1]程序员除外)。我们如是安排那些抽象层级,因为只有这样做才能达到当今软件标准的要求。编程不再只是在屏幕上铺满你的名字,还得用正确的字体写出名字,而且放在窗口里,让它能够随着窗口被鼠标拖拽、调整大小。你的程序还得有“颜值”,复制和粘贴功能也不能少,说不定还得把要显示的名字存储在数据库甚至云端。在屏幕上铺满你的名字再也没那么有意思了。

[1] Haskell是一门很深奥的编程语言,它融入了许多仍处于学术论文阶段的特性。

还好,我们有办法去应付这种复杂性:读大学,参加黑客马拉松比赛,参加训练营,学习网课,使用小黄鸭调试法。

提示

小黄鸭调试(rubber duck debugging)法,算得上一种“高深秘术”,你跟一只“小黄鸭”聊你写的程序,从而找到编程难题的解决方法。关于这种方法的详情,我会在之后的调试章节中详谈。

我们得充分利用这些方法,但是想要仅仅依靠这些方法,就能在竞争激烈、标准严苛的软件开发行业站稳脚跟,还是差点意思。

1.1 在实战中,什么最重要?

专业软件开发行业还是相当神秘的。你打电话催甲方付款,催了好几个月,他们每次都会信誓旦旦地说过几天就给你;有的老板一分钱都没给过,还拉着你“画饼”说一赚到钱就会付给你;当你操起调试器时,有些bug却消失无踪了;有的团队根本不用源代码管理工具。对,就这么吓人。但你不得不面对这些现实。

毋庸置疑的是,工作产出相当重要。通常没有人会真的关注你的那些优雅设计、精妙算法,或者是高质量代码。他们关心的只是你能在规定的时间里出多少活。其实,良好设计、精妙算法和优质代码可能会为产出带来极大的正面影响,很多程序员都没意识到这点。相反,这些事情常会被程序员当作追赶截止日期(deadline,DDL)路上的“绊脚石”。这种思维会让人变成一个得过且过、戴上了脚镣的僵尸。

不过还是有人真的关心你的代码质量的——你的同事。他们才不想优化、维护你的代码,只盼着你的代码能够运行,并且容易理解、维护简单。这是你的分内之事,因为当这份代码被提交到存储库后,就成了所有人共有的代码。团队的总产出要比团队中的任何一个人的产出都重要。如果你写的代码很差劲,就是在拖整个团队的后腿。你的糟糕代码会影响整个团队,拖慢团队进度,甚至可能毁掉整个产品,最后这个“胎死腹中”的产品会成为你职业生涯中的污点。

从零开始写代码时,首先要有一个粗略的想法,其次是设计。这也是为什么说设计非常重要的原因。好的设计不一定非得摆在台面上,也可以保存在你的脑海里。你总是会遇到这样的人:他们对设计这件事不屑一顾,由着性子去写代码。对于这些人,我只能说他们不懂得珍惜自己的时间。

好设计模式或好算法能提升你的产出。不能提升产出的东西就是没用的东西。几乎一切都可以被赋予货币价值,所以你的一切行为都能够用产出来衡量其价值。

你当然可以用糟糕代码来获得高产出,但仅限于项目不需要进行产品迭代的情况。当你的客户想更改需求的时候,你却只能维护“屎山”(糟糕的代码)。在后文,我会举一些相关的例子,这些例子能让你在给自己“挖坑”时幡然醒悟,然后悬崖勒马。

1.2 谁是实战程序员?

微软在招聘时通常招两种人:计算机科学专业的应届毕业生和在软件开发领域拥有丰富经验的业界专家。

自学成才的程序员也好,计算机科学专业出身的人也罢,都常常在自己的职业生涯伊始忽视了一件事——行规,即某个行业里最重要的知识精华。非科班出身的程序员虽然之前可能会有一些试错经验,但是总归缺少理论学习和理论在日常编程工作中的正确运用经验。而大学毕业生,虽然理论知识丰富,但是缺乏实践,有时也缺乏对自己所学知识的质疑态度。这两种人开展事业的不同途径如图1.1所示。

图1.1 开展事业的不同途径

在学校里学习的那套体现不出正确的优先级。在学校里,你不是根据知识的重要性来学习,而是跟着人家设计的路径来学习。你不会知道,某些知识在竞争激烈的行业中会有多大用处。时间不等人,按部就班地顺着时间线学习是不现实的——“黄花菜都凉了”。即便是使用世界上最好的框架,但只要有一个bug,你一整个星期的努力就全都打了水漂。客户需求的反复无常,可能会造成你巧妙的设计难以为继。本来想着通过复制、粘贴来重构代码,但实际情况是你仅仅修改一个配置参数值就会影响另外15处代码。

经过长时间的积累,你逐渐有了处理模糊性与复杂性的能力。非科班程序员学会了有用的算法;科班出身的那批人也体会到,再好的理论在实践中也常常会有不管用的情况。

所谓实战程序员,他们所拥有的那些业界经验,都是被提出无理要求的老板“折磨”出来的——这种老板想让人一个早上就完成一个星期的工作量。他们会将所有代码在多种存储介质上同时备份,这做法也是来自他们在几千行代码丢失、一切从头开始后吸取到的惨痛教训。他们可是见过机房里的爆炸激光[2]的,因为有人直接部署了一段未经测试的代码。他们不得不在机房门口与运维人员争吵,希望能修正生产环境。他们也曾尝试自行实现源代码压缩功能,结果把所有内容都压缩成了一个字节,这个字节的值还是255。想必能够发明相应解压方法的人还没出生。

[2] 这里作者用了《银翼杀手》作品里的一个桥段,原书中的C-beams,是铯射线束的缩写,这是一种用于太空战斗的粒子束武器。作者使用这个夸张的意象来代表机房里出现的燃烧和元器件烧坏景象。——译者注

或许你刚刚毕业,还在投着简历寻找工作;或许你对编程正着迷,但还不知道未来在哪里,以后迎接自己的将是什么;或许你走出了培训班,正期待着工作机会,但你并不清楚你与他人的知识水平有多大差距;抑或是你自学了一门编程语言,又不确定自己的劣势在哪。朋友,欢迎你来进行实战。

1.3 杰出实战程序员

除了拥有实战信念、荣誉感和为人真诚外,杰出的实战程序员还得拥有以下特质。

懂得质疑。

结果驱动(也就是HR口中的结果导向)。

高产出。

化繁为简、直击要点。

好的软件开发工程师不仅仅是程序员

成为一名靠得住的工作伙伴要求的技能不仅仅是在计算机前编写代码,还需要善于沟通,能够提出有建设性的反馈,同时也能虚心接受别人的意见。就连林纳斯·托瓦兹(Linus Torvalds)[3]也承认应该增强自己的沟通技巧。然而这些东西不在本书里。去交些朋友吧!你会在社交当中学到的。

[3] 他是Linux操作系统和Git源代码控制软件的创建者。他秉持着这么一种观点:如果参与你项目的志愿者在技术上出错了,那么骂他们一顿也是可以的。

1.3.1 懂得质疑

一个懂得质疑的人常常会自省,并质疑那些被广为接受的观点,而对这些观点的解构能够让你看得更清楚。

很多图书、软件开发专家,包括斯拉沃伊·日热克(SlavojŽižek)[4]都强调过懂得批判和具有好奇心的重要性,但是,他们几乎没有给你一点实在的东西,让你能够将它们运用到实际工作中去。在本书里你可以看到很多有名的技术,还有一些最佳实践的例子,你也会看到为什么它们没有所声称的那么有效。

[4] 他是一名现代哲学家,他有一个毛病,就是喜欢批判这世间的一切事物,没有例外。

批评一门技术并不代表这门技术没有用处。技术批评能扩大你的眼界,让你能够鉴别出在某些应用场景中更合用的技术。

本书的目标并不是全面介绍每一种编程技术,而是给你一种视角去了解怎么样对待那些最佳实践,然后根据它们的优缺点排个顺序,并且通过这些优缺点来权衡利弊。

1.3.2 结果驱动

你当然可以说你是世界上最好的程序员,对于软件开发当中的复杂问题能有自己的见解,也可以自豪地说你为代码做了最好的设计。但是,如果不能产品交付,这些就都是空话。

根据芝诺(Zeno)[5]的悖论,你如果想要到达终点,就必须先到达这个路程的一半。这是一个悖论,是因为你到达一半之前必须到达那一半路程中的一半,以此类推,这会让你哪里都去不了。根据芝诺悖论衍生出的观点:要做出一个正式产品,就必须达成开发周期中的各个节点和里程碑,否则最终目标不可能实现。以结果为导向,意味着以里程碑为导向,以进展为导向。

[5] 芝诺是一个生活在几千年前的古希腊人,他喜欢问一些悲观的问题,自然而然,不讨喜的他一本作品都没有流传下来。

“为什么说一个项目会推迟一年?……一天推迟一点呗。”

——弗雷德·布鲁克斯(Fred Brooks),《人月神话》

为了得到结果可能意味着牺牲代码的质量、优雅性和技术追求。重要的是,你要时刻反思自己在做什么,为了谁在做。

牺牲代码的质量并不意味着牺牲产品的质量,如果你测试得当,并且需求写得非常明确,你甚至可以用PHP[6]去写。嗯,这可能有点不给面子,但是你在未来会尝到苦头,因为糟糕代码最终会“反噬”你。这可能就叫“代码现世报”。

[6] PHP发明之初是为了证明不设计一门编程语言也能完成工作,据我所知,虽然PHP经过这么多年的发展,但还是常常出现在各种编程语言笑话里。当然,现在它是一门出色的编程语言,但还是得优化自己的形象。

你在本书后面部分学到的部分技巧会帮你做出好决策,得到圆满结果。

1.3.3 高产出

提升开发速度的关键是经验、清晰的需求,以及机械键盘。哈哈,开个玩笑。与此相反,机械键盘几乎不会对你的开发速度有任何帮助,它们只是看起来很酷而已。不过,在打扰别人方面,它倒是可以为你助力。事实上,我完全不认为打字速度快可以提升开发速度。你对于自己打字速度的自信,倒是可能会使得你写出一些画蛇添足的代码。

有些技术是可以从别人的经验和“血泪史”当中学到的。在本书中,你将看到这样的例子。你学到的那些本领能让你写出更简练的代码,更快做出决定,这会尽可能让你少背一些“技术债”,并且会避免你以后花好多天来努力搞明白半年前你写的代码到底是一堆什么东西。

1.3.4 接受复杂性和模糊性

复杂性是可怕的,但是模糊性更可怕,因为它甚至会让你不知道该怕到什么程度,于是就更害怕了。

如何处理模糊性,是微软招聘人员时面试的核心问题之一。在面试中经常会涉及一些假设性的问题,比如说在纽约有多少间小提琴维修店;洛杉矶有多少个加油站;或者总统有多少位特勤人员,他们的任务是什么,时间表是怎么样的,请在白宫的地图上列出他们的名字并标注其行动路线;等等。

解决这种问题有一些小技巧。先厘清你在这种问题上获取到的所有信息,然后根据这些信息想出一个大致区间。比如说你可以先从纽约市的人口入手,推测其中拉小提琴的人大概有多少。这些信息会让你对市场规模有概念,了解到市场能有多大竞争空间。

类似地,当你遇到一些包含未知数的问题时,比如说预估开发某功能需要多长时间,你就可以通过已知信息来尽可能缩小结果的估计范围。你可以“榨干”自身所长,利用所假之物,将那些模棱两可的部分减到最少。

有趣的是,处理复杂问题的方法也是类似的。可以将那些看起来复杂到无法入手的问题,分解为一个个更便于管理、复杂度更低、更简单的小问题。

你的思路越清晰,就越能解决未知的问题。接下来你在本书中学到的技巧,可用于理清一些事情,并使你在面对模糊性和复杂性方面更有信心。

1.4 现代软件开发存在的问题

除了不断增加的复杂性,抽象层次越堆越高,Stack Overflow渐渐失了水准之外,现代软件开发还存在着以下这些问题。

技术繁多:如此之多的编程语言,不可胜数的框架,层出不穷的库。npm(Node.js框架的包管理软件)中甚至有一个名字叫“left-pad”的库,其作用仅仅是给字符串末尾添加空白字符。

现代软件开发由范式驱动:这导致了软件开发的保守主义。在很多程序员眼里,编程语言、最佳实践、设计模式、算法和数据结构好像远古异族遗迹一样神秘,摸不着其中的原理。 

科技黑箱:就像汽车,之前人们还可以靠自己来修理,如今发动机越来越高级,引擎的金属盖如同盖在法老坟墓之上,谁要打开它,就会被诅咒。软件开发如出一辙。虽然现在几乎所有的东西都是开源的,但是20世纪90年代之后,软件的复杂性成倍增长,我觉得如今的技术比二进制代码的逆向工程还要难懂。

代码开销:我们拥有了远超所需的资源,人们不再关心代码开销。现在你还需要写一个简单的聊天软件吗?把它集成到成熟的浏览器框架里会比较省事,而且没人会为了软件消耗以吉字节计的内存而心疼,何乐而不为呢?

自扫门前雪:程序员只关注自己的技术栈,对支撑这些技术栈的知识漠不关心。这也情有可原:饭都吃不上,哪有时间去学习呢?我把这叫作“开发者的吃饭难题”。也正是由于受自身知识所限,那些会影响他们产品的因素同样被忽略了。网页开发者通常对网页底层网络协议一头雾水,只能忍受网页加载延迟。因为他们不知道其中的技术细节,比如说不必要的长证书链会拖慢网页的加载速度。

憎恶重复:多亏了人家教给我们的范式,大家对那些无需技巧的工作(比如重复工作或复制、粘贴)毫无热情。你必须得找到DRY[7]的解决方案。这种观念会让你怀疑自己和自己的能力,从而影响你的生产力。

[7] 即don’t repeat yourself的缩写。这是一种迷信,好像如果有人重复写某段代码,而不是把它封装到一个函数里面,这个人就会立马变成一只青蛙。

npm和left-pad的故事

在过去的10年里,npm成了事实上的JavaScript包生态系统。人们可以向生态系统贡献自己的包,其他的包也可以使用它们,这降低了开发大型项目的难度。阿泽尔· 科丘卢(Azer Koçulu)就是开发者之一。left-pad是他为npm生态系统贡献的250个软件包中的一个。它只有一个功能:将空格附加到字符串上,以确保字符串的大小始终是固定的。这个操作轻而易举。

有一天,他收到npm的一封电子邮件,说他们已经删除了他的一个名为kik的包,因为他们收到了一家同名公司的投诉。npm决定删除阿泽尔的包,并将名称的使用权交给那家公司。这惹怒了阿泽尔,一气之下他删除了他贡献的所有包,包括left-pad。但问题是,前文也提到了,世界有数百个大型项目直接或间接地使用了这个包。他的这个行为导致受波及的所有项目都停止了。这是一场大灾难,也让人对平台的信任打了问号。

我讲这个故事的意思是,软件开发世界中充满了你不愿意看到的意外。

在本书里,我对这些问题给出了解决方案,也会详细讨论一些你也许会认为无聊的核心概念。我还会推荐你优先考虑实用性和简洁性,勇敢地拒绝一些长期以来的“金科玉律”,更重要的是,凡事要质疑、反思,这是有价值的。

1.4.1 技术繁多

由于“银弹”谬论,我们总在不断追求最佳技术。我们认为总有一种技术可以将生产力提高好几个数量级。但是,当然没有。打个比方,Python是一种解释型语言,不需要编译Python代码,就可以立即运行。更方便的一点是,Python[8]中不需要为声明的变量指定类型,这使得写代码更快。那么,Python比C#更好吗?不一定。

[8] Python是那些想推广使用空格缩进的人包装出来的实用编程语言。

因为你不用花时间进行类型声明来注释代码和编译,对代码当中出现的问题就会直接错过了。这就是说你只能在测试时或上线到生产环境中才发现它们,这要比顺手编译付出的代价高得多。大多数技术都是权衡利弊,找出折中的方案,而不是生产力的助推火箭。提高效率靠的是你对这项技术的熟悉程度和应用技巧,而不是你使用的技术本身。当然,有更好的技术,但它们很少能带来数量级上的效率提升。

1999年,我正想着着手开发我的第一个交互式网站,那时我完全不知道如何编写一个Web应用程序。按常理,我应该首先尝试找到最好的技术,那就意味着我要自学VB Script或Perl。相反,我使用了当时我最熟悉的语言:Pascal。这是被认为最不适合Web应用开发的语言之一,但我用它成功运行了Web应用程序。当然,它肯定也有很多问题,比如每次这个应用程序挂起时,这个进程依然会停留在加拿大机房的某台服务器的内存里。然后用户就得给服务提供商打电话,让他们重启那台物理服务器。总而言之,Pascal让我在短时间内就搭好了原型,我对它很满意。你看,我并没有花几个月的时间去学习然后再开发我预想的网站,而是仅仅花了不到三个小时的时间就编程完成并发布代码。

在后文中,我会介绍一些让你能更高效地利用你所拥有的工具的方法。

1.4.2 遍阅范式

我最早接触到的编程范式是20世纪80年代的面向过程编程,结构化编程里没有goto,没有血汗,也没有悔恨的泪水,而是由结构化的代码块(比如函数或者循环)组成的。这种设计让代码更容易维护和阅读,同时还不牺牲性能。也是因为结构化编程,我开始对Pascal和C语言产生了兴趣。

大约过了5年,我又接触到另一种编程范式——面向对象编程,或者叫作OOP(object-oriented programming)。我记得那个时候,数不清的计算机相关杂志铺天盖地地宣传着它。在用面向过程编程之后,怎么样把程序写得更好,变成了一件大事。

在OOP之后,我以为至少要每过5年才能出现一种新范式,没承想,新范式的出现比这还要频繁。在20世纪90年代,Java和JavaScript网络脚本与函数式编程出现了,即时编译(just-in-time compilation,JIT compilation)[9]随之成了主流。截至90年代末,函数式编程慢慢进入了主流编程领域。

[9] 即时编译,Java的创建者Sun微系统公司(现已被收购)创造的一项“神技”。其作用是代码在运行的同时进行编译,整个程序的运行速度会变得更快,因为优化器将在运行时收集更多的数据。虽然我能够“解释”它,但这并不妨碍它是一项神技。

时间来到千禧年,在这几十年里,多层架构的应用(n-tier application)越来越多,如胖客户端,瘦客户端,泛型,MVC、MWM和MVP。随着可信、面向未来及最终的响应式编程(reactive programming)出现,异步编程也开始迅猛发展。类似LINQ那样拥有模式匹配和不变性概念的函数式编程语言进入主流语言之列,风靡一时的新词层出不穷。

讲了这么多我们还没有谈到设计模式和最佳实践。如今,几乎所有项目都有多如牛毛的最佳实践、技巧和窍门。虽然问题的答案显而易见(是空格键),但还是有很多与我们应该用空格键或是Tab键有关的表达观点[10]

[10] 我从实用的角度讨论了这两者,读者可以搜索tabs-vs-spaces-towards-a-better-bike来进行阅读。

我们设定所有问题都可以通过采用某一种范式、某一种模式、某一种框架或是某一种库来解决。但考虑到我们所面临问题的复杂度,这种美事是不存在的。不仅如此,盲目地使用某种工具只会给以后埋下隐患:需要学习全新领域的知识,而且这些新玩意儿本身也有一大堆缺陷。你的开发速度会更慢,甚至倒逼你在设计上进行相应的妥协。本书会让你对使用模式更加有信心,更加感兴趣,在代码复查(code review)时尝到甜头。

1.4.3 科技黑箱

框架,或者一个库,就像一个安装包,软件开发者可以安装它,阅读相关文档,然后运行它。但软件开发者通常不清楚其工作原理,算法和数据结构对于他们来说也是一样的。因为键值对的形式易于使用,所以他们就采用字典数据格式,但他们不知道这么做会带来什么后果。

无条件信任某个包及其生态或者各类框架是很容易出现大问题的。如果不知道往字典中添加具有相同键的项在查询时与列表性能一样的话,可能就会花上几天的时间去调试。当用一个简单的数组就可以满足需求时,我们又采用C#生成器,由此带来的性能下降,我们还可能找不到原因。

1993年的某天,朋友递给我一张声卡,让我装在计算机里。对,为了有个过得去的音效,我们曾经还得给计算机装一张声卡,不然你除了哔哔声之外什么都听不到。言归正传,我之前可从来没有打开过我的计算机,我怕把它拆坏了。我问朋友:“你不能帮我装一下吗?”他答复:“你得亲自打开它,才会明白它是怎么运行的。”

我很认同他这句话,我懂了,我的焦虑是因为我的不了解,而不是因为我的不能够。我打开机箱,看到机箱内部,然后就释然了,这就是一堆板子而已嘛。这块声卡放在……嗯,放在这个插槽里。从此这玩意儿对我来说再也不是一无所知了。过后,在给艺校的学生讲计算机基础知识的时候,我又用了类似的技巧。我拆开一个鼠标的轨迹球,并给学生们看。顺便提一句,那个时候实验室的鼠标还有个球。唉,这样说容易引起误解。[11]然后我又打开机箱,说:“你们看,这也没什么,就是一些板卡和插槽嘛。”

[11] 在这里,“balls”既可以指鼠标内部的滚珠(一种较早的鼠标设计),也可以指代睾丸。——译者注

从此以后,这也成了我处理所有没接触过的复杂问题的准则。我对于一开始就“打开盒子”,见识到事情的全貌,会感到不过如此。

类似地,了解某个库、某个框架,或者一台计算机的工作原理,对你去理解以它们为基础的东西有很大帮助。勇于“开盒”,了解其中的细节,对于你怎样使用“盒子”也是一样的。你真的没有必要阅读全部代码,或者浏览厚达千页的理论书,但至少应当明白某部分的功用,以及它如何影响你的项目。

这就是我在后文会谈到一些基础或者说底层原理的原因。“打开盒子”,好好瞧瞧,这会帮助我们找到高层级编程的更佳选择。

1.4.4 低估开销

我其实非常愿意看到每天有这么多基于云的应用程序,因为这种方案不仅性价比很高,而且方便清晰了解代码的实际开销。当你因写代码时的决策错误而为产生的云服务器额外费用买单的时候,你肯定立马就会把开销当回事了。

框架和库是有用的抽象,通常能帮我们减少开销。但是你不能把所有决策都推给框架来定。有的时候,我们必须靠自己做抉择,必须考虑程序开销问题。开销这一因素对大体量的应用程序更重要。哪怕节省很少的时间,都可能帮你省下宝贵的资源。

一个软件开发者首先要考虑的当然不是开销,但是,至少在某些情况下要懂得如何去避免开销。梳理这种观念,会帮助你节省时间。这不光是为了你自己,也是为了那些眼巴巴看着你开发的网页载入提示“转圈”[12]的用户们。

[12] “转圈”当属现代的沙漏。在早期,电脑用一个沙漏符号来让你无限等待加载。“转圈”是现代动画的沙漏等价物。它通常是一个无限旋转的圆,不过它的作用只是分散用户的注意力。

在本书中,你会看到我给出的一些场景和例子,这些场景和例子会告诉你如何轻松避免开销。

1.4.5 自扫门前雪

只关注我们负责的东西:我们拥有的组件、我们编写的代码、我们造成的缺陷,还有在办公室厨房微波炉里偶尔烤糊了的午餐,这是我们处理复杂问题的一种方法。这听上去确实是节省了我们的时间。但是别忘了,所有的代码都是息息相关的。

了解特定技术、库的工作原理,依赖关系是如何工作和连接的,可以让我们在写代码的时候做出更好的决策。本书给出的例子能够给你提供一个新的视角,让你不再只关心自己那一亩三分地,而接触到你舒适区以外的那些相关知识和难题。因为这样,能让你对自己所写代码的命运做到心中有数。

1.4.6 憎恶重复

所有关于软件开发的原则,总结成一句话就是:你的工作时间越少越好,避免重复的、无脑的工作,比如复制、粘贴,或是为了做一些小更改而从头编写只有少部分不同的代码。首先,它们的确很花时间,其次,它们的可维护性非常差。

不过,不是所有的“打杂”都没用。复制、粘贴有时是有用的,虽然很多人对它有很深的成见,但是在某些方面,将它们运用好了,甚至会比你之前学到的最佳实践更加有用。

除此以外,并不是你写的所有代码都会被加入实际产品中。你写的代码中有些可能会被用于开发某些还处于雏形的产品,有些比较适合做代码测试,有些又可以作为你手头上事情的错误范例来警示自己。我会在后文举一些例子,介绍如何用这些方法给自己带来好处。

1.5 特别说明

本书并不是关于编程、算法或者任何概念的综合指南。我自认为我在这些领域称不上专业,但是我拥有足够的软件开发专业知识。本书里的内容跟那些知名畅销书里的内容可不一样,本书绝对不是用于学习编程的指南。

经验丰富的程序员从本书中获得的启发可能较少,毕竟他们身经百战并已经成了善于实战的程序员。虽然这么说,但我相信书中仍然有一些见解会让他们感到惊讶。

本书其实算得上是一种对于编程书进行大话演绎的尝试,我想通过一种比较好玩的方式来介绍编程。本书并不严肃,所以请不要端着架子去读。如果你读完本书觉得自己在开发领域有提升并且有了一段愉快的阅读经历,就是我最大的成功。

1.6 本书主题

本书涵盖了以下主题。

足以应付你入行之初的基础知识。不过这些知识并不是非常详尽的,但也足够转变之前你觉得它们无趣的看法,激发出你的兴趣。在你做决策的时候,这些知识可是关键。

作为反模式所提出的一些著名的、被广为接受的最佳实践和技巧,在某些情况下更能发挥作用。你对这些内容读得越多,对那些编程实践的批判性思考的“第六感”就会越灵敏。

看起来无关的编程技巧,比如CPU性能优化技巧,可能会在更深层次的领域影响你的决策和代码编写。即便你没直接用上这些知识,“打开盒子”,然后了解其内部原理,也会对你有很大帮助。

我在日常编程活动中发现了一些有用的技巧,这些技巧可能会帮助你提高生产力,让你有时间“咬指甲”和“摸鱼”。

这些内容会让你在看待编程这件事上有新的视角,会改变你对某些“无聊”主题的理解,也会改变你对某些教条的态度,让你更享受于工作。

本章总结

本章描写了专业软件开发世界的残酷现实。你得学会那些在学校里看不上的、老师没有教的和在自学期间错过的技能。

软件开发新人,对于理论知识的看法往往比较极端,要么十分在意,要么完全不关心。你最终当然能找到一个平衡点,但是你可以通过某种方式早点找到它。

现代软件开发较几十年以前复杂得多,即便是开发一个简单的运行程序也需要你有丰富的知识,知识还得横跨多个层面。

程序员在工作与学习之间面临两难。不妨换个角度看,问题可能就会迎刃而解。

如果你所做的事情在你的头脑中是模糊的一团,就只会让你觉得编程是一件乏味的事情,你的工作效率也会降低。你越清楚自己在做什么,就越能体会到编程带来的那份快乐。

相关图书

推荐系统:产品与算法解析
推荐系统:产品与算法解析
面向电子鼻的复合光气体传感方法
面向电子鼻的复合光气体传感方法
程序设计竞赛专题挑战教程
程序设计竞赛专题挑战教程
Serverless核心技术和大规模实践
Serverless核心技术和大规模实践
深入浅出Windows API程序设计:编程基础篇
深入浅出Windows API程序设计:编程基础篇
商业开源 开源软件许可实用指南 第三版
商业开源 开源软件许可实用指南 第三版

相关文章

相关课程