您的位置 首页 > 数码极客

如何控制代码质量如何用代码控制别人的手机…

本篇教你一系列成为优秀程序员的职场准则:话不多说,直接上干货!!(内容较多,建议收藏,慢慢看,持续学)

技术负债

咱们程序员在接到需求初期,是没办法对整个需求作完全正确评估的!(本瓜以为,由产品需求到技术落地是有着天然的鸿沟的)所以,多数情况下,我们都会在代码迭代过程中面对之前未预想到的问题。

这种情况下,往往就会面临 “快速实现” 和 “正确实现” 的抉择!

快速实现 很具有诱惑性,它可以快速响应产品或客户的要求!但它极有可能造成代码杂乱(由于初期设计不足)。

你可能会安慰自己:“没事,先实现它,后面有空再来优化吧~”

苍天知道这个“有空”是什么时候?!功能要一版接着一版的发,时间也一天接着一天的流逝,你的承诺 —— “有空优化” 最终会演变成为一种 技术负债!

当技术负债越来越多、越来越久,我们的代码也将不可避免的走向一条不归路 —— 代码 山!

所以,承诺了要优化的代码,就得尽快了!技术负债是会产生利息的!

函数式编程

近来,函数式编程在主流编程社区越来越火!

掌握【函数式编程范式】可以大大提高您的代码质量。深入理解并应用它,您的设计将表现出更高程度的 referential transparency(如纯函数)。

函数几乎不出现副作用,这是非常重要的!命令式代码最大的缺陷之一就是变量的赋值和引用混乱,你想要将它完全拎清楚,要付出极大的代价。

函数式编程设计通常会将责任分配给更多、更小的函数!这些函数之间的参数传递十分明确,而不会出现随意引用外部可变变量。这样更便于调试、也降低了可变变量引用赋值混乱的缺陷;

我们期待函数式编程有更多展现!有人断言,函数式编程和面向对象编程互相映射,犹如太极中的阴、阳。

观察用户

我们都倾向于假设其他人的想法和我们一样,但事实并不如此!心理学家称之为:错误共识效应。

这个现象解释了用户和程序员之间的距离。用户不会考虑计算机工作原理、计算时间、技术模型......

了解用户的最好方式就是 观察用户。花一个小时观察用户所想比花一天时间猜测用户所想更具有效果!

前文也提过:从业务需求到技术落地之间是有天然鸿沟的,想要填平这道沟,程序员得学着去观察需求!观察得同时,不断问自己:他/她为什么要这么做?这比花大量时间猜测更符合解决问题的方法论!

自动化编码标准

每个项目都有项目规范、编码标准,我们期望通过它们来约束、规范开发者的编码行为。但是最终交付的时候,往往又是一团糟。为什么?

原因有很多,可能是有人没听清、也可能是不完全明白、也可能是不同意、也可能是同意但由于很难实践最终选择放弃......所以,如果不能将编码标准自动化,那么这种约束将很难进行!

实际上,我们有大量工具可用于生成代码质量报告,以及记录和维护编码标准。在可能的情况下,它还会自动化格式代码并强制校验。

我们要确保代码格式化是项目构建的一部分,每次编译代码时,都会运行它;使用静态代码分析工具扫描代码,找到冗余或错误代码;学习配置一些自动化扫描工具,测试代码、检查代码;

这些行为应该是不断更新、补充完善的,随着项目的迭代而改进。

编程简单即美

所有开发者都应该记住:

Beauty of style and harmony and grace and good rhythm depends on simplicity. — Plato (简单即美)

什么是漂亮的代码?这可能是一个非常主观的问题。学人文艺术的人对美的看法,与学科学技术的人不一样。但是 简单 是大多数论点的基础。

我们的代码需要实现:

  • 可读;
  • 可维护;
  • 快速开发;
  • 保持简单;

你可以通过阅读源码来发现其中奥妙~无论应用程序或系统有多复杂,但各部分都应保持简单!它们有着集中的方法、单一的职责。

干净、简单、可测试的代码可以保障系统随着时间推移也有很强的可维护性,从而也可以保持较快的开发速度。再次声明:简单即美!

重构要则

几乎每个程序员都要重构现有代码,在你重构之前,建议你阅读以下几点:

  1. 重构第一步就是 评估 现有代码和编写的测试,这能帮助你理解现有代码的优缺点,然后保留优点,避免缺点。如果重构后的代码比原有代码更加糟糕,那这一步一定没有做充分;
  2. 重构 不代表 重写所有!无论之前的代码有多丑陋,你完全扔掉它,这绝不是一个明智之举。因为你扔掉是几个月(或几年)通过测试,久经沙场的代码。其中可能存在你还不知道的方法或 bug 修复逻辑。全新代码可能出现一样的神秘错误,这会消耗大量时间和精力!
  3. 不断 小规模 的重构比一次大规模的重构要好!这也可以称之为渐进式重构代码。大规模重构如果遇到棘手问题可能会让人倍感压力,甚至崩溃,这一点不开玩笑。
  4. 每次重构迭代后,都要确保 测试通过。这是十分重要的!如果现有测试场景不足以覆盖您所做的更改,请添加新测试。不要在还没考虑清楚时丢弃到旧代码中的测试用例。
  5. 你 不喜欢 旧代码的风格或结构不能成为你重构代码的正当理由!个人喜好总是会变的,个人喜好也不一定会被他人喜好。
  6. 采用 新技术 也不是重构的充分理由!除非成本效益分析表明新语言或框架会在功能、可维护性或生产力方面带来显著改进,否则最好保持原样。
  7. 记住:重构 并不能 总是保证新代码会更好!

小心复用

系统的两个截然不同的部分以相同的方式执行某些逻辑,你可能会想到写一个公共库,然后进行复用。老师都是这么教的!代码复用、组件复用、公共库复用 balabala ......

但实际上,这两个部分往往还会变化出不同的业务,这样复用就变成耦合了。依赖项增加,使得系统的脉络纠缠在一起。

实战也表明:技术的使用应该基于背景,否则产生的将不是价值,而是额外的成本。

不管是用别人的库,还是你把库分享给别人,都得小心些。

童子军规则

童子军有这样一条军规:“始终让露营地比你发现它时更干净”!英国童军总会总领袖 Robert Stephenson Smyth Baden-Powell 这样说的初衷是:试着让这个世界比你发现的更好一点。

我们在代码开发过程中也应当遵循这个规则!如果大家都遵循这个简单的规则,代码最终沦为屎山的命运将被总结。

在编程中留下一团糟的代码,无异于露营中留下一堆垃圾,这是大家都不能接受的!这更像是一个普遍的社会规则。

优先自我检查

排查问题时,我们会怀疑编译器、解释器、操作系统、应用服务器、数据库、内存管理器是否出了问题,但事实证明它们出错非常非常少见。

假设这些工具被广泛使用,并且已发展足够成熟,你没理由去怀疑:是因为它们的错误导致了你这次错误。

定位问题时,请优先自我检查!

福尔摩斯说过:排除所有可能,剩下的,无论多么不可能,它就是真相!

善其事 利其器

工欲善其事必先利其器,是亘古未变的道理。

开发时选择好的工具非常重要!这里主要指组件、第三方库、框架等等。

它们能带来很多便利:

  • 基础的代码可有工具构建;
  • 使用组件、框架出错的情况比自己写更小;
  • 高质量的库是由高质量的开发在维护的,专业的事交给专业的人去做;
  • 可以考虑购买一些工具来提升你的开发效率;

