作者 | James Simone
译者 | 弯月
出品 | CSDN(ID:CSDNnews)
在过去的三个月里,我们团队的效率达到了惊人的高度。我们完成了许多过去的目标,而且还超出了预期,提交了很多新功能,而且我们每个人都沉浸在工作的乐趣中。我们是怎么做的?我们是如何保持这种状态的?在过去的几个月里,我一直在思考这些问题,我也与队友探讨了其中一些问题。
简而言之,只要积极地分享成功,并承担起失败的责任,整个团队就能有优异的表现。在过去的几个月里,我们团队取得了一系列的成功:
- 我们的产品负责人获得了晋升的机会。当然他们也付出了巨大的努力,此次晋升受之无愧。
- 我们的一位团队成员的项目(Nebula Logger)在GitHub上斩获200颗星。去年这个时候,他已经为这个项目付出了多年努力,但是给星一直在50 颗左右徘徊。最近,整个社区突然意识到该工具为管理员和开发人员带来的巨大价值,真是令人难以置信!
- 我们的新队友成长为了真正的工程师。我们这个团队负责10多个应用程序,压力非常大,但我们的新队友迅速成长,熟悉了复杂的逻辑,为团队做出的贡献越来越多。
- 最后,我也得到了晋升,非常感谢我的团队给予的支持!
在庆祝之余,我也明白我们这个项目的压力非常大,如果不是我们一丝不苟、兢兢业业地努力,可能这个项目就要走向失败。
深度剖析工程上的问题
在从事软件开发的八年中,我看到了很多失败的项目,数量甚至超过了成功的项目。长时间这一行业里摸爬滚打,就会觉得自己的人生也是失败的一部分,有时项目的失败是我们无法控制的,就像创业公司烧钱是不可避免的。然而,一般情况下,项目会慢慢走上失败的道路。下面,我们就来回顾一下其中的原因。
裁掉最优秀的员工带来的危险
项目的长期健康会受到一些因素的影响,其中之一就是裁掉最优秀的员工。英文中,有一个专门的词汇来描述这种情况:brightsizing,词典的定义是:
Giving redundancy to the hardest-working / most talented employees of a company。
裁掉公司里最勤奋/最有才华的员工。
许多定义都出给了员工被解雇的例子,但在疫情之前,我以为出现brightsizing的原因不是因为公司发展不景气,而是拒绝承认和分享个人或群体的表现。许多公司几乎没有激励措施来留住员工,或者奖惩条例不够清晰。在许多传统观点中,劳动力只是一种投入,在其他条件相同的情况下,如果一个人离开,那么很容易找到另一个人来代替。
疫情肆虐期间,人们开始意识到人与人是不能相提并论的,想要真正“取代”有才华和知识渊博的专业人士往往需要数年时间。多年来,brightsizing一直是公司人员配置的一个热门话题,而如今由于“辞职大潮”频发这种困境更胜从前,尤其是在科技领域。换句话说,失去最有才华的员工是导致软件项目失败的原因之一。在过去的八年里,我看到许多项目失败就是因为这个原因。
我们很容易认为,糟糕的架构和质量不过关的代码是导致项目失败的原因,但我认为实际上这些只是brightsizing的症状。换句话说,如果你能留住和奖励有才华的人,就不会面临糟糕的架构和质量不过关的代码等问题。我们公司曾有一名“技术主管”提交了如下代码:
const removeLastNCharacters = (string: string, number: number) => {var stringToRemove = new RegExp(".{" number "}$", "i");return string.replace(stringToRemove, "");};
我不得不进行重构:
const removeLastNCharacters = (string: string, number: number) =>string.slice(0, string.length - number);
我想说明一点,这个函数的第一版是直接从一个网上的帖子中复制出来的。从网上搜索答案很容易,但这位“技术主管”并不了解基本的库函数。
有很多问题都是由brightsizing导致的,在此我想特别强调两个:
- 如果你的态度是“这是我写的代码,能有什么问题?”(就像上面这个例子一样),那么你的手下就不可能取得成功。
- 如果项目的核心人员离职,那么剩下的人也不可能取得成功,他们或者根本无法推进项目,或者对问题置之不理,通常都会造成灾难性的后果。
我再举一个例子来说明这两点之间的区别。五年前,我前后花费了几个月的时间才顺利通过一系列行政上的繁文缛节,拿到了另一个部门的代码库的访问权。当时的我天真地以为这只是一个“简单的变更”,然而几个月过去了问题还是没有得到解决,因为那个团队中只有一个人了解他们的应用程序,而且他很“忙”。其实,我只是想给一个对象添加一个属性……
/ this should have been a type, not an interface// but hey, let's pick on one thing at at time, right?interface ImageProps = {path: string;title: string;// my new awesome property!!label?: string;// etc ...}// the question mark indicates a property is able,// which would later on allow us to write something like ...const createProps = (path: string, title: string, label?: string) : ImageProps => {const imageProps = {path,title,label: label ?? title // fallback to title when label not provided// etc ... other assignments};return imageProps;}
但是,当我在其他地方访问这个新的label属性时,TypeScript 告诉我该属性不可用。我有点糊涂了,我刚刚不是才添加好这个属性吗?后来我看到了如下代码,每个使用ImageProps的文件中都重复定义了ImageProps:
interface ImageProps = {path: string;title: string;}
对于那些拒绝承认有才华、勤奋工作的人带来的价值的组织来说,brightsizing 是一个非常现实的问题。代码臃肿、Copypasta(指人们经常在论坛或社交网络上剪切、复制、粘帖文字)、糟糕的架构等等,各种问题开始慢慢浮现,然后迅速蔓延。在我看来,只需要变更两行代码(第一行,将属性添加到接口;第二行,将新创建的label属性正确分配给创建好的<img>元素),结果却不得不修改几十个文件中的100多行代码。没有标准化,没有检查。我在代码审查期间提出了一个建议,集中处理有问题的接口,并创建 img 标签,但他们对我的建议一笑了之。没多久我就离职了。
那家公司的人力资源一直有问题,他们不懂得挽留人才,经常迫使优秀的员工离职。员工的平均就职时间不足两年。许多人未到两年就匆匆离职了。尽管上面我提到了如今总体的趋势,但总有公司落伍,因此才出现了“辞职大潮”。
我总是拿我的第一家公司做比较,虽然那家公司也比较失败,当时我们的CRM系统已有20多年,面临淘汰(该系统的整个生命周期花费了数千万美元)。虽然该系统只使用了几年,但他们决定重新组建团队,构建自定义的CRM系统,而且他们认为这比改进已有的系统更划算。然而,他们试图增加员工并维护的产品其实与核心业务无关,我实在找不到合适的词汇来描述这种行为。当然,确实有Hubspot这样的公司真正做到这一点,但关键性的区别在于 Hubspot 正在寻求进入一个全新市场的机会,以出售他们的劳动成果。HubSpot开出的薪水很有吸引力,而且部分原因是大波士顿地区创业文化的复苏。我敢肯定他们创建CRM系统并不仅仅为了支持内部业务,否则真正的高层或股东肯定无法负担得起这项长期的成本。
有时,裁掉优秀员工的后果不会立刻显现,尤其是当你刚刚加入新组织时。但逐渐地,你就会觉察到有异样的代码、紧密耦合的架构、不够严密的测试以及缺乏组织知识积累。这些都是裁掉优秀员工的后遗症。这也是为什么面试如此重要,因为你可以借机了解面试你的人是否喜欢自己的公司。换句话说,如果团队成员都很讨厌自己的工作,他们聘用你是为了填补其他人离职留下来的空缺,那你肯定也不想加入他们。
代码审查的注意事项
最近,我在读一本书《Accelerate》,书中提到不健康且低效率的团队面临的最大问题之一是不必要的审查流程。我反思了我们现在所做的审查以及我见过的一些审查流程,很明显审查的方式以及审查的内容同等重要。我们应该引入非暴力沟通原则。这些是代码审查的先决条件,审查过程中的任何精神/语言敌意都会导致审查全面崩溃。
一些不良习惯很容易摧毁审查流程,《Accelerate》展示了与其忍受糟糕的变更审查流程,还不如不要任何审查流程,但我更倾向于说,折中才是最理想的。
代码审查可以为团队提供机会,让每个人达成一致的看法。有时,你需要重新回顾自己过去的看法,我经常对自己的开源工作进行代码审查,即使有些功能简单到一天就能改完,也会在审查时经常做出修改。过一段时间再来审查代码,更有可能发现问题。对于Mob编程,我最喜欢的就是它非常重视接收反馈。最近,我们正在开发一个具有解耦架构的功能,但最终一些测试出了问题,我们开始重构。有人建议我们应该深入调查Jest API,如果我们无法通过现有的测试,那么解耦就没有任何意义了。加快反馈循环为我们节省了几个小时的时间,并让我们迅速回滚到以前可以正常工作的代码,然后进行测试。
关于代码审查,有几个关键的问题需要注意:
- 如果按原样部署这段代码会有问题吗?
- 这段代码是否与其他地方采用的模式一致,还是说这段代码是一个独特的存在,并无先例?
- 代码审查提出的建设性反馈是否会成为提交者的学习机会?
如果你的建议不属于上述任何一种,则没有必要提出来,吹毛求疵只会让大家痛恨代码审查,浪费时间,而且还会让大家对代码审查失去信心。代码审查应该是大家期待的环节,不要过分挑剔!
遥想当年,在我离开第二家公司后,一位朋友向我透露,我们的代码库是他所见过的最干净的代码库。多年以来,他的称赞一直困扰着我,尤其是我想到了一些我们的不足之处,以及一些仍然可以改进的领域。其中之一便是我们的风格指南。当初我花了大量时间来调整代码的格式,就是为了让我的代码更加贴近风格指南,每次想起这些,我就觉得很痛苦。我们每个人都有自己的小怪癖,即使完全遵从结对编程,我们也必须在提交代码之前检查自己下意识的编写风格。如果你正在审查代码,而且对代码的格式有疑问,那么就应该找一款可以自动调整代码格式的工具。
摩擦 -> 倦怠 -> 灾难
遇到摩擦时,第一时间解决是最有效的方式。否则,摩擦会导致倦怠,而倦怠会导致灾难。这里我所说的“摩擦”指的是:
- 繁重的代码审查;
- 困难的部署;
- 所有的部署只能由一个人完成;
- 部署之后需要做回归测试,有人害怕因此被指责。
- 不鼓励休假的企业价值观;
- 企业的价值观与你自己的价值观截然不同;
- 公司的管理极其糟糕,以至于价值观根本没有任何作用。
- 无法平衡工作与生活;
- 孤岛式的工作环境;
- 不清楚职业发展的下一步。
- 不切实际的预估与时间计划。
以下是消除摩擦的一些例子:
- 代码审查只关心必要的清理工作和架构问题;
- CI/CD;
- 项目总结不会互相指责;
- 没有限制的带薪休假,并鼓励休假(每年5~6周);
- 良好的调度,开始与结束的时间统一;
- 高度可见、易于理解的晋升渠道以及晋升目标;
- 乐于助人的团队;
- 根据新信息修改时间计划;
- 专注“改善”,降低维护的难度。
我会经常遇到哪些摩擦?人们以为工程师总是想接受艰巨的挑战,使用尖端的技术。我认为,如果问及工程师在工作中对他们来说什么最重要,大多数工程师会从第二重要的事情说起,因为在他们心目中第一位是人。如果工作中的每一项任务都困难重重,那么倦怠将不可避免。如果一切都需要使用尖端技术,那么就意味着你需要花费大量时间来解决没有任何文档记载的错误、处理工具之间的功能差异,还需要费尽苦心来隔离新的基础设施、平台和代码,直到系统稳定。
我看到过很多团队和人员快速迭代,采用尖端技术来解决当今的问题,并解决了很多棘手的问题,但我相信他们有能力做这些事情是因为他们在低摩擦的环境中工作。如果你感觉公司或团队的文化与自己格格不入、与你的工作目标脱节、看不到职业发展的机会,那么就不太可能承受每一天的严酷考验,更不用说长期坚持直到项目取得成功。
我们再来思考一下“失败”的项目。
在恶劣的环境中工作,团队成员团结在一起的例子并不罕见,他们相互扶持,共度难关。这种结构,就像雪花形成的格子一样,无法长期维持,如果有一天破裂了,那么HR就会面临大决堤。我前面提到的那家失败的公司就有过这样的经历,我们的两位最有才华的工程师离职了,后来在半年之内,团队中的其他成员(包括我自己)都相继离职了。
项目成功的已知因素
那么,什么让工程团队变得强大,又是什么让高绩效团队始终保持健康和快速?
高绩效团队能够享受工作的乐趣
在疫情期间,我曾参加过一个项目,所有人都是远程工作,但我们在一起很开心。在我刚加入团队之际,项目经理打电话给我,热烈欢迎我的加入。
虽然后来我离职了,但我非常怀念那段快乐的时光,我至今仍与他们保持着联系。我觉得自己很幸运能有机会与他们合作,并不是每个团队都能彼此信任,主动承担责任。所以,我认为,快乐地一起共事,优雅地欢迎新人加入,这样的团队更有弹性,更有效率。
信任的重要性
长期以来,我一直在思考“信任”这个词(这是我们公司的第一核心价值)。虽说这是一个简单的词语,对不同的人来说有着不同的含义,但我发现彼此信任的团队会更加团结,相处融洽,积极沟通,并超越其他团队。信任有多种不同的形式:
- 愿意表现出脆弱,承认自己不知道;
- 能够倾听某人的心声,不会被打断;
- 能够学习新技术,相信投入的时间不会白白浪费;
- 没有太多微管理。
- 在相互协作的工作环境中,信任以及没有微管理对团队的工作流程有着很大的积极影响。
信任与摩擦是相互对立的。彼此信任的团队可以将社交互动的复杂性降到最低,整个团队就像一台运转良好的机器。团队成员之间会为彼此的成就而感到高兴,而不是相互嫉妒。
只有对团队或个人有足够的信任,权力才能被下放。成功的团队可以由具有许多差异的人组成,只要他们相互信任,而且能够在工作中存同求异。这不禁让我想起了一位老同事,虽然在工作之外他的性格和兴趣与我有着天壤之别,但我们依然能够愉快地共事。志同道合的一群人在一起工作固然好,但天南海北的人们聚在一起,为了某个共同的目标而团结努力也不失为一段佳话。我们有着共同的目标,而且与企业的价值观一致,这是高效团队充满活力的因素之一,即便:
- 需求不断变化;
- 待完成的工作不断积累;
- 有很多错误需要修复;
- 每个人都面临着工作和生活上的巨大变化。
但我们依然会为着崇高的目标而努力,能够帮助我们抵达彼岸的正是信任。
指导的重要性
适当的指导对项目的成功也有一定的辅助作用。经验丰富的人可以通过指导的方式将知识传递给新人。在实践中,良好的指导是双向的,因为双方都可以得到成长。我很幸运,在工作中遇到的第一位老师非常了不起。在我看来,提供技术指导的组织更有弹性,因为他们的团队有更大的共享机构知识的能力。
最关键的是,导师与管理有很大的不同,让经理担任导师并不是一个明智的选择,因为导师的角色与经理的角色不同。导师会给你介绍浏览应用程序中的概念和案例,还可以看看实现这些概念的代码。如果经理有时间和精力担任导师,而学生也愿意,那么也可以让同一个人担负起两个角色。但是指导与管理的性质完全不同,并非每个经理都适合指导他人。同样,也并非每个新人都适合由经理指导。
学习的过程分为两种:第一,通过不断犯错成长(你的代码会出bug,或者编写出的代码并不能按照预期运行);第二,通过观察别人学习。指导可以让我了解决策背后的批判性思维,从而更快速地前进。
在工作中,我也曾有幸指导几位优秀的初级工程师。我非常喜欢结对编程,因为我们可以在这个过程中通过指导学习到更多知识。作为一个团队,我们之所以成功,是因为我们能够探索有趣的模式或软件架构,分享学习成果的过程能够促使我们改变。下面,我来讲述一个结对编程的例子:
type Data = {fieldOne: string;fieldTwo: string;// etc, other properties};type DataOverrideConfig = {fieldOne?: string;fieldTwo?: string;// etc, other properties};const updateBasedOnConfig = (data: Data, config: DataOverrideConfig) => {data.fieldOne = config.fieldOne ? config.fieldOne : data.fieldOne;data.fieldTwo = config.fieldTwo ? config.fieldTwo : data.fieldTwo;// etc ...};
我和同事在一次偶然的机会中发现了这段代码,当时我们打算将几个不同的代码路径合并到一个类中,而该类负责处理数据的配置变更。我们只是打算将函数移动到同一个类中,并没有打算从根本上改变代码。然而,在看到上面的两个三元组时,我忍俊不禁,想借此机会展示如何修改这段代码才更有意义:
const updateBasedOnConfig = (data: Data, config: DataOverrideConfig) => {if (config.fieldOne) {data.fieldOne = config.fieldOne;}if (config.fieldTwo) {data.fieldTwo = config.fieldTwo;}// etc ...};
注意这段代码本来不是用JavaScript编写的(否则可以用更为幂等的方式来实现这个功能)。我对变量读取进行了修改,当配置覆盖中的某个字段不存在时,避免不必要的写入操作:
- 第一段代码中定义的三元组的意思是:“无论何时都必须设置此属性,即使没有配置覆盖,也要用该属性的原始值写入”。
- 加上if语句后,这段代码的意思是:“如果配置覆盖包含匹配的属性,则使用它来覆盖数据的现有值”。
后者更符合业务规则的描述,我们可以回顾一下为什么提高代码的可读性对每个人都有好处。
正确的指导可以让新人迅速成长,并提高他们的自学能力。我有幸在自己的职业生涯中遇到过多位优秀的导师,在此我想向他们表示感谢。
疫情期间的相互协作
疫情期间,每个人的工作和生活都发生了巨大的变化,这一时期的团队合作尤为重要。各个公司对科技人才的需求达到了空前的高度,再加上他们也意识到远程办公不仅能让开发人员随时随地工作,而且人才库也不会受地域的限制,扩大了几倍。当然,所在城市也不再是影响薪资待遇的重要因素,因为开发人员有可能居住在城市之外,或者住在生活成本更低的小城市里。疫情期间人们的思想也发生了变化,大家都希望各个公司能够重视人才,而不是他们所在的位置,每个人都希望回到自己的家乡或居住在其他城市,然后通过远程办公。
能够不受地域的限制,自由选择工作和居住的城市,员工就会更有动力,更有追求成功的欲望。《Accelerate》一书中也提到:
……拥有高度信任工作环境的公司的股票表现优于市场指数三倍……
如果员工能够全身心地投入工作,保持愉快的心情,而且有能力做到最好,那么每个人都会受益。此外,心理健康专业人士还指出,在疫情期间,由于工作中的摩擦以及家庭心理的综合影响,心理健康服务的需求也达到了空前的高度。这是一个持续的变化,正在不断渗透我们的文化和价值观。虽然我不知道疫情究竟对我们的办公室文化和个人的生活方式带来了怎样的影响,但我们知道:
- 人们都在谈论“视频会议疲劳”。
- 虽然许多软件开发人员喜欢单独工作,但他们也是辞职大潮中重要的一部分。
与以往相比,似乎如今的我们更加无法“忍受”企业价值观与自己价值观之间的差异。我们对工作越来越没有耐心,越来越挑剔。再加上强烈的与世隔绝的感觉,很容易理解为什么这个时期辞职的人数创下了记录。
我们虚需要保持积极的心态。虽然我与有些团队成员从未见过面,但我们每天都在视频通话,我对他们一点都不会感到陌生,工作中总是充满了欢声笑语。
这种感觉很重要,这一切都建立在信任之上。我们已经成功地发布了很多功能,还曾在两个冲刺内启动了两个项目。有时,我们的压力很大,有时还不得不连夜修补程序。虽然很辛苦,但我很高兴加入了这个团队,
重视“改善”
“Kaizen Focus”中的Kaizen来自日语,写成汉字就是改善,意思是持续改进的过程。这是敏捷框架从日本制造业中汲取的一个思想。在实施敏捷方法时,这是一个常见的做法,即让团队在每个冲刺确定一个可以帮助他们加快行动速度的改善事项。我与一位朋友曾经感叹道,我们花费在维护代码上的时间甚至超过了编写代码的时间,所以说通过“改善”降低维护的难度是如此重要。说到这里,我想到了以下两个问题:
- 代码在编写完成后,就会立即进入维护模式(还有一种说法“技术债务不仅仅来自继承的代码,就连你刚刚编写完的代码也是技术债务”)。
- 读代码比写代码难。
我们想一想成功的项目与失败的项目之间的区别。有些项目之所以成功,并不是说一开始编写的代码就很出色,之后就再也没动过。相反,这些代码经历了许多次迭代。换句话说,成功的项目得到了良好的维护。
结束语
我前后花了一个月的时间来撰写这篇文章,在此过程中我反思了个人职业生涯中积极和消极经历。我认为,在某种程度上,我们所有人都在寻求“希望看到的改变”,但是在错误的公司、项目和/或团队工作会让人感觉深陷泥沼,不堪重负。
至于如何提高个人或团队编写的代码的质量,我认为我们应该保持积极的心态,为团队做出有意义的贡献。反思我们在软件工程方面哪些做对的事情和做错的事情,也许就是一个很好的起点。
原文地址:https://www.jamessimone.net/blog/joys-of-apex/the-life-and-death-of-software/
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。