Skip to main content

亲爱的,今天你“债”了吗?

730-刘彩红-技术债务       打开百度百科,债务指债户还债的义务,有时也指所欠的债及为了清偿所有的债务而工作。

       1992年一个叫Ward Cunningham的大牛提出了“技术债务”的概念,可以理解为程序YUAN为了赶工期快速构建一些可以工作的代码出来,而不考虑代码本身的可读性,可重用性,复杂度和整洁度;或者测试人员为了尽快完工而不进行仔细完整的软件验证。

       与技术债务类似,被《哈佛商业评论》誉为当代“创新大师”的Steve Blank提出了“组织债务”的概念,指的是一个组织在发展初期曾经为了快速使业务走上正轨不得不采用许多折衷的方法,比如公司仍然沿用管理小规模团队的模式来管理规模已经扩张后的组织架构。

       Ben Horowitz在《创业维艰》的书中对“组织债务”细化到了管理层面,于是产生了另外一个名词叫“管理债务”,说的是当牺牲掉代价高昂的长期利益,做出权益的短期管理决定之时发生的管理债务,比如一事二主,工作没有真正的责任人,想想看会有什么后果。
       上面的各种债务只是用词不同,本质都是各种角色在行使各自的职能时更关注眼前的利益,而忽视未来可能要付出的代价。这些债务绝不仅仅针对程序YUAN, 技术人员,管理者,对所有工种所有角色都适用,更加不幸的是,不管是什么债务都和房贷一样是有利息的,还的越晚付出的代价就越大。

       我家买了4辆自行车,并不是有4个人需要用,而是第一辆自行车我们不愿意花力气抬上楼梯,被贼偷了;于是买了第二辆,加了2把锁,还是不愿意花力气抬上楼梯,又被贼偷了;接着买了第三辆,是个电瓶车,加了一把10斤重的铁链锁,还是不愿意花力气搬上电梯,结果锁没事,电瓶被偷了,后来新买了电瓶,无论如何都要把车往楼上搬了,没完没了的代价已经成了不能承受之重。
        这两天看了《中国农村住校生调查报告》,心情沉重。20016月国家开始实行“撤点并校”政策,本意是整合教育资源,提高办学效益,促进基础教育均衡发展,提高教育质量,实现教育公平。结果2012年的数据中,有1100万农村小学生,2200万初中生住校,其中60%是留守儿童,45%的寄宿学校利有12年级超低龄住校生。寄宿生生活学习环境,身心发育,负面情绪等问题极为严重。越来越远的学校使农村小学的辍学率上升,农民家庭的教育负担显著增加。这笔债被悄悄地被转嫁到了偏远村落的农民。

       佛教中有“因果报应”的信条,在当今社会中很难被广泛信服,原因是一个人种下的因(欠债)会被其他人来承担相应的果(还债),好比写坏代码的人,当bug被用户汇报上来以后,很可能是另外一个维护团队负责修bug, 写了坏代码的人只要把当时的测试通过就可以了;好比决定采用管理小规模团队的方法来管理大规模团队,当问题爆发的时候很可能责任已经落到另外一个领导身上了; 好比国家实行的“撤点并校”政策,最终是农民和千万个孩子来买单。

       但债终究是要还的。

       落实到我们每个人身上,每天你敲的每一行代码,做完的每一个测试,标明的每个一个“task done”, 你有没有花一点时间仔细想一想你有没有因此欠债,会不会有另外一个人或一群人在某个时候要替你还债;抑或你正在做的事是帮其他的人还债,在愤愤不平的同时,有没有想过能否做些什么来阻止债务的蔓延。

       所以,亲爱的,今天你“债”了吗?

Share

Inception的核心逻辑

Inception是ThoughtWorks多年以来使用的启动软件设计和交付项目的方法,通过3天到两周的时间,采用集中式、互动式的设计工作坊,帮助客户在最短时间内达成对项目范围的一致,快速进入项目交付。

Y同学是我们最年轻的设计师,她问:

y-question

最重要的事情,我认为单词应该写对,是「硬塞噗婶」而不是「硬塞噗母」,然后我来用最轻松的、完全不同以往那么严肃的语调,讲讲什么是Inception。

问题

首先,让我们来到问题的情境当中,这个情境有以下几个元素:

  1. 一个企业
  2. 交付一个软件
  3. 解决一个业务问题

