高效程序员的45 个习惯

【高清完整pdf】高效程序员的45个习惯 敏捷开发修炼之道 第1章 敏捷——高效软件开发之道 第2章 态度决定一切 1. 做事 2. 欲速则不达 3. 对事不对人 4. 排除万难,奋勇前进 第3章 学无止境 5. 跟踪变化 6. 对团队投资 7. 懂得丢弃 8. 打破砂锅问到底 9. 把握开发节奏 第4章 交付用户想要的软件 10. 让客户做决定 11. 让设计指导而不是操纵开发 12. 合理地使用技术 13. 保持可以发布 14. 提早集成,频繁集成 15. 提早实现自动化部署 16. 使用演示获得频繁反馈 17. 使用短迭代,增量发布 18. 固定的价格就意味着背叛承诺 第5章 敏捷反馈 19. 守护天使 20. 先用它再实现它 21. 不同环境,就有不同问题 22. 自动验收测试 23. 度量真实的进度 24. 倾听用户的声音 第6章 敏捷编码 25. 代码要清晰地表达意图 26. 用代码沟通 27. 动态评估取舍 28. 增量式编程 29. 保持简单 30. 编写内聚的代码 31. 告知,不要询问 32. 根据契约进行替换 第7章 敏捷调试 33. 记录问题解决日志 34. 警告就是错误 35. 对问题各个击破 36. 报告所有的异常 37. 提供有用的错误信息 第8章 敏捷协作 38. 定期安排会面时间 39. 架构师必须写代码 40. 实行代码集体所有制 41. 成为指导者 42. 允许大家自己想办法 43. 准备好后再共享代码 44. 做代码复查 45. 及时通报进展与问题 第9章 尾声:走向敏捷 9.1 只要一个新的习惯 9.2 拯救濒临失败的项目 9.3 引入敏捷:管理者指南 9.4 引入敏捷:程序员指南 9.5 结束了吗 附录A 资源 索引 立即下载

原文来自:http://blog.youkuaiyun.com/turingbooks/archive/2009/11/21/4840453.aspx

 

【45个习惯】

1    做事

2    快速修复变成了快速流沙

3    对事不对人

4    排除万难,奋勇前进

5    跟踪变化

6    对团队投资

7    懂得丢弃

8    打破砂锅问到底

9    把握开发节奏

10  让客户做决定

11  让设计指导开发,而不是操纵开发

12  合理地使用技术

13  保持可以发布

14  提早集成,频繁集成

15  提早实现自动化部署

16  频繁地演示获得用户反馈

17  使用短迭代,增量发布

18  固定的价格就意味着背叛承诺

19  守护天使

20  先用它再实现它

21  不同环境,就有不同问题

22  自动验收测试

23  度量真实的进度

24  倾听用户的声音

25  代码要清晰地表达意图

 

代码要清晰地表达意图

—— 高效程序员的 45 个习惯之 习惯25

 

“可以工作而且易于理解的代码挺好,但是让人觉得聪明更加重要。别人给你钱是因为你脑子好使,让我们看看你到底有多聪明。”
 

Hoare 谈软件设计

C.A.R. Hoare

设计软件有两种方式。一种是设计得尽量简单,并且明显没有缺陷。另一种方式是设计得尽量复杂,并且没有明显的缺陷。

我们大概都见过不少难以理解和维护的代码,而且(最坏的是)还有错误。当开发人员们像一群旁观者见到 UFO 一样围在代码四周,同样也感到恐惧、困惑与无助时,这个代码的质量就可想而知了。如果没有人理解一段代码的工作方式,那这段代码还有什么用呢?

 

开发代码时,应该更注重可读性,而不是只图自己方便。代码被阅读的次数要远远超过被编写的次数,所以在编写的时候值得花点功夫让它读起来更加简单。实际上,从衡量标准上来看,代码清晰程度的优先级应该排在执行效率之前。

 

例如,如果默认参数或可选参数会影响代码可读性,使其更难以理解和调试,那最好明确地指明参数,而不是在以后让人觉得迷惑。

 

在改动代码以修复 bug 或者添加新功能时,应该有条不紊地进行。首先,应该理解代码做了什么,它是如何做的。接下来,搞清楚将要改变哪些部分,然后着手修改并进行测试。作为第 1 步的理解代码,往往是最难的。如果别人给你的代码很容易理解,接下来的工作就省心多了。要敬重这个黄金法则,你欠他们一份情,因此也要让你自己的代码简单、便于阅读。

 

明白地告诉阅读程序的人,代码都做了什么,这是让其便于理解的一种方式。让我们看一些例子。

 

coffeeShop.PlaceOrder(2);

 

通过阅读上面的代码,可以大致明白这是要在咖啡店中下一个订单。但是, 2 到底是什么意思?是意味着要两杯咖啡?要再加两次?还是杯子的大小?要想搞清楚,唯一的方式就是去看方法定义或者文档,因为这段代码没有做到清晰易懂。

 

所以我们不妨添加一些注释。

 

coffeeShop.PlaceOrder(2 /* large cup */);

 

现在看起来好一点了,但是注释有时候是用来对写得很差的代码进行补偿的(见第 105 页中习惯 26 : 用代码沟通 ) 。

 

Java 5 与 .NET 中有枚举值的概念,我们不妨使用一下。使用 C# ,我们可以定义一个名为 CoffeeCupSize 的枚举,如下所示。

 

public enum CoffeeCupSize

{

  Small,

  Medium,

  Large

              }

接下来就可以用它来下单要咖啡了。

 

        coffeeShop.PlaceOrder(CoffeeCupSize.Largxe);

 

这段代码就很明白了,我们是要一个大杯 [① ] 的咖啡。