你需要注意的事:

  • 弄清工具接口说明,它的模型、协议等是否和你的项目匹配?
  • 减少工具的配置,如果它配置非常复杂,可能导致难以维护和扩展!
  • 注意许可条款,使用版权问题;
  • 注意收费问题,有些工具是局部1范围内免费、局部范围内又收费;

明确类型定义

曾几何时,我们只有非常基本的数据结构:位、字节和字符。后来发展形成了堆栈、队列、散列、链表等。“计算机科学”花费大量精力将现实世界映射到我们的具有限制性数据结构中。

请准确清晰地定义代码的类型。明确表示出来,让下一个人也知道。

同时,明确类型定义利于你以后的代码封装。

编程即设计

设想一下:一觉醒来,建筑行业发生了惊天巨变,生产材料可以凭空产生,并可以由机器人完成建设。

这样一来,设计成本大于施工成本。一家能快速完成设计的公司将在市场上占据优势。

如果建筑工程除了问题,我们只需重新再设计、或者进行修补,因为生产材料没什么成本。建筑质量开始变得糟糕起来,由一次次不完整的设计引起。

这种对建筑行业的漫谈,却实实在在发生在软件行业。我们能快速开发一个项目,不行马上就丢弃。然后再快速开发一个......

设计是一个创造性的工作而非机械性的工作。

编程是设计,开发成本不大,设计成本是更重要的。

规范代码格式

有研究表明:开发人员花了相当一部分时间在寻找“在哪里进行修改”这个问题的答案。

所以,我们需要重视规范代码格式。这样利于我们快速扫描代码,定位问题位置。

代码布局应该是紧凑的、风格一致的、清晰可见的。

有非编程专业的人说:代码有时看起来像诗歌。

真正规范的代码正是这样!既是是一个非专业的人也能感受它的美~

自主 CodeReview

我们应该一致保持 CodeReview 的习惯,它能提高代码质量,降低错误率。

可能我们在 CodeReview 时有过不好的回忆(菜鸟在初始都被吊着打过),许多开发往往不喜欢它。有些公司会有专门的人来做 CodeReview 审查,开发者必须遵守审查者的规则。这样多数偏于严格和正式。其实,这样的方式反而南辕北辙。审查者需要花费时间去了解很多代码细节,这会是一个巨大的瓶颈。

CodeReview 如果能自发,大家共同建立和维护编码指南,会更有效果!寻找错误不是目的,寻找好的编码方法才是目的。因此,做 CodeReview 时,大家最好都是温和的。约定每周或每月来一起建设。

让 CodeReview 变得有趣是关键!如此不仅能优化代码,还能团结团队,共同进步。

给出编码理由

我们应该仔细考虑每个块级代码的正确性,并给出能够说服自己为什么要这样写的理由。

对于条件判断、循环、类的使用、函数的使用等都能给出至少简单的逻辑上的理由。

比如:

  • 避免使用 goto 语句,因为它们会导致与远端的耦合;
  • 避免使用可修改的全局变量,因为它会导致各部分产生依赖;
  • 每个变量都应该有尽可能小的作用域,因为这样不影响外界变量;
  • 如果您需要使用嵌套,请将其封装为函数,因为这样代码会更清晰;
  • 函数的入参最好不超过 4 个,这样可以减少变量引用赋值找寻时间;
  • ......

我们需要养成这样的好习惯 —— 不断推理出“代码为什么这样写的理由”!

这会让你受益无穷!

不断注释

代码注释是非常重要的一块!

作者曾因为在学校的编程测验中代码没写注释而被评低分。

注释本身没有恶意,它们与基本的分支判断或循环结构一样,都是编程所必备。

本瓜之前写过一篇《花五分钟把代码注释也规范一哈子?》,里面有具体介绍注释的相关细则,感兴趣的同学可前往阅读。

当我们阅读别人的代码,往往是先看注释。所以,养成不断维护注释的习惯,能让你的代码走的更远~

有效注释

什么是有效的注释呢?

注释能够表达代码以外的东西,解释代码不能解释的。

还有一种书说法是:代码即注释,优先考虑将你想说的用代码表达出来。

  • 注释应当简短、清晰;
  • 告诉大家你“为什么”写这个注释,而不是告诉大家这段代码 “是什么” ! “是什么”应该交给代码本身去解释;
  • 保持你的注释持久维护,记得及时更新和与代码匹配;

持续学习

随着编程这份工作越来越普及,有一天你也许不会再被需要。你的工作将被取代。你会怎么办呢?

持续学习可能是帮助你的答案之一,这里有一些建议:

  • 阅读书籍、杂志、博客、维基百科、和好的学习网站;
  • 如果你真的想沉浸在一项技术中,那就动手写一些代码;
  • 向身边厉害的人学习,你能从他们那学到更多;
  • 订阅一些好的网络博客;
  • 深入了解你使用的框架和库,你会发现它们的过人之处;
  • 认真的修复问题,记录它们、分享它们;
  • 跟别人谈论技术、或者教别人技术,这会帮你你的理解;
  • 加入学习兴趣小组;
  • 学习 The Pragmatic Programmers,扩展技术栈;
  • 提高学习效率,讲究学习方法论;
  • 回到学校;
  • ......

编程技术更新很快,别掉队!

方便不是标准

我们可能为了方便临时做一些 API 设计,这样导致的问题很多;

作者举了个例子:

  • 他不希望其他类必须进行两个单独的调用来完成这件事。
  • 如果和这个方法几乎一样,我为什么要制作另一种方法?为什么不写一个判断开关来实现?
  • ......

这样考虑的确目的明确,但是会降低 API 的可读性;

API 设计应该有更好的一种策略(比如:用多样化的此词汇进行表达),而非是以“方便”来作为标准。

记住:没有一个英文单词长这样,所以也请别设计出这样的 API,即使它看起来“更方便”~

MakeUpYourRoomBeQuietAndDoYourHomeWork

提早部署

我们通常会在项目尾期进行部署操作,但是部署却是需求方第一时间想看到的事情。

如果把部署放在后面,会出现要去适配代码中对环境的假设等等情况,整个过程将更加复杂化。

提早部署,提高可见,提早可用,完成比完美更重要。

这也是团队生产力的重要体现。

区分异常

程序运行时出现异常通常可以归为:技术异常和业务异常,区分二者有利于我们更好的捕获它们。

技术异常:比如,在长度为 17 的数组访问第 83 位元素,应该将它冒泡到架构设计级别进行统一捕获处理,记录日志、警报管理、输出报告等;

另一种技术异常是由于执行环境的影响。比如数据库无响应了等,应该设有一套基础机制来进行重连,重连合理的次数后仍报失败,再冒泡到统一的技术异常捕获进行处理。

业务异常:比如,尝试从资金不足的账户中取款,客户端应该创建特定、单独的异常层级进行处理,客户端还可以作更多自定义的处理;

混淆二者会让调用者感觉不清晰。

针对训练

“针对训练”与“完成任务”相对立,针对训练可以提高执行任务的技巧和能力;

我们都知道 10000 小时定律。Mary Poppendieck 指出:要经历 10000 小时的针对练习才能成为专家!

10000 小时有多长?每周 20 小时,坚持 10 年!(确实还挺久的~)

