平庸程序员的各类迹象

1.没法从集合的角度思考

从命令式编程过分到函数式和声明式编程,会马上要求你思考将数据集合看成原语来操做,而不是做为标量值。不管你在关系型 数据库(而且不是做为一个对象仓库)中使用 SQL,仍是设计规模会随多处理器线性变化的程序,亦或是你写的代码必需要在拥有 SIMD 能力的芯片(好比现代显卡和电子游戏机)上执行,都须要这种过渡。html

特征算法

只有在带有声明式或函数式编程特性(程序猿应该知道这些特性)的平台上看到这些特征时,下面列出的才算数。数据库

  1. 在 for 或 foreach 的循环里对集合中的每一个元素执行原子操做。
  2. 写的 Map 或 Reduce 函数里包含自定义的循环,在循环里逐一重复执行数据集。
  3. 从服务器抓取大量数据集,并在客户端上计算和,而不是在查询里使用汇集函数。
  4. 函数做用于一个集合中的每一个元素,并在函数开头经过执行一次新的数据库查询来抓取一个关联记录。
  5. 写的业务逻辑函数,例如更新一个用户界面或执行文件 I/O,很不幸地为了某种折衷而伴随有反作用。
  6. 在实体类打开专属的数据库链接或文件操做符,而且在每一个对象的生命周期里都保持链接状态。

补救措施编程

很是有趣,想象着一个发牌的人经过手指在牌里翻转把一副牌切成两堆交叉洗牌,就能让大脑联想到集合,以及如何成批地操做集合。激发人联想的其余想象还有:数组

  • 高速公路上的车流经过一系列收费站(并行处理)。
  • 泉水汇聚成溪流,溪流又汇聚成小河,最后再汇聚成江河(并行分解/汇集函数)。
  • 一个报纸印刷机(协同程序、流水线)。
  • 夹克上的拉链头把拉链齿拉上(简单的联结)。
  • 转移 RNA 加上氨基酸,并在一个核蛋白中加入信使 RNA,就变成了蛋白质(多阶段函数驱动联结,详见 animation)。
  • 在一棵橘子树中,数以亿计的细胞里同时发生着上述的过程,不断地将空气、水和阳光转换成橘子汁(大型分布式集群上的 Map/Reduce)。

若是你正在写一个处理集合的程序,思考一下全部的附加数据和记录,你的函数须要操做它们的每个元素。而且在 Reduce 函数应用到每对数据上以前,使用 Map 函数把它们成对地联结在一块儿。安全

2.缺少批判性思惟

除非你能批判本身的思惟并从中找出缺陷,不然你会错过那些能够在敲代码以前就能解决的问题。若是你也没法评判本身曾经写过的代码,那你只能在不断摸索中以龟速学习。这个问题同时来源于思考怠惰和以自我为中心,所以,这个问题的特征彷佛也来自两个不一样的方向。服务器