作为一个开发者,应该时常提醒自己是否有办法让写出的代码更容易理解。下面是另一个例子。

 

Line 1   public int compute(int val)

     -  {

     -     int result = val << 1;

     -    //... more code ...

     5     return result;

     -  }

第 3 行中的位移操作符是用来干什么的?如果善于进行位运算,或者熟悉逻辑设计或汇编编程,就会明白我们所做的只是把 val 的值乘以 2 。

 

      PIE 原则

      所写的代码必须明确表达你的意图,而且必须富有表现力。这样可以让代码更易于被别人阅读和理解。代码不让人迷惑,也就减少了发生潜在错误的可能。代码要清晰地表达意图。

 

但对没有类似背景的人们来说,又会如何 —— 他们能明白吗?也许团队中有一些刚刚转行做开发、没有太多经验的成员。他们会挠头不已,直到把头发抓下来 ② ] 。代码执行效率也许很高,但是缺少明确的意图和表现力。

 

用位移做乘法,是在对代码进行不必要且危险的性能优化。 result=val*2 看起来更加清晰,也可以达到目的, 而且对于某种给定的编译器 来说 ,可能效率更高(积习难改,见第 34 页的习惯 7 )。不要表现得好像很聪明似的,要遵循 PIE 原则:代码要清晰地表达意图。

 

要是违反了 PIE 原则,造成的问题可就不只是代码可读性那么简单了 —— 它会影响到代码的正确性。下列代码是一个 C# 方法,试图同步对 CoffeeMaker 中 MakeCoffee() 方法进行调用。

 

Public void MakeCoffee()

{

    lock(this)

    {

      // ... operation

    }

}

 

这个方法的作者想设置一个临界区( critical section ) —— 任何时候最多只能有一个线程来执行 operation 中的代码。要达到这个目的,作者在 CoffeeMaker 实例中声明了一个锁。一个线程只有获得这个锁,才能执行这个方法。(在 Java 中,会使用 synchronized 而不是 lock ,不过想法是一样的。)

 

对于 Java 或 .NET 程序员来说,这样写顺理成章,但是其中有两个小问题。首先,锁的使用影响范围过大;其次,对一个全局可见的对象使用了锁。我们进一步来看看这两个问题。

 

假设 Coffeemaker 同时可以提供热水,因为有些人希望早上能够享用一点伯爵红茶。我想同步 GetWater() 方法,因此调用其中的 lock(this) 。这会同步任何在 CoffeeMaker 上使用 lock 的代码,也就意味着不能同时制作咖啡以及获取热水。这是开发者原本的意图吗?还是锁的影响范围太大了?通过阅读代码并不能明白这一点,使用代码的人也就迷惑不已了。

 

同时, MakeCoffee() 方法的实现在 CoffeeMaker 对象上声明了一个锁,而应用的其他部分都可以访问 CoffeeMaker 对象。如果在一个线程中锁定了 CoffeeMaker 对象实例,然后在另外一个线程中调用那个实例之上的 MakeCoffee() 方法呢?最好的状况也会执行效率很差,最坏的状况会带来死锁。

 

让我们在这段代码上应用 PIE 原则,通过修改让它变得更加明确吧。我们不希望同时有两个或更多的线程来执行 MakeCoffee() 方法。那为什么不能为这个目的创建一个对象并锁定它呢?

 

Private object makeCoffeeLock = new Object();

 

Public void MakeCoffee()

{

    lock (makeCoffeeLock)

    {

      // ... operation

    }

}

 

这段代码解决了上面的两个问题 —— 我们通过指定一个外部对象来进行同步操作,而且更加明确地表达了意图。

 

在编写代码时,应该使用语言特性来提升表现力。使用方法名来传达意向,对方法参数的命名要帮助读者理解背后的想法。异常传达的信息是哪些可能会出问题,以及如何进行防御式编程,要正确地使用和命名异常。好的编码规范可以让代码变得易于理解,同时减少不必要的注释和文档。

 

要编写清晰的而不是讨巧的代码
向代码阅读者明确表明你的意图。可读性差的代码一点都不聪明。
 
切身感受

应该让自己或团队的其他任何人,可以读懂自己一年前写的代码,而且只读一遍就知道它的运行机制。

平衡的艺术

  • 现在对你显而易见的事情,对别人可能并不显然,对于一年以后的你来说,也不一定显然。不妨将代码视作不知道会在未来何时打开的一个时间胶囊。
  • 不要明日复明日。如果现在不做的话,以后你也不会做的。
  • 有意图的编程并不是意味着创建更多的类或者类型。这不是进行过分抽象的理由。
  • 使用符合当时情形的耦合。例如,通过散列表进行松耦合,这种方式适用于在实际状况中就是松耦合的组件。不要使用散列表存储紧密耦合的组件,因为这样没有明确表示出你的意图。

 


 

 


[ ① ]      对星巴克的粉丝来说,这是指 venti 。

[ ② ]   没错,那不是一块秃顶,而是一个编程机器的太阳能电池板。

 

26  用代码沟通

27  动态评估取舍

 

动态评估取舍

——高效程序员的45个习惯之习惯27

“性能、生产力、优雅、成本以及上市时间,在软件开发过程中都是至关重要的因素。每一项都必须达到最理想状态。”
 

可能曾经身处这样的团队:管理层和客户 将很大一部分注意力都放在应用的界面展示上。也有这样的团队,其客户认为性能表现非常重要。在团队中,你可能会发现,有这样一个开发主管或者架构师,他会 强调遵守“正确”的范式比其他任何事情都重要。对任何单个因素如此独断地强调,而不考虑它是否是项目成功的必要因素,必然导致灾难的发生。

 