2001年,年轻的Luke叔叔和Marc叔叔还在爱深折工作的时候,就遇到了这样一个情境:

  1. 英国农业部
  2. 一个新的农业税计算平台
  3. 处理全国各个粮仓收粮时候的退税问题

Luke叔叔和Marc叔叔青葱少年自然依恋设计师自由的明媚、追求设计理想,然而现实并不是这样,他俩被安排写了9个月文档。

第十个月,英国农业部宣布农业税的重要改革,他俩的工作变成了然并卵。

02

年轻气盛的二人觉得「再也不能这样过,再也不能这样活」,毅然决然离开了公司,追求新的设计方式。

反思

这就是那个时代的故事,每个流程上的人,辛辛苦苦完成的工作,最后因为一个变动而变得毫无意义,聪明的二人开始反思:作为设计师的我们该如何面对更快的变化呢?

他们的思考逻辑是:

  1. 为什么变化可以让我们做的变得毫无意义?因为我们做的事情都基于某个「不会变化的假设」:农业部的税收制度在一年内不会变化;
  2. 事实上这些假设「被打脸」的频率从一年变成了9个月,而且会越来越高;
  3. 这些假设还来自于内部,领导在让我们写文档的时候说:「同志们,放心吧,真的不会变,写吧」,我们假设领导是对的;
  4. 我们能不能避免假设呢?似乎不能没有,不假设,不打包票,谁会干活?
  5. 那么在不能避免假设的情况下,我们要做的是降低「假设带来的潜在伤害」。

这就是Luke和Marc所思考的:

设计一种流程尽可能降低假设带来的潜在伤害。

假设

你发现你生长在一个充满假设的世界里,他们说市场不会变、他们说他们要的东西一定会成功、他们说他们完全理解你的意思、他们说改了这一版不会再改了,呵呵,都是骗子。

03

事实上,你的项目里,市场一定会变、他们说的东西根本就是个玩笑、他们根本不懂也不在乎你说什么、他们改了一版还之后的二百版。

为什么要有需求冻结,没有需求冻结,被玩死的是你,但是很遗憾,有了需求冻结,被玩死的可能是你的公司。

需求优先级中有「总裁需求」这样的定义,私下里,他们把这样的需求标记为「呵呵」。像一副牌里打出8对王炸,我就炸了,你咋滴!你之前听到的所有假设和承诺,全都不作数。

事实上,真正的王炸,可能来自市场,如果市场变化期缩短到6个月,意味这你所有的研发行为从想法到运营的时间,必须在6个月以内甚至更短,我把这个叫做「王炸周期」。

因此在做软件规划的时候,你如何能够保证从想法到投入市场,短于6个月?

Inception的最核心含义在于:

如何规划一个从想法到运营时间短于「王炸周期」的软件项目?

8年以来,这个周期变得越来越短,在某些极端情况下,你可能只有6周时间。

对策

变化的周期越短,我们就越需要去除更多的假设,一个软件项目的假设有很多,他们包括:

  1. 买单的人知道我们在干什么、怎么干;
  2. 设计师和客户之间对于解决方案达成了一致;
  3. 可以实现;
  4. 可以在一定时间内实现;
  5. 有用户愿意使用我们的服务。
  6. 未来运营的人认同我们的方案并表示可以继续运营下去;

我们也许不能保证这些假设都能够成立,但是「一分也是爱」,能验证一个是一个。

验证这些假设也不容易,你怎么能保证买单的领导没有新的想法?大部分人的思路是:我要做一个尽可能完美的方案,让领导挑不出毛病。你错了,你在「憋大招」的同时,领导也在「憋大招」,意思是你自信满满打出「王炸」的时候,领导打出了8个。

你怎么保证你的设计交付团队就能完成?拍着胸脯说放心吧这个简单的技术骨干拍屁股走人之后,你就哭吧,这时候他们打出「对三」你都要不起。

你怎么保证你的用户会使用你的服务?你大概知道中国创业产品的生命周期平均不超过8个月,你花8个月做的产品,可能够在市场上死两回了。

那你该怎么办?

做小、多验证假设。

最有做得小、你才有时间进行验证,这便是Inception的逻辑,把大部分的时间花在验证假设上,而不在研发上。

过程