其实关键不是 1 万小时,而是 1 万小时有针对的训练,刻意的选择能才让你伟大!

针对训练不是训练你擅长做的事情,它意味着挑战,意味着不断失败,再不断吸取经验、不断成长~

行业黑话

Domain-Specific Languages(DSL)表明:每个领域都有着属于他们独特的语言,本瓜译为 —— 行业“黑话”~

对于开发人员来说,DSL 分为内部和外部:

  • 内部 DSL 是用通用编程语言编写的;
  • 外部 DSL 用文本或图形表达;

我们必须考虑 DSL 的目标受众,他们是开发、还是经理、或是客户?

针对不同的用户需调整不同的语言技术说法、工具名称、语法说明等。同时,借助 DSL 我们还可以隐藏一些技术细节,也能帮助在用户和开发人员之间搭建起一座沟通的桥梁。

语言在逐渐进化,表达方式也会有不同的发展!

勇于打破

我们都接手过一些糟糕的代码,讲道理是:想动,不敢动!

不动,在上面叠加逻辑,可能导致它越来越恶化。就像是医生看病,当病情愈发恶化,你再不动手术的话,就要出大事。

动手术是一时的疼痛,但它会不断往好的方向发展,最终愈合。

当我们再面对槽糕代码时,不要害怕!勇敢牛牛,不怕重构!!

如果能完整的处理它,会帮助你获得很多经验,这都是不可多得的!

重新定义接口、重构模块、重复复制粘贴的代码、减少依赖,加入设计思想......你一定会遇到很多问题,但坚持下来,你最终一定会胜利!

“成为不怕切除肿瘤的外科医生”等于“成为不怕重构槽糕代码的程序猿”。

当然,在这之前,你需要足够的理由说服自己、说服“管理层”。

严于测试

不管是开发做自测,还是测试人员做版本测试,我们可能会随意的写测试数据:123123、testtest、......有时甚至会将一些私密信息作测试数据~

我们要做这样的考虑:如果这些测试数据被公开了,会不会造成一些麻烦和尴尬?

包括一些提示语也是同样,测试的提示语可能在正式线没有进行替换,导致尴尬。

还包括一些测试的地址在上线前也忘了替换,导致麻烦。

所以,在开发过程中编写的任何测试数据,我们都得尽力严格把控。

别放过错误

如果一个错误不太严重,我们往往会选择忽略,而这往往会带来一些不自知的风险。