强调性能的重要性情有可原,因为恶劣的 性能表现会让一个应用在市场上铩羽而归。然而,如果应用的性能已经足够好了,还有必要继续投入精力让其运行得更快一点吗?大概不用了吧。一个应用还有很多 其他方面的因素同样重要。与其花费时间去提升千分之一的性能表现,也许减少开发投入,降低成本,并尽快让应用程序上市销售更有价值。

 

举例来说,考虑一个必须要与远程 Windows 服务器进行通讯的 .NET Windows 应用程序。可以选择使用 .NET Remoting 技术或 Web Services 来实现这个功能。现在,针对使用 Web Services 的提议,有些开发者会说:“我们要在 Windows 之间进行通信,通常此类情况下,推荐使用 .NET Remoting 。而且, Web Services 很慢,我们会遇到性能问题。”嗯,一般来说确实是这样。

 

然而,在这个例子中,使用 Web Services 很容易开发。对 Web Services 的性能测试表明 XML 文档很小,并且相对应用程序自己的响应时间来讲,花在创建和解析 XML 上的时间几乎可以忽略不计。使用 Web Services 不但可以在短期内节省开发时间,且在此后团队被迫使用第三方提供的服务时, Web Services 也是个明智的选择。

 

Andy 说。。。
过犹不及
我曾经遇到这样一个客户,他们坚信可配置性的重要性,致使他们的应用有大概10 000个可配置变量。新增代码变得异常艰难,因为要花费大量时间来维护配置应用程序和数据库。但是他们坚信需要这种程度的灵活性,因为每个客户都有不同的需求,需要不同的设置。
可实际上,他们只有19个客户,而且预计将来也不会超过50个。他们并没有很好地去权衡。

 

考虑这样一个应用,从数据库中读取数据,并以表格方式显示。你可以使用一种优雅的、面向对象的方式,从数据库中取数据,创建对象,再将它们返回给 UI 层。在 UI 层中,你再从对象中拆分出数据,并组织为表格方式显示。除了看起来优雅之外,这样做还有什么好处吗?

 

也许你只需要让数据层返回一个 dataset 或数据集合,然后用表格显示这些数据即可。这样还可以避免对象创建和销毁所耗费的资源。如果需要的只是数据展示,为什么要创建对象去自找麻烦呢?不按书上说的 OO 方式来做,可以减少投入,同时获得性能上的提升。当然,这种方式有很多缺点,但问题的关键是要 多长个心眼儿 ,而不是总按照习惯的思路去解决问题。

 

总而言之,要想让应用成功,降低开发成本与缩短上市时间,二者的影响同样重要。由于计算机硬件价格日益便宜,处理速度日益加快,所以可在硬件上多投入以换取性能的提升,并将节省下来的时间放在应用的其他方面。

 

当然,这也不完全对。如果硬件需求非常庞大,需要一个巨大的计算机网格以及众多的支持人员才能维持其正常运转(比如类似 Google 那样的需求),那么考虑就要向天平的另一端倾斜了。

 

但是谁来最终判定性能表现已经足够好,或是应用的展现已经足够“炫”了呢?客户或是利益相关者必须进行评估,并做出相关决定(见第 45 页中 习惯 10 )。如果团队认为性能上还有提升的空间,或者觉得可以让某些界面看起来更吸引人,那么就去咨询一下利益相关者,让他们决定应将重点放在哪里。

 

没有适宜所有状况的最佳解决方案。你必须对手上的问题进行评估,并选出最合适的解决方案。每个设计都是针对特定问题的 —— 只有明确地进行评估和权衡,才能得出更好的解决方案。

 

没有最佳解决方案 (No best solution)

 
动态评估权衡
考虑性能、便利性、生产力、成本和上市时间。如果性能表现足够了,就将注意力放在其他因素上。不要为了感觉上的性能提升或者设计的优雅,而将设计复杂化。
 

切身感受

即使不能面面俱到,你也应该觉得已经得到了最重要的东西 —— 客户认为有价值的特性。

平衡的艺术

  • 如果现在投入额外的资源和精力,是为了将来可能得到的好处,要确认投入一定要得到回报(大部分情况下,是不会有回报的)。真正的高性能系统,从一开始设计时就在向这个方向努力。
  • 过早的优化是万恶之源。
  • 过去用过的解决方案对当前的问题可能适用,也可能不适用。不要事先预设结论,先看看现在是什么状况。

 

28  增量式编程

29  保持简单

30  编写内聚的代码

31  告知,不要询问

32  根据契约进行替换

33  记录问题解决日志

 

记录问题解决日志

—— 高效程序员的 45 个习惯之习惯33

“在开发过程中是不是经常遇到似曾相识的问题?这没关系。以前解决过的问题,现在还是可以解决掉的。”
 

面对问题(并解决它们)是开发人员的一种生活方式。 当问题发生时,我们希望赶紧把它解决掉。如果一个熟悉的问题再次发生,我们会希望记起第一次是如何解决的,而且希望下次能够更快地把它搞定。然而,有时一 个问题看起来跟以前遇到的完全一样,但是我们却不记得是如何修复的了。这种状况时常发生。

 

不能通过 Web 搜索获得答案吗?毕竟互联网已经成长为如此令人难以置信的信息来源,我们也应该好好加以利用。从 Web 上寻找答案当然胜过仅靠个人努力解决问题。可这是非常耗费时间的过程。有时可以找到需要的答案,有时除了找到一大堆意见和建议之外,发现不了实质性的解决方案。看到有多少开发人员遇到同样的问题,也许会感觉不错,但我们需要的是一个解决办法。

 