之前提到的6大假设正是Inception活动的精髓所在,它们是:

  1. 验证利益相关者的战略;
  2. 验证设计有效性;
  3. 验证方案可行性;
  4. 验证方案实现时间的合理性;
  5. 验证产品有效性;
  6. 验证产品可运营性。

传统方式中,这些假设的验证通常通过一系列活动打散在公司的决策过程中,例如总裁例会讨论决策、用户研究和设计由不同服务提供商完成、交付由IT或外包完成、最后交给产品运营团队完成。

而Inception则尝试用最短的时间(短至1周)完成以上的6大验证。最简单的一条Inception逻辑是:

04

这个过程充满着视觉化的工作坊,协助组织内的利益相关者进行互动,共同识别问题、设计方案:

05

设计流程也是快速而逐层演进的,而绝不使用一个完美的设计方案:

06

Inception不仅仅是业务和设计的整合,还包含技术,我们会根据设计梳理出完整的交付列表,请注意,这一切都只发生在一周之内:

07

当然我们也会注意用户测试,这样的用户测试可能主要来自于企业内部,测试内容主要在可用性、而不在产品策略,关于产品策略的验证,将有其他更具备精益特征的服务方式覆盖。

而Inception和敏捷软件交付的关系则是:

  1. Inception负责明确足够进入交付的工作量,并尽可能地去除这个工作量中的假设;
  2. 敏捷软件交付则基于这个「足够的设计」进行不断地迭代交付和设计、并进行测试和改进。

08

总结

话说回来,Luke叔叔和Marc叔叔就是我们引以为傲的Inception的创立者,10多年来这套方法帮助我们启动了超过200个软件交付项目,包括现在所说的服务设计,其中的逻辑并不出其左右,在这个基础上,我们又演进出来1天、3天、2周、4周、甚至8周的版本,根据不同项目情况进行定制,而核心逻辑一直没有变化,它们是:

  1. 我们认为软件交付需要拥抱变化;
  2. 假设越多、变化时候成本越大;
  3. 我们要尽可能消除6大假设,包括:决策者、业务需求与设计范围、技术实现、交付时间、产品有效、和可运营性;
  4. 我们通过做小来降低消除假设所带来的成本;
  5. 我们通过丰富的设计和沟通实践降低消除假设所带来的成本;
  6. 我们只计划和设计足够启动的体验,进入迭代式的交付过程。

到后来Marc叔叔写了一本《Agile Experience Design》的书,而Luke叔叔管理着ThoughtWorks在欧洲区的业务,遗憾地是,在2014年的一场车祸中丧生,请爱穿花衬衣的Luke叔叔放心,中国的小伙伴早已把薪火一代代传了下去,也要谢谢你。

Share

被信息塑造的新工人阶级

当你看见十多个年轻小伙子围坐在一张长条型电脑桌上,各自神情专注地紧盯着自己面前的屏幕,一边快速地敲击着各自的键盘与鼠标,还不时互相呼喊,你会认为他们在干什么?玩游戏?没错。但又不止于此。他们可能受雇于一个“工头”,每天在后者提供的电脑上玩网络游戏超过十小时,将游戏中获得的装备和宝物交给工头变卖并从后者那里领到一份工资。尽管不为任何现实中的职业认证或劳动保障机构认可,这些被称为“游戏矿工”的职业游戏玩家确实是在劳动并撑起一个每年交易额超过十亿美金、年增长超过20%的市场。他们是信息时代塑造的新工人阶级成员。

赵月枝教授在《传播与社会》书中提到一个有趣的事实:中国在2001年制定的国家信息化指标是全球第一个官方确定的全国性信息化指标。政府对信息化建设的重视由此可见一斑。然而,精英驱动、市场驱动的信息化建设在制造“高大上”的城市白领信息时代生活方式之余,却常常有意无意地忽视工人、农民等“信息中下阶层”。难怪赵月枝教授要问出“手机之后,是什么?”——被迫卷入这场数字革命的广大劳动人民在这场革命中有何收获,这是一个颇可玩味的问题。

