你的网站或应用程序存在哪些问题?如果你在等着用户来告诉你,那么你只能看到所有的问题中已经暴露的那极小的一部分。要知道,那只是“冰山一角”!

而且,如果你真的是在守株待兔,我不得不很抱歉地告诉你,你有点失职——你应该比用户更了解你的程序的健康状况。每当有用户告诉我一个他们在使用我的软件的过程中碰到的“善意的错误”时,我总会感到局促不安。甚至有些惭愧。我没有在用户把问题告诉我之前发现它们,并且事先解决它们,这就是我的失职。我忽视了对程序崩溃应付的责任。

任何负责任的软件项目,首先要做的事是建立一种异常和错误报告机制。Ned Batchelder把这比作为“先给自己戴好氧气罩,然后再帮小朋友戴”:

当你的程序出现问题时,请你总是先去检查这个错误是否被适当处理了。如果没有被适当处理,那么先去解决错误处理代码里的问题。保持这个工作顺序很重要,原因有如下几方面:

  1. 在根源错误还在的情况下,你实际上为错误处理代码中的bug准备了一个完美的测试用例。但如果你先把根源问题解决了,你又将怎样来测试你的错误处理代码呢?记住,你的代码里之所以有bug,部分原因就是它在当初的开发阶段很难被测到。
  2. 一旦根源问题被解决了,解决错误处理代码里的问题的紧迫性便随之消失。你可能会说,“我以后会去解决的,现在急啥?”就好比你家屋顶漏了——下雨的时候,你没办法去修屋顶,因为外面正下着雨呢;但雨停了之后,家里又不漏水了(不急着去修屋顶了)!