要想得到更好的效果,不妨维护一个保存曾遇到的问题以及对应解决方案的日志。这样,当问题发生时,就不必说:“嘿,我曾碰到过这个问题,但是不记得是怎么解决的了。”可以快速搜索以前用过的方法。工程师们已经使用这种方式很多年了,他们称之为 每日日志 ( daylog )。

 

不要在同一处跌倒两次                     Don't get burned twice
 

可以选择符合需求的任何格式。下面这些条目可能会用得上。

  • 问题发生日期。
  • 问题简述。
  • 解决方案详细描述。
  • 引用文章或网址,以提供更多细节或相关信息。
  • 任何代码片段、设置或对话框的截屏,只要它们是解决方案的一部分,或者可以帮助更深入地理解相关细节。

要将日志保存为可供计算机搜索的格式,就可以进行关键字搜索以快速查找细节。图 7-1 展示了一个简单的例子,其中带有超链接以提供更多信息。

图 7-1  带有超链接的解决方案条目示例

如果面临的问题无法在日志中找到解决方案,在问题解决之后,要记得马上将新的细节记录到日志中去。

 

要共享日志给其他人,而不仅仅是靠一个人维护。把它放到共享的网络驱动器中,这样其他人也可以使用。或者创建一个 Wiki ,并鼓励其他开发人员使用和更新其内容。

 

维护一个问题及其解决方案的日志。

保留解决方案是修复问题过程的一部分,以后发生相同或类似问题时,就可以很快找到并使用了。

 

切身 感受

解决方案日志应该作为思考的一个来源,可以在其中发现某些特定问题的细节。对于某些类似但是有差异的问题,也能从中获得修复的指引。

平衡的艺术

  • 记录问题的时间不能超过在解决问题上花费的时间。要保持轻量级和简单,不必达到对外发布式的质量。
  • 找到以前的解决方法非常关键。使用足够的关键字,可以帮助你在需要的时候发现需要的条目。
  • 如果通过搜索 Web ,发现 没人 曾经遇到同样的问题,也许搜索的方式有问题。
  • 要记录发生问题时应用程序、应用框架或平台的特定版本。同样的问题在不同的平台或版本上可能表现得不同。
  • 要记录团队做出一个重要决策的原因。否则,在 6~9 个月之后,想再重新回顾决策过程的时候,这些细节就很难再记得了,很容易发生互相指责的情形。

 

34  警告就是错误

35  对问题各个击破

 

对问题各个击破

——  高效程序员的 45 个习惯之习惯35

“逐行检查代码库中的代码确实很令人恐惧。但是要调试一个明显的错误,只有去查看整个系统的代码,而且要全部过一遍。毕竟你不知道问题可能发生在什么地方,这样做是找到它的唯一方式。”
   

单元测试(在第 76 页, 第 5 章)带来的积极效应之一,是它会强迫形成代码的分层。要保证代码可测试,就必须把它从周边代码中解脱出来。如果代码依赖其他模块,就应该使用 mock 对象,来将它从其他模块中分离开。这样做不但让代码更加健壮,且在发生问题时,也更容易定位来源。

否则,发生问题时有可能无从下手。也许可以先使用调试器,逐行执行代码,并试图隔离问题。也许在进入到感兴趣的部分之前,要运行多个表单或对话框,这会导致更难发现问题的根源。你会发现自己陷入整个系统之中,徒然增加了压力,而且降低了工作效率。

大型系统非常复杂 —— 在执行过程中会有很多因素起作用。从整个系统的角度来解决问题,就很难区分开,哪些细节对要定位的特定问题产生影响,而哪些细节没有。

答案很清晰:不要试图马上了解系统的所有细节。要想认真调试,就必须将有问题的组件或模块与其他代码库分离开来。如果有单元测试,这个目的就已经达到了。否则,你就得开动脑筋了。

比如,在一个时间紧急的项目中(哪个项目的时间不紧急呢 ), Fred 和 George 发现他们面对的是一个严重的数据损毁问题。要花很多精力才能知道哪里出了问题,因为开发团队没有将数据库相关的代码与其他的应用代码分离开。他们无法将问题报告给软件厂商,当然不能把整个代码库用电子邮件发给人家!

于是,他们俩开发了一个小型的原型系统,并展示了类似的症状;然后将其发送给厂商作为实例,并询问他们的专家意见,使用原型帮助他们对问题理解得更清晰。

而且,如果他们 无法 在原型中再现问题的话,原型也可以告诉他们可以工作的代码示例,这也有助于分离和发现问题。

识别复杂问题的第一步,是将它们分离出 来。既然不可能在半空中试图修复飞机引擎,为什么还要试图在整个应用中,诊断其中某个组成部分的复杂问题呢?当引擎被从飞机中取出来,而且放在工作台上之 后,就更容易修复了。同理,如果可以隔离出发生问题的模块,也更容易修复发生问题的代码。

    分离原型                          Prototype to isolate

可是,很多应用的代码在编写时没有注意到这一点,使得分离变得特别困难。应用的各个构成部分之间会彼此纠结:想把这个部分单独拿出来,其他的会紧随而至。 在这些状况下,最好花一些时间把关注的代码提取出来,而且创建一个可让其工作的测试环境。

对问题各个击破,这样做有很多好处:通过将问题与应用其他部分隔离开,可以将关注点直接放在与问题相关的议题上;可以通过多种改变,来接近问题发生的核心— —你不可能针对正在运行的系统来这样做。可以更快地发现问题的根源所在,因为只与所需最小数量的相关代码发生关系。

隔离问题不应该只在交付软件之后才着手。在构建系统原型、调试和测试时,各个击破的战略都可以起到帮助作用。

对问题各个击破
在解决问题时,要将问题域与其周边隔离开,特别是在大型应用中。

切身感受