邱林川所著的《信息时代的世界工厂》是对这个问题一次有益的探索。首先,他带领读者回顾了两种主要的信息技术——手机与互联网——在中国普及的过程。从“信息中下阶层”的视角,他敏锐地捕捉到了这样一个“迅速扩散与变异”的模式:中下阶层在商业机构跑马圈地的扩张过程中被卷入信息化大潮,而后又因从他们身上无利可图而被市场边缘化,最终使得中下阶层尽管“被信息化”,却无力于表达和决定自己的信息化需求。用这样一个草根视角回顾网吧、寻呼机、小灵通、短信等信息技术的兴衰历程,能使读者在这些耳熟能详的故事中读出一层别样的意味。

在作者的探索过程中,流动者、尤其是中下阶层的在外务工人员是一个特别吸引他关注的人群。作者准确地指出,在传统的“知识工作者”(或称“可自我编程的劳工”)与“体力劳动者”(或者“一般劳工”)之间,还存在着一个为数众多且高速增长“灰领”阶层(或称“可被编程的劳工”)。一如汪晖在一篇文章中所写,这些受过高等教育、工作于高档写字楼、外表光鲜亮丽的“新穷人”,实质上干的却是枯燥程度与蓝领工人相当的工作——薪酬也堪相比较。当信息的加工处理成为一类价值增长点,工业时代的泰勒主义便自然地延伸到信息时代,将文员、质检等职业塑造成日益枯燥重复的“灰领”,并创造出短信写手、网游代练等新的“灰领”职业。尽管劳动的场所和形式与传统意义上的“劳工”相去甚远,这些被信息技术催生的新工人与前者面临同样的信息挑战:信息技术使他们被原子化、边缘化,他们在新技术塑造的工作环境中不仅承受资本的剥削,而且还在精英导向的话语体系中失语,然后又在无力于表达的状态中被主流媒体污名化。从时下流行的“屌丝”、“蚁族”等戏谑又不乏苦涩的自称中,人们不难看到这个群体的挣扎与无奈。

资本透过信息技术对中下阶层的剥削还不仅限于工作空间。通过消费主义和娱乐至上的意识形态构建,信息技术与服务的提供者们着眼于如何将中下阶层工作之余本已为数不多的闲余时间与金钱压榨干净,而非如何为他们提供更为有益的信息工具。正如安杰维克在《异化的自由劳动》(收录于《数字劳工》)一文中所说,曾经开放、自由的互联网已经被高度私有化,对于已经没有太多购买力的中下阶层,资本则将他们异化为免费劳力。当提供免费上网时段的飞宇网吧被清一色的网络游戏专用机取代,当数亿微信用户平均每天花1.7小时刷朋友圈,我们可以清晰地看到无远弗届的信息技术是如何协助资本来榨干中下阶层的最后一点剩余价值。

然而正如马克思所说,资本主义是它自己的掘墓人。信息技术被用于塑造、束缚和剥削一个新工人阶级的同时,也为这个新工人阶级的成长、联合和斗争提供了工具。不无遗憾地,邱林川在他的书中并未深入探索中下阶层信息化的现状可以如何发展,从而使这个阶层本身(而非控制信息技术的资本家和精英群体)受益和赋权。这个“怎么办”的问题并不容易,因为回答它不仅需要兼具人文社科与信息技术的跨界知识与技能,而且需要具备这样技能的一个专业人士站在无产者(而非资本家)的立场上思考。不过,即令留下了“怎么办”的问题,这本《信息时代的世界工厂》毕竟用无产者的视角清晰勾勒了我国信息空间的发展图景,为后来者的继续探索预备了一个坚实的基础。

Share

重构是每个程序员的洗髓经

相传达摩祖师面壁九年,功成坐化,少林僧众于其面壁之处获铁函一只,内有两部不世绝学:《易筋经》和《洗髓经》,易筋功法强筋健骨,洗髓心法采纳调息,故少林冠绝武林,为江湖称颂

自软件江湖降世,功法、心法及门派层出不穷,各大公司年年招兵买马,意在争夺江湖头把交椅。然每每武林大会之季,仍有不少公司感慨人马多而不强,鱼龙混杂,江湖地位力有未逮。原因何在?究其根本乃是软件江湖快速膨胀,功法(编程语言,设计模式等)之类见效快,故而为世人推崇,而内功心法(思想,方法,习惯)非一年之功乃是经年累月之效因而鲜有问津。更有不少江湖骗子伪装得道大师,随意杜撰心法,更是造成不少初涉软件开发之人,走火入魔,饮恨江湖,从而导致世人对内功心法更加敬而远之。