特征网络

  1. 自制“业务规则引擎”。
  2. 静态工具类很冗余且庞大,或者多学科的函数库只用一个命名空间。
  3. 把各类应用糅合在一块儿,或给当前的应用附加不相关的特性来避免启动新项目的开销。
  4. 程序架构开始须要创建 epicycle 模型。(译者: epicycle 模型是天文学上使用的模型,用来解释天体在运动过程当中出现的误差等异常行为。)
  5. 为了很不相关的数据向表中添加字段(好比:在通信录的表中放置“# cars owned”字段)。(译者:通信录的内容要记录是否有车干吗,确实扯远了。)
  6. 先后矛盾的命名规范。
  7. 处于“拿着锤子看什么都是钉子”的心态,或者改变对问题的定义,这样全部问题都能用某个特定的技术来解决。
  8. 编写程序下降问题的复杂度。
  9. 从病理上冗余地防护式编程(“企业级代码”)。
  10. 用 XML 从新发明 LISP。

补救措施架构

从Paul 和 Elder 写的《批判性思惟 | Critical Thinking》这样的书入手,控制自我意识,在向朋友或同事发表本身的想法以此寻求评论时,练习抵制为本身辩护的冲动。编程语言

一旦你习惯了别人来检验你的想法,你就会开始自我审视并练习想象这些想法的结果。另外,你也须要培养起区别轻重缓急的能 力(能直觉知道对这种规模的问题,须要花费多少精力比较合适)、用实践验证假设的习惯(这样你就不会高估问题的大小)和面对失败的健康心态(就算艾萨克. 牛顿的地心引力说是错的,但咱们依然爱他并须要他去尝试)。

最后,你必须自律。意识到计划里有缺陷不会让你更高效,除非你有足够的意志力去改正缺陷,并重建手中正在进行的工做。

3.弹球式编程

若是你把面板倾斜得刚恰好,把曲柄拉回到恰好的距离,而且以正确的顺序击中那些凸起的按钮,那么程序就会像弹球同样运行无误:随着指令的执行流程,从条件语句返回,跳过未选中的指令,转向下一次的状态转换。

特征

  1. 用一个 try-catch 代码块包围 Main() 的整个函数体,并在 Catch 分句中重置整个程序(像弹球地沟,掉下去之后从新开始游戏)。
  2. 在强类型的语言中,用字符串或整型来存储那些拥有(能够用)更合适封装类型的值。
  3. 把复杂数据打包成带分隔符的字符串,而后在使用它的每一个函数里解析一遍。
  4. 对输入有歧义的函数,不会用断言(assertion)或方法协定(method contract)。
  5. 使用 Sleep() 来等待另外一个线程完成任务。
  6. 对非枚举类型的值使用 switch 语句,并且分支语句中没有“Otherwise”分句。
  7. 用 Automethods 或 Reflection 来调用在非法的用户输入中提到的方法。
  8. 在函数里经过设置全局变量来返回多个值。
  9. 类里有一个方法和几个字段,经过设置字段来为方法传递参数。
  10. 不用事务来更新多行数据库内容。
  11. 背注一掷(好比,试图不用事务和 ROLLBACK 来恢复数据库的状态)。

补救措施

把程序的输入想象成水。它即将流过每个缝隙,灌满每个容器。那么你要想想,若是它流过的地方并无明确建立任何东西去呈接它的话,会形成什么后果。

你要让本身熟悉平台的机制,这有助于写出健壮且易扩展的程序。共有三种基础机制:

  1. 当某种意外发生时,能在产生任何破坏以前中止程序,而后帮助你识别出是哪里出错了(类型体系、断言、异常等)。
  2. 将程序的执行导向处理意外最佳的代码块( try-catch 模块、多重分发、基于事件驱动编程等)。
  3. 暂停线程直到一切就绪(WaitUntil 命令、互斥锁和信号量、同步锁等)。

还有第四条,单元测试,你能够在设计阶段使用。

使用这些机制应该成为你的次日性,就像在句子里用逗号和句号同样。为了作到这些,每次浏览一遍上面介绍的机制(括号里 提到的那些),并重构你的旧程序,把提到的这些机制塞到任何能塞的地方,就算最后发现这么作并不合适(尤为是在它们看似不合适的时候,至少那时你也开始明 白其中的原因)。

4.不熟悉安全原则

若是要说下述特征并不很严重,但它们几乎是大部分程序都存在的总体质量问题。意思是说,这些特征不会让你成为一名很糟糕的程序猿,只是意味着你不该该从事网络程序或安全系统的工做,直到你已经在这方面作了一些功课。

特征

  1. 以明文形式存储可利用信息(名字、卡号、密码等)。
  2. 用低效的加密术存储可利用信息(将密码编译在程序中的对称加密算法;简单密码;任何“解码环( decoder-ring )”、自创加密算法、专有的或未验证的加密算法)。
  3. 在接受网络链接或解释来自非置信源的输入信息以前,程序或设备没有限制它们的权限。
  4. 不进行边界检查或输入合法性验证,尤为是在使用非托管类的语言时。
  5. 把不合法或非转义的输入串接到字符串上来构建SQL查询。
  6. 调用用户输入中指定的程序。
  7. 试图经过搜索已知漏洞的签名(signature)来阻止漏洞被利用。
  8. 用不加盐的哈希值(unsalted hash)存储信用卡卡号或密码。

补救措施

下面只涵盖了基本原则,但遵守这些原则会避免绝大多数臭名昭著的错误,那些错误可让整个系统大打折扣。对于任何处理或存储有价值信息的系统,不管是向你仍是其用户,或是控制一个贵重资源的系统,它们一般都有一个安全专家来审查系统的设计与实现。

从审查程序开始,找出用数组或其余配置内存的容器来存储输入的代码,确保这部分代码检查了输入的大小不会超出分配给它的 内存大小。没有其余类型的 bug 能比缓冲区溢出更能致使可利用的安全漏洞。从某个层面来讲,在写网络通讯程序或任何安全第一的场合下,你应该认真考虑使用某种内存托管型的编程语言。

下一步,审查数据库查询操做。审查那些将未修改输入串接到 SQL 查询内容中的查询操做,而且,若是平台支持的话就切换为使用参数化查询,若是不支持就对输入进行过滤或转义。这么作是为了防止 SQL 注入攻击。

在你清除了这两类最臭名昭著的安全 bug 以后,你应该继续将全部的的程序输入视为彻底不可靠,或是有潜在恶意。按有效的验证规则来定义程序的输入很重要,并且除非输入能经过验证,不然程序应该拒 绝它,这样你就可以经过修复验证方法并使其更加明确来修复可利用的漏洞,而不是经过扫描已知漏洞的签名来修复漏洞。

进一步说,你应该老是在开始设计程序以前,思考程序须要执行的操做以及这些操做须要从 host 得到什么样的权限,由于这个时候是想出怎么样能尽量使用最少权限的最佳时机。这条建议背后的原则是,若是在你的代码中找到一个可利用的 bug ,限制这个bug可能对系统其余部分形成的损害。换言之:在你学会不信任输入以后,你也应该学会不要信任本身写的程序。

最后你要学会的是数据加密基础,从《Kerckhoff’s principle》开始。这一点亦可表达为“安全第一”,从中还衍伸出了一些有趣之处。

原则一,永远不要信任一个密码或其余加密原语,除非它已经被公开发表,而且已经由更高级别的安全社区对其进行了全面的分 析和测试。从密码学的发展来看,模糊晦涩的加密法、专有的加密法或是新出现的加密法都毫无安全可言。即便是可信的加密原语,其实现中也会存在缺陷,所以, 对于你不能肯定其已经获得全面审查的加密算法(包括本身实现的版本),要避免使用。全部的新型加密系统都要通过一系列的详细审查,这个过程可能长达十年之 久,或更长,而你只要关注那些最后经受住了审查而且全部已知错误都已修复的加密系统。

原则二,若是密钥容易破解或存储失当,那这和彻底不加密同样糟糕。若是程序要对数据加密,但不须要解密或不多须要解密,那就考虑只把对称加密密钥对的公钥给它,并让解密阶段和私钥分开运行,用户必须每次输入一个好的口令来确保密钥的安全。

越是处于危险之中,你须要作的功课越多,而且必须在程序的设计阶段投入更多精力。这都是由于一旦你的程序部署下去,就会有成堆、有时候多是成千上万的不速之客试图去破坏它的安全性。

绝大部分可追溯到代码问题的安全故障都归因于一些很愚蠢的错误,其中大部分错误能够经过筛选输入、谨慎使用资源、利用常识、想清楚再写代码等方式来避免。

5. 代码一塌糊涂

特征

  1. 不遵循一向的命名规范。
  2. 不使用缩进,或缩进不一致。
  3. 不使用空格,例如在方法之间不加空格(或表达式里不加空格,看“ANDY=NO”)。
  4. 有一大堆被注释掉的代码。

补救措施

程序猿在匆忙之下(或特殊状况下)犯了上述全部毛病的话,会在以后返回来清理,但一个糟糕的程序猿真的就只是粗枝大叶。 有时,利用可经过快捷键来修复缩进和空格(“美观的格式”)的 IDE 是很帮助的,但我发现程序猿老是把代码搞得一团糟,极大地违背 Visual Studio 对适当缩进的坚持。

相关文章
相关标签/搜索