面对必须要隔离的问题时,感觉就像在一个茶杯中寻找一根针,而不是大海捞针。

平衡的艺术

  • 如果将代码从其运行环境中分离后,问题消失不见了,这有助于隔离问题。
  • 另一方面,如果将代码从其运行环境中分离后,问题 还在 ,这也有助于隔离问题。
  • 以 二分查找 的方式来定位问题是很有用的。也就是说,将问题空间分为两半,看看哪一半包含问题。再将包含问题的一半进行二分,并不断重复这个过程。
  • 在向问题发起攻击之前,先查找你的解决问题日志

 

36  报告所有的异常

37   提供有用的错误信息

 

提供有用的错误信息

——  高效程序员的 45 个习惯之习惯37

“不要吓着用户,吓程序员也不行。要提供给他们干净整洁的错误信息。要使用类似‘用户错误。替换,然后继续。’这样让人舒服的词句。”

      当应用发布并且在真实世界中得到使用之后,仍然会发生这样那样的问题。比如计算模块可能出错,与数据库服务器之间的连接也可能丢失。当无法满足用户需求时,要以优雅的方式进行处理。

 类似的错误发生时,是不是只要弹出一 条优雅且带有歉意的信息给用户就足够了?并不尽然。当然了,显示通用的信息,告诉用户发生了问题,要好过由于系统崩溃造成应用执行错误的动作,或者直接关 闭(用户会因此感到困惑,并希望知道问题所在)。然而,类似“出错了”这样的消息,无法帮助团队针对问题做出诊断。用户在给支持团队打电话报告问题时,我 们希望他们提供足够多且好的信息,以帮助尽快识别问题所在。遗憾的是,用很通用的错误消息,是无法提供足够的数据的。

针对这个问题,常用的解决方案是记录日志:当发生问题时,让应用详细记录错误的相关数据。错误日志最起码应该以文本文件的形式维护。不过也许可以发布到一个系统级别的事件日志中。可以使用工具来浏览日志,产生所有日志信息的 RSS feed ,以及诸如此类的辅助方式。

 记录日志很有用,可是单单这样做是不够的:开发人员认真分析日志,可以得到需要的数据;但对于不幸的用户来说,起不到任何帮助作用。如果展示给他们类似下图 中的信息,他们还是一点头绪都没有 —— 不知道自己到底做错了什么,应该怎么做可以绕过这个错误,或者在给技术支持打电话时,应该报告什么。   

如果你注意的话,在开发阶段就能发现这个问题的早期警告。作为开发人员,经常要将自己假定为用户来测试新功能。要是错误信息很难理解,或者无助于定位错误的话,就可以想想真正的用户和支持团队,遇到这个问题时会有多么困难了(见图 7-2 )。


图 7-2  无用的异常信息

 

例如,假定登录 UI 调用了应用的中间层,后台向数据访问层发送了一个请求。由于无法连接数据库,数据访问层抛出一个异常。这个异常被中间层用自己的异常包裹起来,并继续向上传递。那么 UI 层应该怎么做呢?它至少应该让用户知道发生了系统错误,而不是由用户的输入引起的。

 接下来,用户会打电话并且告诉我们他无法登录。我们怎么知道问题的实质是什么呢?日志文件可能有上百个条目,要找到相关的细节非常困难。

 实际上,不妨在显示给用户的信息中提供更多细节。好比说,可以看到是哪条 SQL 查询或存储过程发生了错误;这样可以很快找到问题并且修正,而不是浪费大把的时间去盲目地碰运气。不过另一方面,在生产系统中,向用户显示数据连接问题的特定信息,不会对他们有多大帮助。而且有可能吓他们一跳。

 一方面要提供给用户清晰、易于理解的问题描述和解释,使他们有可能寻求变通之法。另一方面,还要提供具备关于错误的详细技术细节给用户,这样方便开发人员寻找代码中真正的问题所在。

 下面是一种同时实现上述两个目的方式:图中显示了清晰的错误说明信息。该错误信息不只是简单的文本,还包括了一个超链接。用户、开发人员、测试人员都可以由此链接得到更多信息,如图7- 3 、图 7-4 所示。

 

 

图7-3 带有更多细节链接的异常信息

 

图 7-4  供调试用的完整详细信息

 

进入链接的页面,可以看到异常(以及所 有嵌套异常)的详细信息。在开发时,我们可能希望只要看到这些细节就好了。不过,当应用进入生产系统后,就不能把这些底层细节直接暴露给用户了,而要提供 链接,或是某些访问错误日志的入口。支持团队可以请用户点击错误信息,并读出错误日志入口的相关信息,这样支持团队可以很快找到错误日志中的特定细节。对 于独立系统来说,点击链接,有可能会将错误信息通过电子邮件发送到支持部门。

除了包括出现问题的详细数据外,日志中记录的信息可能还有当时系统状态的一个快照(例如 Web 应用的会话状态)。  

使用上述信息,系统支持团队可以重建发生问题的系统状态,这样对查找和修复问题非常有效。   

错误报告对于开发人员的生产率,以及最终的支持活动消耗成本,都有很大的影响。在开发过程中,如果定位和修复问题让人倍受挫折,就考虑使用更加积极主动的错误报告方式吧。调试信息非常宝贵,而且不易获得。不要轻易将其丢弃。   

展示有用的错误信息
提供更易于查找错误细节的方式。发生问题时,要展示出尽量多的支持细节,不过别让用户陷入其中。
区分错误类型
程序缺陷。 这些是真正的 bug ,比如 NullPointerException 、缺少主键等。用户或者系统管理员对此束手无策。

环境问题。 该类别包括数据库连接失败,或是无法连接远程 Web Services 、磁盘空间满、权限不足,以及类似的问题。程序员对此没有应对之策,但是用户也许可以找到变通的方法,如果提供足够详细的信息,系统管理员应该可以解决这些问题。