世间繁杂在一定程度上掩盖了武林至尊绝学《洗髓经》的光彩,口口相传更是加深其神秘感,但这都改变不了其真正价值–洗髓是脱胎换骨,由内而外地强化自身功力,更是打通任督二脉,实现向真正的高手大师过渡。

软件开发也有一部内功绝学–《重构》,它来自一位长相酷似达摩祖师的软件大师,Martin Fowler,关于这位大师的传奇经历此处不表,本文所要说的是他创立的软件开发内功心法–重构。

正如前文所说软件开发的内功心法其实讲的是软件开发者使用的方法,习惯和思想,与武功中的内功一样,长期的修炼能够让修炼者参悟到软件开发的不同境界。说到这里,有人会说真有这么神奇吗?回答是肯定,因为这样的修炼改变的是开发者的习惯,包括思考的习惯、设计的习惯和开发的习惯。这正是江湖流传的不二箴言:习惯决定成败。

重构改变了开发者的什么习惯?首先来看下重构是什么,重构是指不改变软件功能的前提下,通过代码调整来改善代码质量,提高代码的可维护性,使代码的架构更加趋于合理。一言以概之–改代码,有人会提出疑问,改代码我们也会啊,这有什么习惯可以改变的,重构并不是改一次代码,而是在开发的过程中频繁地进行代码调整,只要在开发中发现可以重构的点,就可以记录下来,然后进行重构,不只对现在正在开发,也可以是对之前完成的代码,这不是件容易的事,需要付出恒心和毅力。

可能有人担心重构会破坏代码原先的功能,OK,那就引入TDD和版本控制,TDD保证重构不破坏原有功能,而版本控制则保证出现了破坏原有功能的错误后能迅速回到原先正确的状态。

重构+TDD+版本控制,开发者一下就拥有了三件法宝来改善代码质量。恭喜你,你现在变成XP(极限编程)了,你的开发习惯一下就从瀑布迁移到了敏捷上,再加上经年累月的磨砺,OK,你自然而然地成了高质量代码的贡献者,是不是想想有点小激动呢?

内功心法大多分为若干层次,重构也是一样,分为几重境界。第一层是通达,即阅读性重构,如同打通全身经脉,消除代码中词不达意,表意不明,做到“信、达、雅”;第二层是凝炼,即结构性重构,如同气沉丹田,消弥杂念,剔除代码中的重复,提取职责单一的功能块,实现代码重用和逻辑清晰;第三层是突破,即设计性重构,如同打通任督二脉,重铸体内气血循环,在这一层次,经过前面的两层修炼,代码结构趋于合理,逻辑趋于清晰,此时可以考虑引入模式,完成质的跨越。

完成三重修炼,经历若干次周天循环,开发者的思考习惯就发生了变化,首先思考如何实例化需求,因为开发者通过修炼了解何种需求是有价值的;接下来就是如何在实现的过程思考合理的引入模式,因为开发者明白何种模式能够更适合,区分哪些是坏味道;并自然地写下清晰通顺的代码,因为那是每次重构时的条件反射,只是简单地表达出来。

在经历了重构带来的开发习惯和思考习惯的变化之后,那么在设计过程中就会将这些习惯融入其中,化为无形,设计出功能合理的软件产品。

习惯的改变使得开发者可以站在一个全新的角度看问题,正如练就洗髓经的武林高手能够无招胜有招,花对手万般招数于无形,因为他们看到了最核心价值的东西。

最后引用豆瓣上看到的一篇奇文中的一段来说明练好重构这门内功的重要性,因为它能让你成为真正读懂软件开发,学会软件开发,运用软件开发的高手:鸠摩智上少林寺挑衅,使遍七十二绝技,方丈群僧无不骇然。这时,小和尚虚竹跑过来,只瞅了一眼,就说:“这位大师用的明明是小无相功嘛。”鸠摩智慌了。一般人看到的是招数(各种花式的语法,模式),厉害的人看到的是内功(开发的思想,设计的思想)。

笔者仍属于只见树木不见林的开发凡人,因此谨以此文激励自己向高手的路上前进。

Share

今天你写了自动化测试吗

一艘货轮满载着货物从港口启航,向浩瀚的大海深处破水而去。海面平静,微微皱起波浪,从容而显得宽容。然而,货轮的步履却有些蹒跚,发动机“轰轰轰”地嘶吼着,不堪重负,却无法让船只游得更快,倒像是海水咬住了船底往下在拖曳。

