本文转载自微信公众号“砖码杂工”,作者我不想种田。转载本文请联系码砖手公众号。软件测试中有一句名言:测试只能证明缺陷的存在,不能证明产品没有缺陷。为了确保安全,我们引入规范和流程,精心设计代码,如履薄冰地测试发布。软件的结构和实现在为稳定性服务中占有很大的比重。然而,即使我们尽力而为,也只能将失败降到最低,最终无法完全消除失败。为保证交付和运营质量,企业往往制定安全标准,制定事故处罚规则,加剧了交付效率与系统安全之间的矛盾。软件工程师变成了外科医生一样的高危行业从业者。我们被灌输了很多与安全生产相关的知识,我们的认知受到了一定的教育,我们对在线故障的观念根深蒂固,深信不疑。怀疑。笔者曾在网游和互联网公司工作过,他们对网游失败的态度完全不同。这些差异对我产生了强烈的影响。可以说,我只是接受了某种形式的教育。我接受的是另一种方式的教育,所以我的认知也在不断变化,这些经历和变化让我比别人思考和感受更多。如果说我和刚开始做这个题目的时候有什么不同的话,那就是我逐渐意识到事情不是绝对的,我的认知未必是正确的,这可能才是正确的方式。因此,我把自己的所见所感记录下来,力求客观呈现原貌。我不提倡什么,也不批评什么,因为事实比意见更重要,我更愿意听取大家的意见。1、我读网游研究生的时候,在Xhuhu实习。那个时候,旭虎是个大牌手。后来,FirefoxStudio独立上市。没错,就是在纳斯达克上市的CY。我作为游戏开发人员在《TLBB》工作了一年半。这款游戏在当时非常成功,PCU超过80万,贡献了公司90%以上的收入。TLBB通过过程管理软件管理需求和缺陷,这在当时是非常先进的。计划和提出需求,编写文档,程序开发功能,测试,然后流程流向测试。测试的考核是按BUG统计的,测试后BUG的多少也会影响程序的性能。这样的系统设计可以保证程序在测试前充分自检,从而减少缺陷数量。程序是功能的开发者,也是白盒测试的最佳实施者。他似乎有一种天生的感知,即可能会出现问题,并且为了方便自测,他会尝试开发一些辅助测试功能,以提高测试效率。而测试会为了性能尽最大努力去发现缺陷。但是这种制度的一个副作用就是程序和测试的关系往往很紧张,程序觉得地位不高,往往都是由测试来完成。CTO为TLBB服务器制定了几个规则:1.简单至上,设计简单明了,不要过度设计;模板、STL和C++高级功能(如异常、放置新等)不允许在编码中使用。2、注意防御,安全重于效率,任何函数都嵌入了ENTER_FUNCTION和LEAVE_FUNCTION宏,多做判断和检查,尽量不要死机。这种设计的一个显着优点是项目代码简单,门槛不高。应届毕业生两周内即可上手。缺点是程序性能不高。开发人员每天都与数组和指针打交道。一年多下来,我发现自己看不懂开源项目,感觉自己弱智了。游戏需要快速迭代,每周都会发布版本。因此,将代码从功能分支合并到主体是有时间窗口的。在窗口关闭期间,只能修复错误。新人上半年提交代码,会有人做review,相当于给孩子自行车的轮子装了2个支撑轮,然后就可以独立提交了。至于静态扫描和代码权限控制,抱歉,没听说过。由于良好的系统设计和测试过程,TLBB服务器总体稳定,偶尔会出现小问题。公司有在线故障分级,但是公司不多说,所以大家觉得不是很强。如果某个项目没有开放,评估就会受到影响。如果有太多次,它将被发布为不可靠。标签,只有专业的杂工。后来去了WMSJ,几个清华同学开的公司。2007年,《WMSJ》凭借其在3D方面的出色表现惊艳亮相,随后连续推出多款好游戏。成立三年后赴纳斯达克上市。当时CHI的老板意气风发,花几千块买马骨头,给应届生开出1.5万到1.8万的高薪。遇到校招的时候,可是他们要求很高,我一边被打死,一边被我打死。研究生去了八面,被赶了出去。这家公司的第一代程序员水平都很高,我至今认为他们是我工作中接触过的最有才华的程序员。从客户端引擎到UI到数据库,整个游戏前后端+引擎,全部自研。因为我是做服务端的,所以无法评价引擎和客户端技术。在服务器方面,他们的技术水平很高。我在CY的时候,大家都一直在Y,WM的大世界(无缝地图)是怎么做到的?TLBB是一个平铺地图。当我看完WMSJ服务端的代码后,惊呆了。这是一个非常精致的设计。后来流行的BigWorld引擎在服务器设计上和WM有一些共同点。与TLBB相比,WMSJ完全没有规则可言。做什么事、做什么事都没有明文规定。只要能编译通过,运行不报错就可以了。编码标准?交叉审查?不需要,代码访问控制?密封版?不存在的,在线错误处罚?没有什么。但是我进公司一个半月后,除了看代码,没有别的工作可做。我一度怀疑领导对我有意见。我趁着老婆生孩子的机会,请了一个月的假,表达一下自己的不满。领导问我为什么请这么长的假。我说反正我也没什么事,领导说,你没看到这个清华新毕业的已经读代码2个多月了吗?让我冷静一下,马上组织就会有任务交给我了。通过这个项目,我学习了epoll模型、多线程、通过消息机制解耦、COW、惰性求值、真正的OOP和GP(抽象、泛化和扩展)。最大的思想碰撞来自于容错。以前TLBB编码会做很多容错处理。比如简单的get函数也会有enter_function/leave_function宏,会判断空指针,检查参数有效性,返回值。Checking、logerror等,大量的容错代码淹没了功能代码,导致完成同样的功能,需要更多的代码。但是WMSJ的做法是完全相反的。它广泛使用断言。函数侧重于功能逻辑,对调用者有期望。我工作以来受到的教育不是这样的,这让我很困惑。我找到了GameServer的主要开发者C先生(技术VP,毕业于清华大学,60%以上的代码都是他自己写的),是WMSJArchitect(非本人点名)中公认的最强。我说:“TL服务器的风格是面向失败的编程,不崩溃就不会崩溃,这样程序才能有足够的弹性,WM服务器错了!”C先生回答:“不是这样的,越容错越多的日志越多越好,检查应该只在边界进行,函数的实现者和调用者遵循一定的契约。太多了防御没有体现面向失败编程的思想,不利于构建健壮的程序,crash越早暴露问题,不管crash与否,只会把bug埋得更深,让它越陷越深缺陷很难定位,最终程序会变成一个充满污垢和污垢的混乱场,从而变得更加脆弱。”“依赖假设,修改代码后,很容易出问题,应尽量避免上线失败。”我当时无法接受他的解释,只是试图说服他。C总说:“Assert在编译debug版本的时候起作用,但是发布的时候编译release版本。用这样的规则写程序会更清晰,更健壮,多看几个开源项目就明白了。””当时被“多看几个开源项目就明白了”给噎住了,因为当时真的没看任何开源项目,以至于很多年后,遇到同样的时候辩论,我会用同样的一句话来呛人。WMSJ没有规则,新人看代码一个月,这些东西,我一点都不夸张,所以,你看,虽然他们都是游戏公司(而且是同一个类型的游戏MMORPG),TL和WM采取了完全不同的策略,而且都是当时成功的。当时我们做游戏的时候,开发任务很重,一般服务器只有5-7个人team.2年大概要写50万行C++代码,一个人每年4万行C++,这个工作量非常大,所以其实很难经过繁重的研发过程。可以说严格按照流程,基本上游戏还没走就死掉了o在线。但不走流程不代表游戏程序员技术差。相反,大剂量的编码训练,往往会让大家的编码水平更高,而且普遍非常务实。还有一个比较有意思的是,我们以前都是按照银行系统的要求来开发数据库,??比如支持事务,支持回滚,觉得很麻烦很别扭。直到有一天,我们意识到我们想太多了。想太多是想太多的最简单方法。想多了,文科生容易出家,想多了,艺人会自杀。最后,我们参考了BerkeleyDB,用简单的3000行C++写了一个CacheDB。其实用起来就够了。在做游戏的时候,另一个体会是我们在稳定性设计上投入了很多精力,或者说公司的运营对稳定性提出了更高的要求。我们曾经坚信这些是必须的,直到有一天,我们发现事情可能不是。比如有一款游戏,公测期间一个晚上就宕机十几次,而疯狂的玩家却在等待重启恢复的时候在论坛上大喊大叫。数据显示,本场比赛的输球率非常低。玩家们似乎真正关心的是游戏的乐趣。我们曾经错误地认为稳定性是保留的敌人。又如某游戏因程序缺陷被玩家利用。这件事曝光后,消息在论坛和网络上传播开来,引起了大批新玩家的吃瓜看热闹。在运营广告的时候,我们觉得有故障影响玩家体验,那么给玩家补贴也是不得已的做法。不过调查发现,这其实是玩家乐于看到的事情,可以大大增加话题热度和玩家活跃度。2.网络说完游戏体验,再来说说网络体验。先后在TX和某地工作过。先说TX-WX。虽然WX的做法不代表整个TX公司的做法,但我觉得还是可以反映出一些问题,以供参考。有一段时间在WX里做了一个搜索项目,就是WX搜索。WX后台服务基本都是基于svrkit框架开发的。svrkit是一个RPC框架(开源名称phxRPC),负载均衡、错误重试等框架都已经完成,基于这个框架的应用只需要关注业务逻辑即可。那时,维信搜索北京虽然有60人左右,但大部分是做算法的,只有3个人是做工程的。再来说说TX怎么也是大厂。按理说研发过程应该是很利索的,但坦白说,我们上线一款应用真的没有那么麻烦。我们甚至没有特殊测试。我开发一个功能之后,可能先灰度一台机器,观察5分钟,查看日志,如果没有明显的异常,那我就灰度机器的1%,再观察1小时,如果没有问题,我会灰度10%,然后观察半天小时,如果没有问题,我stud。你看,一个更新,2个小时就能搞定整个过程,是不是很别扭?如果有问题怎么办?万一出了问题,速度回滚,或者出问题了,那就拉出来打板,不过这板其实一般都不想死,有点自罚三杯的味道。其实我一直都不敢对外说这些,因为这样说会觉得丢人,会觉得自己很卑微,一点也不高,不好意思跟你的人打招呼同事。一位WX的技术负责人告诉我,他经常因为网络事故被举报和批评,但他从来没有被罚款过。一旦WX支付失败,就被大领导审问,但即便如此,WX也没有想过通过加重流程来降低失败率。他说这是WX的一个选择。WX认为,业务快速迭代能力的丧失是难以承受的。虽然线上故障有时会造成严重的后果,但WX对线上故障的容忍度很高,稳定性和敏捷性是两个矛盾的层面,很难平衡。WX倾向于敏捷。由此可见,无论是TX,还是WX,都不是不知道稳定生产的重要性,也不知道可以通过加重流程来减少故障,而是在综合考虑各种因素后,选择了保证业务快速迭代。敏捷是互联网的命脉,不能输。有人告诉我另一个WX故事。WX基本是立项几个月就上线了,因为赶时间。其实后台有很多问题,比如内存泄露,但是他们并没有选择马上解决这个问题(或者说PositiveGang),他们选择了绕过这个问题。服务100次后,进程自动重启,完美解决了这个问题。这段代码后来被一个实习生review,改了100到1000次,性能提升了不少。说到这里,肯定有很多人认为WX技术薄弱,WX工程草率。很好的证明。说说某个地方,那是完全不同的景象。某地把稳定性比作木桶的底板。如果稳定性有问题,就没有水了。这时,坚持地板思维。某地的开发商每年都要通过安全生产考核。每年6.18、双11、双12、春节、三十八、甚至两会,TB都要提前很久关门,尤其是双11,至少一个半月的关门时间,任何更新都会需要特批,所以留给开发的时间窗口其实非常非常短。每年,我们都会重点开展安全生产知识培训。每年我们都会分阶段召开会议,强调安全生产的重要性。安全生产比天大。这条神经绝对不能放松。我们会提前制定各种应急预案。如果出现XX情况,YY会处理并制作成手册,出现异常的时候,跟着执行,这些东西耗费了大量的人力财力。但实际情况是99.999%都是没用的,完全是忙活,大促期间都是集体出动,熬夜值班,然后一起发朋友圈poss,你可能会说这些都是绝对必要的,为了万一出了事,有个底线的预案,是的,你说的对,这个逻辑没人会懂,某个地方的人也是这么想的。但是凡事都有一个度,这个度可以算是一个平衡点,太多就是太多,这是最简单的道理。为什么某个地方如此重视安全生产,当然首先他们会从他们业务的特殊性上进行一波分析,比如声称结核病是国家基础设施,就像水、电、气一样,从不同的角度解释了它的极端重要性。但其实最根本的原因就不说了,就是谁出问题了,谁3.25了,还一直坐着,一旦出事故基本就到位了,某个地方就没办法继续了,处罚很重,会受到很高级别领导的处罚。所以,在安全生产的“大是大非”面前,谁也不敢大意,谁也不敢做违反流程挑战zz的正确事。结果,不知道是不是某个地方的服务稳定性比TX强,但是从迭代效率上来说,肯定慢很多。最有趣的是,如果你和某人(尤其是老人)讨论安全生产问题,你会发现他们的观点出奇地一致。他们坚信安全生产责任大于天,要坚持零容忍。越严格越好,但真的应该这样吗?我不知道,但至少同为互联网公司的TX不是这样的,TX的业务不是发展得很好吗?我们可以很容易地举出业务的特殊性。比如某电商可以举个例子,某某心脏病患者因为无法在网上下单购买救命药而死亡。如果你制造了这个bug,那就相当于间接杀死了。不可怕吗?比如DD,也可以说是因为APP的问题,导致打车的妹子没能及时赶到。求救,被强奸,然后被杀,不可怕吗?其实在游戏行业也可以举出这样的例子。比如一个孩子因为副本游戏卡一怒之下订了房子,导致房子被毁。所以,没必要用这样的例子来强调业务的特殊性,因为是软件还是需要尊重软件的常识,回归本质,但是业务有没有特殊性呢?是的!我们必须正视它。比如电信业务,我们需要把软件部署到别人的数据中心,在别人的机房更新程序有很多限制。这确实与互联网软件有很大的不同。这对我们的软件提出了更高的稳定性要求,这是显而易见的,但我们往往过分强调安全性而忽略了对生产效率的影响,而对生产效率的副作用可能远比我们任何人所能想象的大。京东大东子曾经总结过几个商业成功的关键因素,其中之一就是你可以实现更低的成本或者更高的效率。因此,对于商业公司来说,生产效率对应于软件的高开发效率和成本控制其实远比我们想象的重要,但我们往往会掉进一个顾此失彼的陷阱。总结我之前说过,我会解释事实,而不是观点。很抱歉欺骗了你。事实上,完全没有意见的文章是不值得写的。史书还是有意见的。那么作者的观点到底是什么呢?在故事里。