用户错误。 程序员与系统管理员不必担心这些问题。在告知是哪里操作的问题后,用户可以重新来过。

通过追踪记录报告的错误类型,可以为受众提供更加合适的建议。

切身感受

错误信息有助于问题的解决。当问题发生时,可以详细研究问题的细节描述和发生上下文。

平衡的艺术

  • 像“无法找到文件”这样的错误信息,就其本身而言无助于问题的解决。“无法打开 /andy/project/main.yaml 以供读取”这样的信息更有效。
  • 没有必要等待抛出异常来发现问题。在代码关键点使用断言以保证一切正常。当断言失败时,要提供与异常报告同样详细的信息。
  • 在提供更多信息的同时,不要泄露安全信息、个人隐私、商业机密,或其他敏感信息(对于基于 Web 的应用,这一点尤其重要)。
  • 提供给用户的信息可以包含一个主键,以便于在日志文件或是审核记录中定位相关内容。

[ ① ]    有些安全敏感的信息不应该被暴露,甚至不可以记录到日志中去,这其中包括密码、银行账户等。

 

38  安排有规律的会面时间

39  架构师必须写代码

 

架构师必须写代码

——   高效程序员的 45 个习惯之习惯39

“我们的专家级架构师Fred会提供设计好的架构,供你编写代码。他经验丰富,拿的薪水很高,所以不要用一些愚蠢的问题或者实现上的难点,来浪费他的时间。” 

软件开发业界中有许多挂着架构师称号的人。作为作者的我们,不喜欢这个称号,原因如下: 架构师 应该负责设计和指导,但是许多名片上印着“架构师”的人配不上这个称号。作为架构师,不应该只是画一些看起来很漂亮的设计图,说一些像“黑话”一样的词汇,使用一大堆设计模式 —— 这样的设计通常不会有效的。

不可能在PowerPoint 幻灯片中进行编程                You can’t code in PowerPoint

这些架构师通常在项目开始时介入,绘制各种各样的设计图,然后在重要的代码实现开始之前离开。有太多这种“ PowerPoint 架构师”了,由于得不到反馈,他们的架构设计工作也不会有很好的收效。

 一个设计要解决眼前面临的特定问题,随着设计的实现,对问题的理解也会发生改变。想在开始实现之前,就做出一个很有效的详细设计是非常困难的(见第 48 页上的实践 11 )。因为没有足够的上下文,能得到的反馈也很少,甚至没有。设计会随着时间而演进,如果忽略了应用的现状(它的具体实现),要想设计一个新的功能,或者完成某个功能的提升是不可能的。

作为设计人员,如果不能理解系统的具体细节,就不可能做出有效的设计。只通过一些高度概括的、粗略的设计图是没有办法达成对系统的理解的。

这就像是尝试仅仅通过查看地图来指挥一场战役 —— 一旦开打,仅有计划是不够的。战略上的决策也许可以在后方进行,但是战术决策 —— 影响成败的决策 —— 需要对战场状况的明确了解。

可 逆 性

“程序员修炼之道”丛书中指出 不存在所谓的最终决策 。没有哪个决策做出之后,就是板上钉钉了。实际上,就时间性来看,不妨把每个重要的决策,都看作沙上堆砌的城堡,它们都是在变化之前所做出的预先规划。

新系统的设计者  Donald E. Knuth
新系统的设计者必须要亲自投入到实现中去。

 正像 Knuth 说的,好的设计者必须能够卷起袖子,加入开发队伍,毫不犹豫地参与实际编程。真正的架构师,如果不被允许参与编码的话,他们会提出强烈的抗议。

有一句泰米尔谚语说:“只有一张蔬菜图无法做出好的咖喱菜。”与之类似,纸上的设计也无法产生优秀的应用。设计应该被原型化,经过测试,当然还有验证 —— 它是要进化的。实现可用的设计,这是设计者或者说架构师的责任。

Martin Fowler 在题为“ Who needs an Architect ?” 的文章中提到:一个真正的架构师“……应该指导开发团队,提升他们的水平,以解决更为复杂的问题”。他接着说:“我认为架构师最重要的任务是:通过找到移除软件设计不可逆性的方式,从而去除所谓架构的概念。”增强 可逆性 是注重实效的软件实现方式的关键构成部分。

要鼓励程序员参与设计。主力程序员应该试着担任架构师的角色,而且可以从事多种不同的角色。他会负责解决设计上的问题,同时也不会放弃编码的工作。如果开发人员不愿意承担设计的责任,要给他们配备一个有良好设计能力的人。程序员在拒绝设计的同时,也就放弃了思考。

优秀的设计从积极的程序员那里开始演化
积极的编程可以带来深入的理解。不要使用不愿意编程的架构师——不知道系统的真实情况,是无法展开设计的。

切身感受

架构、设计、编码和测试,这些工作给人的感觉就像是同一个活动 —— 开发 —— 的不同方面。感觉它们彼此之间应该是不可分割的。

平衡的艺术

  • 如果有一个首席架构师,他可能没有足够的时间来参与编码工作。还是要让他参与,但是别让他开发在项目关键路径上的、工作量最大的代码。
  • 不要允许任何人单独进行设计,特别是你自己。

 

40  实行代码集体所有制

41  成为指导者

42  允许大家自己想办法

 

允许大家自己想办法

—— 高效程序员的 45 个习惯之习惯42

“你这么聪明,直接把干净利落的解决方案告诉团队其他人就好了。不用浪费时间告诉他们为什么这样做。”