“嘟——嘟——嘟”,突然警报声响起,甲板上变得喧闹起来,一个水手模样的年轻人声嘶力竭地呐喊:“船超重了,快快快……快卸货!”声音急迫,甚至能听到哭音。然后,又是一阵喧嚷,似乎是在争吵甚么,就看到一个胖胖的中年人冲了出来。看他那肥胖的体型,真难想到他的身手竟然如此敏捷,如海豹一般破开人群,两手挥舞,大声喊道:“怎么了?怎么了?”,他停下来,吼道:“我看哪个不长眼的家伙敢卸我的货!谁敢!”

船长走了过来,略带恭敬地对那中年人说道:“老板,你看,这船超载了,船身吃紧,已经发出超重警报了。倘若不减轻船的重量,这船开不了多久就得沉了啊!”

“他奶奶的,这船可真秀气啊!”中年人一边骂骂咧咧,却也知道形势紧迫,容不得自己不下决断。可是心里总存着侥幸心理,突然灵机一动,一把拉过船长,指着这艘货轮问道:“既然这船超重,那我问你,除了货物,这船上还有哪些东西占了船身的重量?”

船长一听,立刻明白老板心里的小九九,没好气地回道:“除了货物,占了这船重量的就还有人、淡水、食品,还有救生圈、救生衣、救生艇。老板你看那样不顺心,你就扔哪样吧!”

嘿,回到现实中来吧。回答问题:倘若你是老板,你会扔哪样呢?稍有理智的人,都不难做出正确的选择。——然而,为何在软件开发过程中,我却常常看到有人选择丢弃救生圈、救生衣、救生艇呢?哪怕它们的重量对于整艘船而言如同九牛一毛,却总有人存着侥幸,认为船就超了那么一点点,或许扔出几个救生圈,就能恢复重量到安全线;于是,货物得以幸存,可以避免不必要的损失了。

或许,我们没这么傻吧。那么,让我们想想。

假设将这航行比作是软件开发的过程,那么载货到达目的地,就是实现软件需求。只有交付了货物,才算是实现了价值。至于淡水、食品以及船只,就是开发的工具与环境,而救生圈、救生衣、救生艇,就是我们在开发过程中需要编写的自动化测试(单元测试、集成测试、验收测试等)。我们需要这些测试来随时检测开发功能是否有误,及时反馈,就像在航行过程中,若是有人溺水,可以用救生衣、救生圈挽回一条生命一般。

可一旦开发时间紧促,人手严重不足,进度压力山大时,我们想到了什么呢?对于我见过的多数软件团队而言,每当面临如此窘境时,首先想到的就是减少甚至不做自动化测试。有人认为自动化测试没有价值,浪费成本;有人认为自动化测试可以以后再补,先把功能完成再说;有人认为有了手动测试,就足以保障项目的质量……如此这般,自动化测试就这般被忽略了,沦落到随时可以抛弃的地位。

倘若软件开发就只有这一个阶段,没有需求变更,没有后续开发,没有软件维护。项目的代码库如树苗一般在阳光雨露下茁壮成长,没有大风狂吹,没有烈日暴晒,没有大雨倾盆,亦没有虫蚁啃啮,那自然由得它去。然而,现实世界哪有如此美好!

Michael Feather将没有自动化测试的代码称为“遗留代码”,温伯格在《咨询的奥秘》中则认为应该将“维护”工作视为“设计”工作。自动化测试是修改的基础,重构的保障,设计的规约,演化的文档。它的重要性怎么强调都不过分,然而很可惜,在很多软件项目开发中,它甚至不如“鸡肋”的地位,说放弃就放弃了,在决定当时,毫不觉得可惜。至于以后的以后,不远的未来,谁还顾得上!!?债欠下了,什么时候偿还呢?——不知道!到了催债的那天,再想办法还债吧。

鸵鸟心态害死人啊!

扪心自问,我们经历过维护的苦楚吗?体验过修改代码的烦恼吗?修复过不胜其扰的缺陷吗?答案若是肯定,那么,如果老天再给你一次机会,把选择自动化测试的权利放在你面前,作为“曾经沧海难为水”的你,你会怎么选?——所以,我想问问程序员们:今天,你写自动化测试了吗?

Share