你需要集中在一个地方去处理所有的错误。这个地方是你团队里的所有开发人员非常熟悉的,并且每天都会接触到。在Stack Overflow,我们采用了ELMAH(https://code.google.com/p/elmah)的一个自定义分支。

我们每天都在监视着这些异常日志;有时候每个小时都察看。这些异常日志实际上已经成为了我们团队的To-Do列表(待办事项)。我们这么做是有充分理由的。比如,微软收集类似的这种错误日志已经几年了,他们不仅收集自家软件的日志,还收集第三方软件的;他们的这个机制叫Windows Error Reporting(简称WER)。成效喜人啊:

当最终用户遇到程序崩溃时,他们会看到一个对话框,问他们是否想发送错误报告。如果他们选择发送,WER会收集应用程序以及发生崩溃的模块的信息,然后把这些信息通过一个安全服务器发送给微软。

于是,软件厂商可以访问到关于他们产品的数据,再加以分析,定位到问题的根源。他们可以通过错误对话框与用户交互,还可以通过Windows Update升级他们的程序。

在对错误报告数据的广泛分析之后,我们看到:80%的客服问题在修复了用户报得最多的20%的bug之后就能得到解决。即使只修复用户报得最多的1%的bug,也能解决50%的客服问题。这个分析结果通常对于各家公司都是成立的。

尽管我仍然推崇“测试驱动开发”(Test-Driven Development,缩写为TDD),但时间投入的投机性一直是我纠结的一个问题。如果你修复了一个真实用户永远也碰不到的bug,那你的修复有什么价值呢?我知道还有很多其他的理由促使我们去实践TDD,但若单纯作为一种修复bug的机制,在我看来,选用TDD就大可不必了;就像我们过早地进行代码优化一样,是不可取的。我更愿意把时间花在解决真实发生的问题上,而不是那些理论上存在的bug。

当然,你可以两者都做。但考虑到开发时间总是有限的,我更倾向于立足于实实在在的数据,把时间花在解决真实用户在使用我的软件过程中碰到的问题上。这就是我所谓的“异常驱动的开发”(Exception-Driven Development)——将你的软件发布出去,让尽可能多的用户使用它,然后一心一意地研究他们产生的错误日志。使用那些异常日志去找出问题的根源,并且专注在你的代码中有问题的区域。重新架构,重构代码,以消除最严重的3个问题。快速迭代,部署,如此周而复始。这种数据驱动的反馈机制是非常有效的,几个迭代下来,你的程序将非常稳定,坚如磐石。

异常日志可能是用户能够提供给你的最强有力形式的反馈。这种反馈基于已经发布的软件,为了得到它们,你不必去询问用户或者诱导他们,也不必理会他们关于问题的不可思议、似是而非的描述。真正的问题伴随着转储出来的堆栈跟踪信息,已经悄悄地为你自动收集好了。异常日志才是用户反馈中的根本

(译者注:2009年4月16日,DareObasanjo与Scott Koon ‏@lazycoder在Twitter上有过一次辩论;Dare Obasanjo的观点是:通过将产品发布出去,然后从客户那里得到真实的反馈,这比任何事先的沟通或调研更有价值。)

我是在提倡发布带有bug的代码吗?或者半成品?或者狗屎软件?当然不是!我的意思是说,你越快将你的软件推到真实用户的面前,你就会得到越多的数据以改进你的软件。异常日志在这个过程中扮演着很重要的角色,同样地,用户使用数据也很重要。当然,如果你受得了的话,你还应该跟用户交谈。

不管怎么样,软件在发布的时候总是会带有bug的。所有软件都是这样。只要是软件,它就会崩溃,它就可能丢失数据,它还会难以学习、难以使用。问题不在于你在发布软件的时候带出去了多少bug,而在于你能多快地修复那些bug?如果你的团队一直在践行异常驱动的开发模式,答案就很简单了——别担心,我们马上就会改进我们的软件!看着吧,我们会越做越好!

听起来多美妙啊!就像一首美妙的乐曲,在每个用户的耳边萦绕……

异常驱动的开发(Exception-Driven Development)的更多相关文章

  1. 【翻译稿】Behavior Driven Development (BDD)行为驱动开发

    这是一篇翻译稿,方便给不知道BDD的同学扫盲.原文链接:What is BDD (Behavior Driven Development)? | Agile Alliance Definition定义 ...

  2. Bug驱动开发(Bug-driven development)

    说实话,作为一个Domino开发者,像測试驱动开发(Test-driven development).功能驱动开发(Feature-driven development)之类软件开发的高大上的方法论( ...

  3. 敏捷软件开发 Agile software Development(转)

    原文链接: http://www.cnblogs.com/kkun/archive/2011/07/06/2099253.html 敏捷软件开发 Agile software Development ...

  4. 编写高质量代码改善C#程序的157个建议[用抛异常替代返回错误、不要在不恰当的场合下引发异常、重新引发异常时使用inner Exception]

    前言 自从.NET出现后,关于CLR异常机制的讨论就几乎从未停止过.迄今为止,CLR异常机制让人关注最多的一点就是“效率”问题.其实,这里存在认识上的误区,因为正常控制流程下的代码运行并不会出现问题, ...

  5. Java异常:选择Checked Exception还是Unchecked Exception?

    http://blog.csdn.net/kingzone_2008/article/details/8535287 Java包含两种异常:checked异常和unchecked异常.C#只有unch ...

  6. S3C2440触摸屏驱动实例开发讲解

    出处:http://www.embeddedlinux.org.cn/html/yingjianqudong/ 一.开发环境 主  机:VMWare--Fedora 9 开发板:Mini2440--6 ...

  7. Vs2010 配置驱动的开发环境

    我已被用来VS2010开发环境,之前曾经与vs2010驱动的开发环境.重装系统,一次又一次的配置,找了好几篇文章,配置没有成功,在配置阶段突然成功了,直接把原来的驱动程序的配置文件将能够接管使用. 当 ...

  8. hibernate异常:org.hibernate.exception.GenericJDBCException

    异常:org.hibernate.exception.GenericJDBCException 提示:Cannot open connection 提示:不能打开链接 一般这个异常是由 java.sq ...

  9. Atititjs javascript异常处理机制与java异常的转换.js exception process

    Atititjs javascript异常处理机制与java异常的转换.js exception process 1. javascript异常处理机制 Throw str Not throw err ...

随机推荐

  1. 学习ASP.NET Core Razor 编程系列七——修改列表页面

    学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...

  2. 03_OGNL

    1.值栈: 解答Struts能够直接获取属性值: 原因:Struts并不是直接通过request隐式对象中获取,而是将整个对象封装到了ValueStack值栈中,直接匹配是否存在这个属性,找到了就取出 ...

  3. 在查询语句中使用 NOLOCK 和 READPAST

    对于非银行等严格要求事务的行业,搜索记录中出现或者不出现某条记录,都是在可容忍范围内,所以碰到死锁,应该首先考虑,我们业务逻辑是否能容忍出现或者不出现某些记录,而不是寻求对双方都加锁条件下如何解锁的问 ...

  4. 虚拟机克隆,并设置新的ip

    6.1克隆新的虚拟机 选中某个虚拟机-à右键à管理à克隆 选择下一步 选择下一步 点击完成 6.2修改主机名 [root@hadoop3 ~]# vim/etc/sysconfig/network 将 ...

  5. 第一个Angular2的样例

    欢迎跟我一起学习Angular2 本文根据angular2官网手动敲码得来: 本文地址:http://blog.csdn.net/sushengmiyan 本文作者:苏生米沿 - 开发环境搭建 - 配 ...

  6. SceneKit一个3D场景角色的代码重构

    SuperSpaceMan3D是一个以SceneKit为基础的小游戏项目,作者展示了用SceneKit开发3D游戏的强大威力.不过在实际运行时会发现有一些小bug,这里我们依次尝试将其修复 首先,当s ...

  7. Android程序员必须掌握的知识点-多进程和多线程

    当某个应用组件启动且该应用没有运行其他任何组件时,Android 系统会使用单个执行线程为应用启动新的 Linux 进程.默认情况下,同一应用的所有组件在相同的进程和线程(称为"主" ...

  8. SQLite Update 语句(http://www.w3cschool.cc/sqlite/sqlite-update.html)

    SQLite Update 语句 SQLite 的 UPDATE 查询用于修改表中已有的记录.可以使用带有 WHERE 子句的 UPDATE 查询来更新选定行,否则所有的行都会被更新. 语法 带有 W ...

  9. GitHub无法访问或访问缓慢解决办法

    缘由 由于众所周知的原因,Github最近无法访问或访问很慢.由于Github支持https,因此此次屏蔽Github采用的方法是dns污染,用户访问github会返回一个错误的IPFQ当然是一种解决 ...

  10. Struts 2 之校验器

    对于输入校验,Struts2提供了两种方式,1.使用validate方法:2.基于XML配置实现 . validate()方法 支持校验的Action必须实现Validateable接口,一般直接继承 ...