“授人以鱼,三餐之需;授人以渔,终生之用。”告诉团队成员解决问题的方法,也要让他们知道如何解决问题的思路,这也是成为指导者的一部分。

     了解上个实践 —— 成为指导者 —— 之后,也许有人会倾向于直接给同事一个答案,以继续完成工作任务。要是只提供一些指引给他们,让他们自己想办法找到答案,又会如何?

 这并不是多么麻烦的事情;不要直接给出像“ 42 ”这样的答案,应该问你的队友:“你有没有查看在事务管理者与应用的锁处理程序之间的交互关系?”

     这样做有下面几点好处。

  • 你在帮助他们学会如何解决问题。
  • 除了答案之外,他们可以学到更多东西。
  • 他们不会再就类似的问题反复问你。
  • 这样做,可以帮助他们在你不能回答问题时自己想办法。
  • 他们可能想出你没有考虑到的解决方法或者主意。这是最有趣的 —— 你也可以学到新东西。

如果有人还是没有任何线索,那就给更多提示吧(或者甚至是答案)。如果有人提出来某些想法,不妨帮他们分析每种想法的优劣之处。如果有人给出的答案或解决方法更好,那就从中汲取经验,然后分享你的体会吧。这对双方来说都是极佳的学习经验。

作为指导者,应该鼓励、引领大家思考如何解决问题。前面提到过亚里士多德的话:“接纳别人的想法,而不是盲目接受,这是受过教育的头脑的标志。”应该接纳别人的想法和看问题的角度,在这个过程中,自己的头脑也得到了拓展。

如果整个团队都能够采纳这样的态度,可以发现团队的知识资本有快速的提升,而且将会完成一些极其出色的工作成果。

给别人解决问题的机会
指给他们正确的方向,而不是直接提供解决方案。每个人都能从中学到不少东西。

切身感受

感觉不是在以填鸭式的方式给予别人帮助。不是有意掩饰,更非讳莫如深,而是带领大家找到自己的解决方案。

平衡的艺术

  • 用问题来回答问题,可以引导提问的人走上正确的道路。
  • 如果有人真的陷入胶着状态,就不要折磨他们了。告诉他们答案,再解释为什么是这样。

 

43  准备好后再共享代码

44  做代码复查

45  及时通报进展与问题

 

 

高效程序猿的45个习惯 “项目启动了一段时间之后,你应该进入一种舒适的状态,团队和客户建立了一种健康的富有创造性的关系。 突发事件应极少发生。客户应该能感觉到,他们可以在一定程度上控制项目的方向” 摘录来自: 亨特,苏帕拉马尼亚姆. “高效程序员的45个习惯:敏捷开发修炼之道”。 iBooks. 立即下载

相关推荐

高效程序员的45个习惯-读书笔记

自己总结的《高效程序员的45个习惯》,把书中自己感觉较好的段落摘抄下来,稍稍加了点自己的想法,挺好的...

高效程序员的45个习惯

敏捷宣言: 个体和交互胜过过程和工具 可工作的软件胜过面面俱到的文档 客户协作胜过合同谈判 响应变化胜过遵循计划 敏捷是一种一人为本、团队合作、快速响应变化和可工作的软件作为宗旨的开发方法,敏捷方法可以快速地响应变化,它强调团队合作,人们专注于具体可行的目标,这就是敏捷精神。敏捷开发就是在一个高度协作的环境中,不断地使用反馈进行自我调整和完善。 没有任何计划在遇敌后还能

iamsupercola的专栏 1245

多仓智能订单路由与动态优化方案设计

现在有很多商家会有多个仓库,并分布在不同的城市中,当用户下单后会根据商品库存及订单信息将订单分配到对应的仓库进行发货,如就近原则,本期基于多仓库设计一套订单路由方案,包含核心逻辑、规则优先级和实施方案,适用于多仓协同的订单自动分配场景

14年产品经理老兵,想把自己的经验及总结分享给大家,只讲思路及实操,不讲废话 2482

高效程序员的40个好习惯和行为方式

每一个好的习惯,开头都会相应有一个唱反调的句子哦。 1 做事 “出了问题,第一重要的是确定元凶,找到那个人!一旦证实了是他的错误,就可以保证这样的问题永远也不会再发生了。” 指责不会修复bug,把矛头对准问题的解决办法,而不是人。这是真正有用处的正面效应。 也许你不相信,但确实有些人常常不把解决问题放在最高优先级上。也许你也没有。先自我反省一下,当有问题出现时,“第一”反应究竟是什...

雷利风行 885

你的OLED屏和MPU6050为啥通信失败?从IIC的7位地址冲突讲起

本文深入解析OLED屏与MPU6050传感器在I2C总线上的通信故障,重点探讨7位地址冲突问题。通过分析I2C协议中的地址分配机制,提供硬件调整和软件调试的完整解决方案,帮助开发者有效排查和解决设备无响应问题。

weixin_26876073的博客 151

【亲测】ubuntu20.4 安装CUDA11.8

一步一步搭建C++ CUDA CUDNN TENSORRT OPENCV DNN ONNXRUNTIME推理环境

weixin_40950590的博客 3896

[转]优秀程序员的45个习惯

态度篇 1. 做实事 不要抱怨,发牢骚,指责他人,找出问题所在,想办法解决。对问题和错误,要勇于承担。 2. 欲速则不达 用小聪明、权宜之计解决问题,求快而不顾代码质量,会给项目留下要命的死角。 3. 对事不对人 就事论事,明智、真诚、虚心地讨论问题,提出创新方案。 4. 排除万难,奋勇前进 勇气往往是克服困难的唯一方法。 学习篇 5. 跟踪变化 新技...

weixin_30471065的博客 432

SIwave学习记录:电热耦合仿真