try { // ...do something... } catch (...) {} // ignore errors

说中了,catch 里面的内容本瓜有时会忽略,通常加一句 con(err)。

不处理错误会导致:

  • 代码脆弱;
  • 代码不安全;
  • 代码结构差;

别放过错误,别欺骗自己程序总能正常运行、始终有效!

了解语言文化

计算机语言和自然语言一样,我们不仅仅要学它的语法知识,还要学习它背后的文化。

The Pragmatic Programmer 鼓励我们每年学一门新的语言~

了解语言背后的文化是非常有趣的,旦当涉猎,触类旁通。

u1s1,一年学一门,这个想法真的挺大胆的!

一旦你学会了一门新语言的诀窍,它还会反哺你重新认识之前一直在使用的语言。

作者从 Ruby 编程中学会了如何在 C# 中有效地使用委托,在 .NETs 对泛型的使用启发了 Java 中泛型的用法,学习 LINQ 让 Scala 变得轻而易举......

捕获错误

作者为了防止应用程序的终止,用了大量的 try...catch....,却为此付出了代价。

他的团队自制了一个 C++ 的基本应用程序类,它处理了所有转义异常的代码,这导致了每当出现问题时,错误会像在黑帮片被杀的人一样消失,没有留下任何痕迹。

用多次的 try...catch 捕获错误表面上看取代了系统的混乱,但实际上也会产生难以排查的崩溃。

  • 此点存疑:本瓜猜测作者想表达有些错误需要暴露出来在错误处理机制中进行统一处理,而不是写很多 try...catch... 来掩盖。

不相信假设

没有开发经验的管理者觉得程序猿的工作很简单,同样,没有管理经验的开发者,认为管理的工作很简单(不就是合周报吗?)。

编程最困难的部分是思考,而这一部分不那么外露,也就不容易被旁人重视。

在任何项目中,程序猿可能不会积极的参与很多事情:获取用户原始需求、获取预算、构建服务器、部署到生产线、从旧业务中迁移一部分程序等;

当你不积极的参与这些事的时候,你就会无意识的假设:它可能事这样的,这样做那样做应该就行了吧.......

有时,这种假设会成功,有时,则会带来麻烦。

不相信这种假设,问清楚:“什么时候”在“什么条件下”发生“什么事”,而不是“如果”。

不重复自己

在所有编程原则中,不要重复自己 (Don't Repeat Yourself) 可能是最基本的原则之一,它是许多知名软件开发最佳实践和设计模式的基础。

  • 重复是浪费

重复会增加系统的复杂性,导致代码库的膨胀,导致其它开发人员难以理解这个系统。DRY 要求“系统中的每一个点都必须有一个单一的、明确的、权威的表示”

  • 重复的过程可以自动化

软件开发中的许多过程都是重复的,可以实现自动化。自动化过程不易出错,并且还能配套构建自动化测试,进行更方便的测试。

如果你苦于手动做一些重复操作,就应该考虑到:自动化和标准化。

  • 重复的逻辑可以抽象

许多设计模式的重要目标是减少或消除应用程序中的逻辑重复。

如果一个对象在使用之前通常需要发生几件事,这可以通过抽象工厂、工厂模式来完成。如果一个对象的行为有许多可能的变化,则可以使用策略模式,而不是大量使用 if...else... 这些行为结构。

事实上,设计模式本身的制定,就是为了减少解决方案内所需的重复工作。

别动生产线

在大多数基于 Web 的开发环境中,架构可以这样分解:

  • 在开发者机器上进行本地开发和单元测试;
  • 完成手动或自动集成测试的开发服务器;
  • QA 团队和用户在其中进行验收测试的临时服务器;
  • 生产服务器;

开发人员(甚至是高级开发人员)不应拥有超出开发服务器的访问权限,就像 QA 团队和用户也不需要接触开发服务器上的任何东西。

如果出现程序错误,生产线不是修复它的地方!

作者参与过的一些最大的编程灾难是由于违反了这一规则。

封装行为

封装行为,而不是仅仅封装状态。

在软件开发中,封装的价值是有目共睹的。但作者发现:类似乎是开发人员最难掌握的封装结构之一。

如果你的类里面有 3000 多行,说明你还没有完全理解面向对象的思想。

假设我们有三个类:Customer、Order 和 Item。

实现调用:用户消费信用卡

cu())

如果该方法的后置条件失败,则会抛出异常并中止购买。

经验不足的开发会将业务规则包装到一个叫 OrderManager 或 OrderService 的对象中,在里面记录 Order,Customer、Item,再与 if...else... 进行捆绑。

明显,前者封装行为做的更为出色!后者封装状态,将很难维护。

浮点数不是实数

浮点数不是数学意义上的“实数”,尽管在某些编程语言(例如 Pascal 和 Fortran)中被称为实数。

实数具有无限精度,因此是连续且无损耗的;但浮点数的精度有限,因此它们是有限的。

IEEE 浮点数是基于以 2 为基数的科学记数法的固定精度数 1.d1d2...dp-1 × 2e ,其中 p 是精度。了解浮点数原理可以帮助您避免经典的数字错误。

你不应该在金融应用的程序中使用浮点数,这也是 Python 和 C# 等语言中用十进制类的缘由。

开源实现梦想

也许你没办法在日常工作中满足你雄心勃勃的编程野心,也许你只关心自己的项目想开发创业,也许你想在谷歌、苹果、微软这样的大公司工作.......

好消息,它们有一个共同的答案:开源。通过开源,你可以和世界顶尖编程人员进行对话、可以聚集非常活跃的开发同伴、可以做你任何想做的编程梦~

当然,这就需要你牺牲你得空闲时间。同时,你也需要注意版权、专利、商标和知识产权法等问题。

开源为有上进心的程序员提供了巨大的机会。你觉得你可以,你就真的可以!

API 设计黄金法则

API 设计是非常复杂的,尤其是在大型项目中。你设计它,就一定要考虑到将来如何修改它。

如果你使用 Java 工作,可能会倾向于将大部分类和方法设为 final。

使用 C# 工作,可能可能会密封类和方法。

无论使用哪种语言,你都可能会倾向于:通过单例或使用静态工厂方法来设计 API,以便可以保护它不被覆盖,以及限制别人的使用。

这样真的好吗?

在过去的十年里,作者团队逐渐意识到单元测试是实践中极其重要的一部分,但它并没有完全渗透到整个行业。

API 设计黄金法则:为自己开发的 API 编写测试是不够的;你必须为使用 API 的代码编写单元测试。

这样做,你可以更加清楚的知道他人调 API 所面临的障碍,然后倒逼 API 的设计。

克苏鲁神话

开发过程中,我们偶尔会被同事这样提问:

“我这里这里报这个错,你知道是为什么吗?”

搞笑的是:提出问题的人通常比被问问题的人更有能力作出解答。

克苏鲁神话说人类最原始、最强烈的情感是:恐惧。

当我们问别人问题时,首先需要问自己有没有尽全力去排查、检索?

面对恐惧、克服恐惧,你问的那个人真的还不一定比你更懂这个问题!!

努力不代表回报

作为程序员,努力工作往往没有回报。

你可能会欺骗自己和一些同事,让他们相信你在办公室呆了很长时间,对这个项目做出了更多贡献。

但事实上,你也并没有取得更多成就。

如果你每周工作超过 30 小时保持着“高效”工作,那么那么你可能工作太努力了!你应该考虑减少工作量,提高效率。

软件开发耗时 = 开发耗时 + 持续学习耗时

大多数开发工作都像是一场马拉松,你短期内跑的快,不太可能成功。你需要持续的跑,坚持的跑,看清目标的跑。

除了工作,我们可能还需要阅读书籍、参加会议、和其他专业的人交流、尝试新技术等等。因此,你不能总在工作的项目上!

通过寻找聪明的解决方案来尽可能多地做出贡献,提高你的技能,反思你正在做的事情,并调整你的行为。避免让自己表现得像笼子里的仓鼠一样不停在旋转轮上奔跑。

错误追踪

一个好的错误报告需要:

  • 如何更加精准的复现错误;
  • 它的正确表现应该是怎样的;
  • 实际的错误表现是怎样的;

Bug 就像一场对话,所有的历史暴露在每个人面前。不要责怪他人或否认错误的存在。而是询问更多信息。

追踪 bug ,要不断的去更新 bug 的状态,花时间解释下这个错误为什么发生了,清晰的表达让非开发人员也知道。

确保每个人都知道如何找到团队中处理的错误,可以进行追踪反馈。

最后,请记住,目的不是为了记录错误,追责错误,目的是为了解决错误、避免错误。

用删除来改进代码

乔布斯说:less is more,这句话看着挺简单,但是是真的处处都如此。

奥卡姆剃刀原理与之呼应。

我们可以通过删除代码来改进代码。

这些不必要的代码会基于什么样的理由产生:

  • 有人认为将来可能用到它;
  • 有人觉得写了有趣,但不一定有用;
  • 实现起来更容易,比如引入了一个很大的库,但只用其中一小点;
  • 开发理解了额外的需求,实际不存在;

删了只会让你的代码更清晰,just do it ~

易安装

当我们用一个第三方库的时候,很少会仔细去深究其中原理吧。

只要是大致看了功能和 API ,立即就上手安装了。

但是如果安装是个麻烦的话,还有谁会去坚持使用呢?

如果你要做一个插件,请确保它易安装。同样的,接口也是、第三方库也是、组件也是......

易安装、易卸载,是程序被频繁使用的基础。

进程通信导致延迟

响应时间对软件可用性至关重要。

然而,响应时间差的原因却鲜为人知,或者很神秘。

当性能成为应用程序的大问题时,作者的经验是检查数据结构和算法,并寻求改进的方法。但实际上响应时间很大程度上取决于远程进程通信(IPC)数量。每个远程的进程通信都会对整体响应时间产生一些不可忽视的影响,它们加起来就会成为大问题。

比如数据库的调用,如果调用在顺序中执行,延迟会累计,最终导致长时间的响应时间。

再比如包括 Web 服务调用、来自 Web 浏览器的 HTTP 请求、分布式对象调用、请求回复消息传递,以及通过自定义网络协议进行的数据网格交互.......响应所需的远程 IPC 越多,响应时间就越长。

  • 一种解决策略是应用简约原则,优化进程之间的接口,以便用最少的交互量获取正确的数据。
  • 另一种策略是在可能的情况下并行进程间通信,总体响应时间由延迟最长的 IPC 决定。
  • 第三种策略是缓存先前 IPC 的结果,以便可以通过访问本地缓存来避免将来的 IPC。

重视进程通信导致的延迟,比改变数据结构或调整排序算法会带来更多的回报。

干净的构建

当你构建项目时,是否会出现一串警告⚠?

你确实是知道应该修复它,但是项目还能正常跑,现在确实没时间,下次吧 QAQ

但是!随着代码库的增长,这些警告就会开始堆积,当它足够多的时候,你没办法在数百个警告中找到真正想去阅读、修复的那个单个警告了。

为了使警告变得有用起来,我们必须对构建产生的警告使用零容忍策略。即使警告不重要、不影响运行、不影响生产线,也要去修复它!

拥有干净的构建可以让其他人更轻松的接管我的工作,否则,其他人也无法从众多警告中区分出哪些是重要的,哪些是可忽略的。

本瓜认为:处理构建中的警告和重构代码是一样的,不要企图等到最后来一次大清理、大重构,我们应该提高处理的频次,细化处理的颗粒度,这样才能让代码走的更远~

命令行操作

现今,我们基本都是借助各类开发集成工具(IDE)进行开发,它们带来了足够的便利,减轻了程序猿对各个环节的细致思考的负担,用就完事了!

然而,事物具有两面性,易用性有着它的缺点:我们不清楚 IDE 实际做了什么,只用点点点各种按钮,魔法就相继发生了~

如果使用命令行,我们将更加清楚的知道可执行文件的所有步骤(比如编译、汇编、链接等)。

你可以使用开源命令行工具,比如 GCC,也可以使用专有 IDE 提供的工具。毕竟,设计良好的 IDE 只是一组命令行的图形前端。

如果你习惯使用命令行,你会发现它的功能比 IDE 的功能更强大。既然说 IDE 是一组特定命令行的图形前端,那么走出 IDE ,就意味着你可以完成各种自定义的命令,比如:grep 和 sed 提供的搜索和替换功能比 IDE 更强大。

当然,这里并不是让你放弃使用 IDE,只是给出一个建议:偶尔走出 IDE ,用命令行操作,你可能会发现新大陆~

精通两种及以上语言

每个程序员都从学习一种编程语言开始,该语言对程序员思考方式具有主导作用。无论使用这门语言有多少年的经验,如果只知道这门语言,思维将一直被这门语言限制着。

而学习第二语言将面临挑战,特别是如果该语言的范式与第一语言不同时。C、Pascal、Fortran,具有相同的范式。从 Fortran 切换到 C 将不会太困难,但如果从 Fortran 迁移到 C++,这将是一个巨大挑战,从 C++ 迁移到 Haskell 也同样~

我们可以列举出许多编程范式:面向过程、面向对象、函数式、逻辑、数据流等。

接受挑战是对自身有益的!知识交叉能让你更专业!一种语言无法解决的问题可能在另一门语言中被轻易解决!

编程语言的交叉融合具有巨大的影响。任何精通函数式编程的人都可以轻松地应用声明式方法,使用声明式方法让程序更短、更易于理解。

每个程序员都应该熟练掌握至少两种不同范式的编程技巧,最好是上述五种范式(都给我往死里学)。

可以把学习一门新语言当作一项兼职,它最终一定会使你受益!

花更多时间学习 IDE

1980 年代,语法高亮是一种奢侈品,格式化工具也是一种外部工具,调试器也是一个单独的程序;

1990 年代,公司开始认为为程序猿提供更好的工具可以获得更高受益,于是集成开发环境(IDE)开始发展,图形界面和鼠标也开始流行,开发人员可以轻松的在各种菜单中点点点;

21 世纪,IDE 变得如此普遍,很多还是免费供大家使用的,这让修改代码、搭建项目变得非常容易。

现代 IDE 还有一个惊人的特性就是自定义设置代码规则,这让团队开发更加规范。

不算太好的一点是,现代 IDE 不需要我们投入很多精力来学习它,这导致我们只能认知到它功能以内的东西。

学习 IDE 的快捷键也是很重要的,建议大家花更多时间来研究:如何使用 IDE 让工作更有效~

认知自己的极限

"Man's got to know his limitations." — Dirty Harry

人必须知道他的局限性!

资源是有限的,你只有这么多时间、这么多金钱、这么多努力、这么多聪明......尊重这些限制,是一切的基础。

对于软件工程师也同样,需要认知:系统的架构和性能特征也有它的限制。

复杂性的软件分析是在抽象层面,但软件在真实机器上运行。

现代计算机系统被分为物理机和虚拟机的两种层次结构,包括语言运行时、操作系统、CPU、缓存、随机存取存储器、磁盘驱动器和网络。

下表显示了典型联网服务器的随机访问时间和存储容量的限制。


access time

capacity

register

< 1 ns

64b

cache line


64B

L1 cache

1 ns

64 KB

L2 cache

4 ns

8 MB

RAM

20 ns

32 GB

disk

10 ms

10 TB

LAN

20 ms

> 1 PB

internet

100 ms

> 1 ZB

算法和数据结构在使用缓存的效率方面也各不相同:

Elements

Search time (ns)




线性搜索

排序数组的二分搜索

搜索 van Emde Boas 树

8

50

90

40

64

180

150

70

512

1200

230

100

4096

17000

320

160

清楚你的下一次提交

作者拍了三位程序员的肩膀,问他们在做什么:

第一位说:“我正在重构这些方法”;

第二位说:“我正在向这个网络操作添加一些参数”;

第三位说:““我正在研究这个用户的行为”;

前两位似乎更全神贯注于工作细节,第三位则有着更大的图景。前两个非常清楚将涉及更改哪些文件,并将在一个小时左右完成。第三个程序员则准备在几天内完成这个任务,可能添加一些类、可能会怎样怎样修改服务......

第三位程序员最大的问题在于没有分解问题,任务的颗粒度太大。

如果任务情况发生了变化,前两位可以放弃所作更改,然后重新开始。但第三位由于代码一次性修改太多,将不愿全部丢弃,导致遗留糟糕的代码。

清楚你的下一次提交是什么! 如果你不能完成它,请及时修改。

大型互联数据属于数据库

一旦掌握了 SQL,编写以数据库为中心的应用程序将是一种乐趣(本瓜还没体会过~)。

将正确规范化的数据存储在数据库中后,可以轻松地使用可读的 SQL 查询数据,无需编写任何复杂的代码。

同样,单个 SQL 命令可以执行复杂的数据更改。对于一次性修改,比如改变持久数据的组织方式,您甚至不需要编写代码:只需启动数据库的直接 SQL 接口即可。

在这个相同的界面,还允许你进行测试查询,避开常规编程语言的编译到编辑,再到编译的循环。

高级数据库系统甚至可以在背后利用多核处理器。 而且,随着技术的进步,您的应用程序的性能也会提高。

学习沟通

程序员需要进行很多交流。

通常来说,我们更多时候与计算机进行交流,除此之外,我们也会跟同事交流。

今天的大型项目更多的是社会性的努力,而不仅仅是编程技术的应用。

除了与机器、自我和同行交流之外,一个项目还有许多利益相关者,其中大多数具有不同的技术背景或没有技术背景,包括测试、质检、部署、营销、销售,不同群体的用户等。

如果你不会说他们的语言,就想进入他们的领域,这几乎是不可能的。

如果与会计师交谈,你需要了解中心会计、固定资本、已用资本等方面的基本知识。如果与营销或律师交谈,你应该熟悉他们的一些行话和语言(以及他们的思想)。

所有这些领域特定的语言都需要由项目中的某个人 —— 最好是程序员,来掌握。程序员负责通过计算机将想法变为现实。

当然,生活不仅仅是软件项目。正如查理曼大帝所说,了解另一种语言就是拥有另一种灵魂。

Whereof one cannot speak, thereof one must be silent. - Ludwig Wittgenstein

学会估算

作为程序员,你需要经常对你的任务进行评估,然后提供给经理、同事或用户,以便他们能够对实现目标所需的时间、成本、技术或其它资源有一个准确的把握。

为了能够很好地进行估计,学习一些估计技术显然很重要。

如下项目经理和程序员间的对话并不少见:

项目经理:你能估计下开发功能xyz所需的时间吗?

程序员:一个月。

项目经理:太久了!我们只有一个星期!

程序员:至少需要三个星期。

项目经理:我最多可以给你两个。

程序员:成交!

这个对话中有三个关键的定义:估计、目标、承诺

  • 估计是值,数,数量,或对某物的程度的近似计算或判断,它是基于硬数据和以往经验的事实衡量标准,例:开发任务估计持续 234 天;
  • 目标是一个对理想实现的判断声明,例:系统必须至少支持400个并发用户;
  • 承诺是在某一日期或在一定的质量水平下,提供特定功能的承诺,例: 搜索功能将在产品的下一个版本中可用;

项目经理真正需要的是程序员针对目标做出承诺,而不是估计。

Hello, World

Hello, World 是我们编程的初心。

作者分享了这样一个例子:

他遇到了一个棘手的问题,然后请教了一个解决问题的专家同事,这位同事并没有采取什么高明的办法,而是通过把问题代码从大项目中分离出来,再做测试验证,最终解决了问题;

没错,也许我们已经太久处在一个大项目中了,当遇到一个复杂问题时,如果不能抽象简化为一个原始问题,那么处理起来真的会很麻烦。

回归本地,回归命令行,回归小项目,回归最原始的代码:

#include <; int main() { printf("Hello, World\n"); return 0; }

让项目说话

你的项目可能有一个版本控制系统,它连接着一个持续集成的服务器,能够自动化测试进行验证正确性,能这样做太棒了!

持续集成可以帮助你获取很多构建信息,如果你想让你得测试覆盖率不低于 20%,你可以把这个任务委托给这个项目本身,如果它没有完成这个指标,就会进行报警,输出报告。

你需要让你的项目有发言权。

你可以通过电子邮件或者即时消息来完成,通知开发人员应用程序的情况。也可以通过使用反馈设备(XFD)应用到项目中去。

XFD 原理是根据自动分析的结果来驱动物理设备,例如灯、便携式喷泉、玩具机器人,甚至 USB 火箭发射器。每当你得指标限制被打破,设备就会更改其状态,灯就会亮起来,你也可以听见构建中断的声音,甚至可以设定闻到代码的味道~

如果你在项目经理的办公室放一台这种设备,展示着整个项目的健康状态,他一定对你感激不尽!

让你的项目说话,奇妙的感受不言而喻。

链接器不神奇

不少程序员认识到从源代码到可执行文件的过程是:

  1. 编辑源代码;
  2. 将源代码编译成目标文件;
  3. 神奇的事情发生了;
  4. 运行可执行文件;

作者在做技术支持的几十年来,一直被问到一下问题:

  1. 链接器表明 def 被定义了不止一次;
  2. 链接器表明 abc 是一个未解析的符号;
  3. 为什么我的可执行文件这么大?

紧接着是“我现在该怎么办?”,还带着“似乎”和“不知道什么原因”这种论调。

实际上,这里面没有什么魔法,链接器是一个非常愚蠢、简单、直接的程序。

它所做的只是将目标文件的代码和数据部分连接在一起,将符号的引用与其定义连接起来,将未解析的符号从库中提取出来,并写出一个可执行文件。而已。

没有咒语!没有魔法!

(本瓜对于此条的理解是:作者苦于解答链接器的相关问题,实际上它本身并不复杂,有些问题看起来很复杂,但是实际上是个弟弟,当然这前提是我们理解其原理机制。)

临时解决方案的持久性

我们为什么要有临时的解决方案?

通常有一些直接的问题需要解决,它可能源自开发团队内部,填补一些工具链的空白,也可能来自外部,用于解决一些功能的缺失。

我们有时会采取一些临时的解决方案,它很有用,但是会对代码标准带来冲击(系列第一点就有提到~)。

临时解决方案永远存在,它可能不太符合公认的生产质量,但是它的优势就是快速解决问题。

我们该怎么办?

  1. 避免采用临时解决方案;
  2. 改变项目经理的临时决策;
  3. 保持原样;

如果你的项目非常混乱导致项目频繁停滞,那就需要很认真的思考临时解决方案和项目标准之间的关系了。

愿你能平静地接受你无法改变的事情,有勇气改变你能改变的事情。

接口设计

软件开发中最常见的任务之一是制定接口规范。

接口包括最高抽象级别(用户接口)、最低抽象级别(函数接口)以及介于两者之间的级别(类接口、库接口等)。

无论你是与用户指定他们将如何与系统交互,还是与开发人员合作指定 API,还是声明类的私有函数,界面设计都是工作的重要组成部分。

好的接口是:

  1. 正确使用很容易:在良好的 GUI 中,我们总是能单击正确的图标、按钮或菜单项,因为这是显而易见且容易的事情。在 API 中,同样如此,以正确的值传递正确的参数,这是最自然的;
  2. 使用错误很难:好的 GUI 可以预见人们可能犯的错误,并使他们难以犯错。例如,禁用或删除在当前上下文中没有意义的命令,或者 API 通过允许以任何顺序传递参数来消除参数排序问题;

请记住接口的存在是为了方便用户,而不是创建者。

谨慎设计的隐形

软件设计原则之一 —— 机制透明、信息隐藏。

Google 的主页非常简洁,但是你要检索一个信息时,它背后发生的事情是非常复杂的。

肯能 10% 展现给了用户,另外的 90% 被隐藏了。这样做有一个好处,你的项目有更多空间去操作,因为用户都看不到;也有一个坏处,用户可能认为你没有任何进步,或者说缺乏明显的更新。

隐形可能是危险的! 而显性的表示可以使人们相信进步是真实的而不是幻觉,是有意的而不是无意的,可重复的而不是偶然的。

  • 编写单元测试提供了单元测试的难易程度的证据。它有助于展示你得代码的发展变化;低耦合、高内聚等特性;
  • 运行单元测试可提供有关代码行为的证据。它有助于表明应用程序运行时的质量;
  • 使用公告板和卡片可以使进度变得可见和具体。任务可以分为未开始、进行中或完成,无需参考隐藏的项目管理工具,也无需跟踪程序员虚构的状态报告;
  • 提高开发进度(或缺乏进度)的可见性。可以完整的表明现实;

消息传递解决并发

程序员可能在学习计算机的最开始就会教导如何处理并发,并发是一个较难的问题,要关注线程、信号量、监视器,以及并发访问变量、安全问题等。

但问题的根源是什么?—— 共享内存。

人们讨论几乎所有的并发问题都与共享内存的使用相关:竞争冒险、死锁、活锁等。

要么放弃并发,要么避开共享内存!

放弃并发肯定不可能,那我们应该避开共享内存吗?

实际上,我们可以使用进程和消息传递,而不是使用线程和共享内存作为我们的编程模型。 这里的进程只是指具有执行代码的受保护的独立单元,不一定是操作系统进程。

Erlang(以及之前的 occam)等语言已经表明,进程是一种非常成功的并发和并行系统编程机制。此类系统没有共享内存、多线程系统所具有的同步压力。此外,还有一个正式模型 —— 通信顺序过程 (CSP) —— 可以作为此类系统工程的一部分加以应用。

我们还可以更进一步,引入数据流系统作为一种计算方式。在数据流系统中,没有明确编程的控制流。取而代之的是,建立一个由数据路径连接的运算符的有向图,然后将数据输入系统。由系统内数据的准备情况控制,没有同步问题。

不使用共享内存编程,而是使用消息传递,可能是实现计算机硬件中普遍存在的并行性的系统的最成功方法。

代码写给未来的自己看

我们都是聪明人,但是仍然认为目前所写的项目代码或者所解决的问题,对于后面接受这个代码或问题的人来说应该也是一个不小的困难。甚至是隔了段时间,自己都看不懂自己写的东西了......

作者举了个例子:

他有一个叫做乔的学生,一次数据结构课上,他问乔:“我猜不透你写的代码的作用是什么,你不是有一个弟弟吗?”

乔说:“是的,他也正在学习编程!”

老师问:“我想知道他是否能读懂这段代码。”

乔说:“不,这太难了!”

老师说:“这是真正的工作上的代码,几年后你弟弟会被雇来进行维护更新。你为他做了什么?”

乔说:“我懂了,我要写的更好一点,让菲尔也能看懂!”

......不得不说,这段翻译起来有些尴尬,但是确实是这个道理。

善用多态

多态性是面向对象的基本思想之一。这个词取自希腊语,意思是许多 ( poly ) 形式 ( morph )。在编程中,多态是指特定类的对象或方法的多种形式。

用代码来演示,例如以下实现简单购物车:

public class ShoppingCart { private ArrayList<Item> cart = new ArrayList<Item>(); public void add(Item item) { cart.add(item); } public Item takeNext() { return cart.remove(0); } public boolean isEmpty() { return cart.isEmpty(); } }

假设我们的网上商店提供可以下载的商品和需要发货的商品。再来构建另一个操作对象:

public class Shipping { public boolean ship(Item item, SurfaceAddress address) { ... } public boolean ship(Item item, EMailAddress address { ... } }

当客户完成结帐后,我们需要运送货物:

while (!cart.isEmpty()) { (), ???); }

另一种解决方案是创建两个都扩展 Item 的类。我们称这些为 DownloadableItem 和 SurfaceItem。把 Item 提升为支持单一方法 ship 的接口。要运送购物车的内容,通过致电i(shipper)。

public class DownloadableItem implements Item { public boolean ship(Shipping shipper) { (this, cu()); } } public class SurfaceItem implements Item { public boolean ship(Shipping shipper) { (this, cu()); } }

在这个例子中,我们将处理的责任委托给了每个 Item。由于每件商品都知道它的最佳运输方式,因此这种安排使我们无需if-then-else。

该代码还演示了两种经常一起使用的模式:Command 和 Double Dispatch。这些模式的有效使用依赖于多态性的有效使用。借助它们,我们代码中if-then-else块的数量将会减少。

虽然在某些情况下使用if-then-else比多态更实用,但更多情况下,多态的编码风格将产生更小、更易读和更稳定的代码库。

和测试做朋友

无论把测试叫做质量保证还是质量控制,许多程序员更愿意称他们为“麻烦”。程序员似乎与测试有着敌对关系,因为测试似乎太挑剔了或者他们想要一切都完美......

作者认为:你可能认为测试人员通过写了长篇的测试报告让你觉得自己写的代码很糟糕,但是这会让用户对你有一个更好的反馈~

实际上,测试是帮助我们解决问题的,给力的测试是代码上线质量的最强保障。

被测试怀疑,好过被用户怀疑。

可能一时难以接受:那些总是把代码中的每个小错误暴露出来的测试人员实际上是你的朋友。(本瓜尝试接受~)

环境同步

作者见过几个项目,其中的构建会重写部分代码,为每个目标环境生成自定义二进制文件。

但是这总是使得事情变得比原本更复杂,并给团队带来一定得版本风险,因为大家可能安装版本不一致。

作者建议:构建一个二进制文件,您可以在发布管道中的所有阶段识别和提升它,这与项目代码分离。

保持环境信息版本化! 没有什么比破坏环境配置并且无法弄清楚到底发生了什么更糟糕的了。

环境信息应该与代码分开进行版本控制,因为它们会以不同的速率和不同的原因发生变化。

一些团队为此使用分布式版本控制系统(例如 bazaar 和 git),因为它们可以更轻松地将生产环境中所做的更改(不可避免地发生)推送回存储库。

代码告知真相

  1. 代码才能清楚的告诉别人你的真实意图,需求文档都不一定能说明全部真相;
  2. 注释只是辅助功能,别打算用注释来代替代码进行说明;
  3. 写代码要像写诗一样,精心去表达;

负责构建

  1. 项目的构建脚本也要一直去维护;
  2. 构建过程应该由研发团队负责,而不是给测试团队或其它“质量保障”团队;
  3. 充分了解构建过程,太重要了;

结对编程

  1. 全身心的一人编程很难实现,有太多中断和干扰;
  2. 尝试结对编程,促进交流、团队融合,取长补短,共同进步;
  3. 结对编程,让新同事快速上手;(真会玩~)

精确定义类型

  1. 举了个真实例子:一个卫星因为程序单位计算错误导致失联;
  2. 使用静态类型语言,编译器可以做检查;使用动态类型语言,则依赖单元测试;
  3. 精确定义变量的类型(自定义类型),而不仅仅是使用原始类型 String 或 Float 等;

避免报错

  1. 用户错误的使用程序而造成报错,这些情况是能预测,并采取措施进行避免的;
  2. 提供有效的提示能避免用户操作报错;
  3. 系统需零容忍错误,反思交互,甚至重新设计;

专业程序员

  1. 第一特质是:责任感;
  2. 第二特质是:团队合作;
  3. 第三特质是:不容忍错误;
  4. 第四特质是:手艺人、代码干净;

版本控制

  1. 所有内容都需置于版本控制之下(源代码、文档、构建脚本、测试用例、第三方库等);
  2. 版本控制让一些行为可追踪;
  3. 版本控制减少开发之间的摩擦、冲突;
  4. 版本控制让团队更高效;

放下鼠标离开键盘

  1. 当你想问题几个小时都不能解决,应该放下鼠标离开键盘,出去换换脑子;
  2. 换换脑子之后能有更多创意性的解决想法;

阅读代码

  1. 程序猿喜欢写代码,但是不喜欢读代码;
  2. 思考代码如何易读?
  3. 想提高编程技能,可以通过阅读代码实现;

学习人文

  1. 程序的工作往往不是单纯的写代码,不可避免要与人打交道;
  2. 学习人文知识能提升你的思维;

造轮子

  1. 别急着否定重复造轮子的行为;
  2. 重复造轮子可以帮助你对工作原理有更深的理解;
  3. 重复造轮子更重要的意义是训练、获得经验;

慎用单例模式

  1. 单例模式确实简单,但是实际弊大于利,它不利于可测试性、可维护性;
  2. 单例模式易造成代码单元的隐式依赖,造成耦合;
  3. 单例模式不利于单元测试;
  4. 下次当你考虑实现或访问一个单例时,请再想一想;

代码炸弹

  1. 高度耦合的代码都是代码炸弹;
  2. 有很多方法可以衡量和控制代码的耦合度和复杂度;
  3. 衡量耦合有两个指标:扇入和扇出;
  4. 借助这些指标来进行调优;

还原代码

  1. 可以通过还原代码来理清混乱;(此小点与前文提过的“用删除来改进代码”契合)

单一职责

  1. 单一职责原则是良好设计的基本原则之一;
  2. 它表示一个子系统、模块、类,甚至是一个函数,不应该有多个改变的理由;
  3. 善用单一职责,将因不同原因而改变的事物分开,可创建具有可独立部署的组件结构。

从 yes 开始

  1. 将观点从 no 转变为 yes,再开始工作;
  2. 当别人说了一个荒谬得观点,你先别急着说 no,可以先问一下 why ?
  3. yes 代表着合作;

自动化

  1. 可重复得行为都能应用自动化;
  2. 自动化不仅用于测试;
  3. 在 IDE 中也要自动化;
  4. 自动化操作并不神秘;
  5. 学习自动化可以边做边学;

利用代码分析工具

  1. 测试只是提高代码质量的众多工具之一;
  2. 利用其它代码分析工具,比如 lint 等;
  3. 可以尝试自己自定义代码分析工具;

测试必需的行为

  1. 测试陷阱之一就是:测一些偶然的行为,与功能无关的行为;
  2. 测试需精确、准确;
  3. 测试需对被测单元的接口进行约定,测试行为与所需行为保持一致;

精确而具体地测试

  1. 测试基本行为而不是附带行为;
  2. 测试需要精确、准确;(与上一小点契合)

空余时进行测试

  1. 测试服务器应该利用起夜间和周末等空闲时间;
  2. 长时间运行的测试对于识别内存泄漏和其他稳定性问题至关重要;
  3. 当然,这些前提都是已经应用了自动化测试;

测试保障开发严谨

  1. 软件开发与桥梁建设这类实体工程相似,但最大的不同是:软件开发不需要实体材料;
  2. 对软件的测试尤其重要,软件的构建成本低,可以开发一个完整的生态来进行测试,包括:单元测试、模拟对象、测试工具等等;
  3. 测试保障了软件开发工程的严谨性,就好比建筑工程在图纸上所做的结构分析保障了工程质量;

认清“状态”

  1. 程序员对于“状态”的认定需要非常清晰;
  2. 举了一个例子:作者去喝咖啡,想找店员要一点牛奶,店员回答说:“牛奶紧缺”,作者懵 b 了,“牛奶紧缺”是什么意思?要么就是“有”牛奶,要么就是“没有”牛奶,对于作者来说,“牛奶紧缺”意味着“没有”牛奶吗?
  3. 现实中,我们会对很多状态进行模糊的描述,但是在程序员的世界中,这不被允许。
  4. 认清“状态”可以让你的代码更简洁、健壮;

不要闭门造车

  1. 对程序员的刻板印象是:编程需要独自深度思考;
  2. 但作者是结对编程的死忠粉;(结对编程,前文提过)
  3. 与同事合作解决问题,能提升开发效率;

错误的交织

  1. 举了一个例子:阿波罗 11 号登月舱存在一个着陆器不稳定的错误,但是这个错误被另外一个错误弥补了,以至于登月时它们都没被发现;
  2. 当多个错误交织,修复错误将是一个非常头痛的事情,你将会不断遇到错误陷阱;
  3. 如果遇到这类问题,你需要有一个清醒的头脑,并考虑所有可能性;

Ubuntu

  • Umuntu ngumuntu ngabantu:他人在,故我在。

不说了,这位作者又是结对编程的死忠粉~

不过,有个哲学问题可以探讨:

笛卡尔说:“我思故我在”,而祖鲁族有句谚语:“他人在,故我在”,意味着:与外界的联系和互动,是形成和保持连贯完整的自我印象的重要外在条件。剥夺这些条件后,人会面临失去自我的危险。

使用 Unix

  1. 他钟爱 Unix 胜过 IDE;
  2. Unix 可以非常有效的处理庞大的数据集;
  3. Unix 小而美;
  4. 在 Windows 上,Cygwin 环境可以提供 Unix;
  5. 遵循 Unix 规则,地球都是你的~(QAQ, NB)

算法和数据结构

  1. 举了一个例子:一个银行行长嫌购买的一批电脑太慢,最后发现是因为一个程序消耗了所有 CPU,这个程序就是一个循环算法;
  2. 先让程序能用,不一定是对的;
  3. 选择不同的算法和数据结构将会有很大不同;
  4. 推荐一本书 Donald Knuth 的《计算机编程艺术》;

拒绝冗长日志

  1. 大多日志记录根本无用;
  2. 分布式系统要考虑如何处理外部依赖失败时的日志记录问题;
  3. 日志是监控的一部分,记录它,就要有处理它的打算;
  4. 杂乱的日志投入生产将难以控制;

WET 消耗性能

我们前面说了 DRY 原则(不要重复自己),任何重复都可考虑进行抽象;

而 DRY 的对立原则是 WET(每次输入);

看段代码:遍历集合来进行查询

// WET:使用这个类的每个人都可能重新实现相同的查询; public class UsageExample { private ArrayList<Customer> allCustomers = new ArrayList<Customer>(); // ... public ArrayList<Customer> findCustomersThatSpendAtLeast(Money amount) { ArrayList<Customer> customersOfInterest = new ArrayList<Customer>(); for (Customer customer: allCustomers) { if (amount)) cu(customer); } return customersOfInterest; } } // DRY:从 API 中删除暴露的原始集合,可以轻松避免上述情况; public class CustomerList { private ArrayList<Customer> customers = new ArrayList<Customer>(); private SortedList<Customer> customersSortedBySpendingLevel = new SortedList<Customer>(); // ... public CustomerList findCustomersThatSpendAtLeast(Money amount) { return new CustomerLi(amount)); } } public class UsageExample { public static void main(String[] args) { CustomerList customers = new CustomerList(); // ... CustomerList customersOfInterest = cu(someMinimalAmount); // ... } }

遵循 DRY ,让性能更优。

开发和测试合作

  1. 当程序员和测试人员合作,就能更快弄清错误;
  2. 程序员可以给测试提供建议以更好的覆盖测试;
  3. 程序员和测试人员配合完成自动化,了解良好的编码实践;
  4. 放下成见、合作共赢;

终身维护代码

  1. 编码态度第一,编写代码就好像你必须在你的余生会一直维护它一样;
  2. 这种认知会帮你成为一位专家,因为你会去学习设计模式、编写好的注释、测试代码并不断重构、不断扩展;
  3. 编的不是代码,是态度!!!

编写小函数

  1. 问题的关键都是一些小函数,它可能只有几行,几个变量,几个操作;
  2. 小函数通常回归到一些数学函数;
  3. 用小函数写一些测试用例,是解决问题的关键;

为他人编写测试

  1. 好的测试有完善的测试文档,描述测试场景、原理;
  2. 为他人编写测试,让其他人也能看懂;
  3. 测试你的测试,给出测试报告;
  4. 试着和测试人员换个角色;

关心代码

  1. 写得一手好代码需要努力工作、不断实践、不断关心代码;
  2. 精通算法不代表能写出好的代码,简洁的代码往往是好的代码;
  3. 合格程序员和优秀程序员之间的真正区别在于:态度;(这里与前文小点又契合了,看来态度真的还挺重要!!)
  4. 没有程序员是一座孤岛;
  5. 阅读本文意味着你关心代码,祝福你; O(∩_∩)O

了解客户

  1. 客户不会把自己的需求讲的非常详细,或者说在与客户的沟通中获得的信息是有限的,所以需要多次的互动;
  2. 尽早挑战挑战客户的需求的不合理之处;
  3. 学会用客户的术语与他们沟通,因为身份不同,即使谈论同一个东西,差异也会很大;
  4. 用图表或其它视觉辅助工具和客户沟通,提高信息的保留率;

OK,以上便是本篇分享~~

我是掘金安东尼: 一名人气前端技术博主(文章 100w+ 阅读量)

终身写作者(INFP 写作人格)

坚持与热爱(简书打卡 1000 日)

我能陪你一起度过漫长技术岁月吗(以梦为马)

觉得不错,给个三连吧(这是我最大的动力 )

责任编辑: 鲁达

1.内容基于多重复合算法人工智能语言模型创作,旨在以深度学习研究为目的传播信息知识,内容观点与本网站无关,反馈举报请
2.仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证;
3.本站属于非营利性站点无毒无广告,请读者放心使用!

“如何控制代码质量,如何用代码控制别人的手机,如何把控代码质量,如何提高代码质量,如何保证代码质量”边界阅读