众所周知,assert是程序调试阶段的一柄利器,可以帮助程序员快速的定位代码问题。但一般来说当程序部署到生产环境的时候,我们会选择关闭assert。不过由于历史原因,我们运行在生产环境下的程序中的assert依旧发挥着作用,这样一把双刃剑就悬在了我们头上。
我们用的是自己的assert实现,这个实现没有C标准库中assert实现那么普适,不过可以满足我们自己需要的功能,它在运行时可以将断言失败信息记录到文件中以便事后分析,并调用了C运行库的assert让进程退出。
在生产环境下开启assert似乎确非惯例,但这也许还谈不上对与错,更多可以看成是当时开发者们的一种选择,在“让程序带着bug继续运行”和“遭遇bug时尽早让程序退出”两者之间,他们选择了后者。当然这也可能是当时他们的一个无奈的选择:在产品诞生初期,Bug较多,为了快速在生产环境定位Bug而开启了assert。
不过时过境迁,客户对产品质量的要求越来越高,我们除了在线下通过各种方法提升产品质量外,在线上当程序出现Bug时的处理方式也要考虑做适当变化,遭遇Bug直接主动退出导致业务瞬间中断的方式被越来越多的开发人员质疑。或者现在的开发人员也是迫于一些外部压力,宁愿选择让程序带着Bug运行下去。
昨天我们针对这个问题做了一个内部讨论,考虑修改自有assert的实现,去除对C库assert的调用,只保留断言失败的信息记录。这样就不会导致程序遇Bug立即退出的情况。不过仍有一个项目组认为这种方案不能解决他们系统中遇到断言失败时无法自恢复并继续健康运行的问题,希望能在代码中感知断言失败,并对错误现场进行一些修复性处理,比如如果断言失败发生在加锁后,希望断言发生后能直接走到解锁环节。最后居然给出了一个让人哭笑不得的方案:给assert加上一个特定的返回值表示程序出现异常(多为Bug)。
先不谈给assert加上返回值有悖assert设计的初衷,如果真的给assert加上了返回值,那意味着什么呢?据不完全统计有如下几点需要改动:
1、assert的实现彻底颠覆,如果按照C标准库中对assert设计的要求,甚至可能是很难实现的;
2、你完全不能忽视assert,因为它还有返回值,甚至当断言失败时,会直接退出使用assert的那个函数;
3、大量遗留库代码和业务层代码需要判断assert的返回值以及使用assert的函数的返回值,增加对assert返回的所谓特殊错误码的处理;
4、实在想不出这个"恐怖"的想法还能带来什么改变。
对这样一个费力不讨好、不给力的想法我除了反对,还是反对。有这些时间还不如仔细琢磨一下如何提高设计能力和产品质量呢。如果非要这么做,建议放过assert吧,请重新实现一个能检查感知Bug的函数接口吧。
评论