SIwave电热耦合仿真

m0_68174736的博客 2336

高效程序员的45个习惯-学习笔记

高效程序员的45个习惯-学习笔记 不觉间,走入编程的世界已经2年了,没有做出什么值得说道的成绩,也没有什么伟大的事迹。两年,是个说长不长,说短不短的时间,在这个时间内,也许自己已经发生了改变,却没有达到质变的程度吧。但是两年也可以理清一些事情,决定一个方向。两年的编程确定的事情: 1)程序员是可以不分性别的,需要的只是努力和付出。只要认真了,无论有无基础,总是可以适应的。 2)程序员不是高薪

Transform 1633

NR/5G - On demand SI

NR中系统消息如MIB -可用于无线帧同步等,SIB1 -小区物理信道公共配置等,是驻留一个小区所必需获得的信息,这类系统信息小区是进行周期性广播。 在SI的配置中,系统消息分为BroadCasting和non-BroadCasting,BroadCasting类系统消息基站也是周期性进行广播的,而non-BroadCasting类系统消息则是通过一种on-demand的方式,即如果需要读取这类系统消息,那么UE需要先发送请求给基站,基站响应UE之后,再下发系统消息。 ...

6775

2、OTA/gm - C滤波器的架构与设计

本文深入探讨了OTA/gm-C滤波器的两种主要设计方法:基于LC梯形网络的设计与多环反馈(MLF)结构。前者包括元件替代法和信号模拟法,分别通过电感替代或电路方程模拟实现低敏感度滤波器;后者通过积分器级联与反馈网络合成全极点或含任意传输零点的传递函数。文中详细分析了不同结构如IFLF和LF的反馈矩阵、传递函数及性能差异,指出LF结构在敏感度、噪声和整体性能上的优势。同时比较了各类方法的优缺点及适用场景,为高频连续时间集成电路滤波器的设计提供了系统性指导。

fanta的博客 137

《高效程序员的45个习惯》读书笔记【思维导图】

《高效程序员的45个习惯》   这是一本好书,书中的观点直白、朴素,极富实践性!当然最重要的,还是要在实际项目中身体力行!      下面摘取书中要点,花了大半天时间做成思维导图,以方便随时查阅。

我本书生 3353

2024年数字IC秋招-海康威视-数字逻辑设计工程师-笔试题

笔试题型:单选 + 多选笔试平台:百一测试(电脑监控)笔试时间:1h30minA. AW通道B. D通道C. B通道D. AR通道return x++;x = 5;y = Foo(x);

qq_42135020的博客 3203

《高效程序员的45个习惯》读书笔记

《高效程序员的45个习惯》这本书的副标题是敏捷开发修炼之道,这是一本讲敏捷的书,如果你之前未接触过敏捷,从这本书,可以了解到敏捷的核心观点。 这里面主要讲了三方面的内容,观念,沟通,以及编码。   观念 我们首先从观念来看,提观念当然少不了敏捷宣言: 个体和交互胜过过程和工具; 可工作的软件胜过面面俱到的文档; 客户的协作胜过合同谈判。 响应变化胜过

京东放养的爬虫 2090

【第48例】BLM模型之市场洞察-五看工具篇(一)

BLM市场洞察

产品线负责人 2111

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

45句话读懂《高效程序员的45个习惯》【转载】

45句话读懂《高效程序员的45个习惯》【转载】 1、不要抱怨,把注意力集中到解决问题上来。 2、了解清楚情况,比如团队风格,业务需求等,才动手编码。 3、指出问题,当然,更好的做法是礼貌一点。 4、勇敢的说出实情,然后努力的去解决问题。 5、...

dingjiao5797的博客 196

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

资料_书籍_PDF_《高效程序员的45个习惯》

1. 高效程序员的45个习惯:敏捷开发修炼之道:  a. 介绍 b.下载

quezhengbo 590

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

《高效程序员的45个习惯》-之一

敏捷开发是当下最流行的开发方法,它采用的是一种以人为核心、迭代、循序渐进的开发思想,值得你关注和学习。 最近我就阅读了一本有关敏捷开发的书籍,《高效程序员的45个习惯》。 它以“举反例”的方式来讲述了敏捷开发中程序员应该运用的45个好习惯。

maopig的专栏 1015

《高效程序员的45个习惯》

《高效程序员的45个习惯》 敏捷开发是当下最流行的开发方法,它采用的是一种以人为核心、迭代、循序渐进的开发思想,值得你关注和学习。 最近我就阅读了一本有关敏捷开发的书籍,《高效程序员的45个习惯》。 它以“举反例”的方式来讲述了敏捷开发中程序员应该...

270

一个高效程序员的45个习惯-文末送脑图

一个重大的错误应该被当做一次学习而不是指责他人的机会,团队成员一起工作,应该互相帮助,而不是互相指责不要为了修复问题而去修复,要投入时间和精力保持代码整洁一个团队能够很公正的讨论一些方案的优点和缺点,你不会因为拒绝了有太多缺陷的方案而伤害别人,也不会因为采纳了某个不完美(但更好的)解决方案而被人记恨如果设计或者代码中出现了奇怪的问题,花时间去理解为什么代码会这样。如果你找到了解决办法,但代码还是令人费解,那么只能重构,让它可读性更强。如果你没有理解那段代码,就不要轻易否定和重写它们跟踪技术变化,你不需要精通

分享技术干货和灵感、推荐新书和好玩的项目、分享Java面试题 803
上一篇: Tom大师的调优脚本-- Runstats 的使用方法
下一篇: [转载]梦想成真 高兼容性启动U盘制作实例与分析 用ULTRAISO刻录ISO到U盘
leminsky
博客等级 码龄22年 43粉丝 0原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值