让你成为英雄的五大开发技能(提示:涉及乐高积木)
编程就像搭建乐高积木。任何开发者都可以选择一套全新的乐高积木,按照说明进行搭建。这非常简单。你可以把它想象成编程学校的作业或入门教程。
真正的软件项目则不同。它就像搭建一座巨大的城堡。只不过它过去已经建好了。然后有人猛地一脚把它踢成了碎片。较大的部分还勉强凑合着,较小的部分则完全被压垮了。有些元素被踢飞了。
你收到的盒子里除了剩下的零件,还有成千上万个其他套装的零件。说明书当然也丢了。你只能靠一张图片来了解城堡的样子。
这真有趣!让我们看看如何在这种环境下有效地工作。
1.接受未知
任何开发任务的第一步都是了解你需要做什么。这听起来显而易见。然而,任务越大,未知变量就越多。现在正是设定明确预期的时候。
如果您不知道如何开始,那么长时间的思考和概念化是没有意义的。开始吧,先弄清楚您需要的关键要素。然后再三思。如果有人提前询问您的预算或截止日期,请坦诚相待。系统的某些部分您不了解或可能不理解。这没关系。
想想 Facebook、Netflix 或 Oracle 这样的平台,或者任何大型企业软件。很少有人能够完全掌握它们。那些真正掌握它们的人,要么自己构建了它们,要么花了数年时间使用它。所以,首先,给自己一些时间,让自己意识到自己并非无所不知。更重要的是,接受你不会无所不知的事实。
经验丰富、高效的开发人员并非编程水平更高。他们更善于评估自己需要做什么,并能选择正确的策略来应对未知情况。
乐高类比:想象一下我们想要重建的城堡。假设有人给你一张城堡和盒子的图片,然后问你“你需要多长时间才能建好这座城堡?” 除了“我还不知道。让我先开始,过一两天看看进度”之外,没有其他好答案。
完成这项任务的终极方法是“我害怕未知”,即将盒子里的所有积木摆放在地板上。试着根据颜色和形状将它们分成不同的组。然后看着图片,在脑海中勾勒出如何组装积木的路线图。
这种方法是错误的,原因有二。首先,如果城堡太大,你可能永远也到不了那里。其次,也是最重要的一点:你无法评估进度。你可能走在正确的道路上,也可能完全没有。你没有反馈回路。
另一种方法是开始建造城堡。随着你的不断尝试,你会逐渐了解是否容易找到所需的部件。你也会知道图片是否展示了所有细节,或者实际建造过程是否比看起来更复杂。
基于这些信息,你将能够对完成这项工作所需的时间和精力做出更有根据的预测。以及它是否值得做。如果你明天早上就需要建造它,也许去商店再买一座同样的城堡是更好的解决方案?这或许不是你的决定,但问题的解决方案并不总是通过更多的代码来实现的。
2. 接受妥协
开发人员经常赞扬和重视“高度关注细节”。每个工作机会都以某种形式包含这种要求。这很好。但不要把关注细节和固执地追求完美混为一谈。
开始一项大任务时,你必须定义两个版本。第一个版本是最低限度的,你需要验证所有事情是否按照你预期的方式进行。
这就是我所说的“横向工作”。你每个大型任务的组成部分都是纵向的。你不必深入研究就能获得初步成果。先专注于主要的用例或场景,然后以此为基础继续推进。
您可以在第一个版本中保留的内容:
- 错误处理
- 边缘情况
- 干净的代码
- 配置
别想那么多,直接写你需要的代码,让它从你的指尖流淌而出。你应该很快就能达到较高的功能覆盖率。当然,从这个最初的、简单的版本过渡到最终版本需要花费很多时间。那么,这样做的意义何在?
这是更稳妥的进步方式。你需要验证你的假设。无论你多么聪明能干,设计和概念化也只能让你走这么远。亲自动手去做。这比任何“深思熟虑”都能给你带来更多的知识和洞见。
这与商业领域中新产品或新功能的 MVP 理念相同。这种方法还有一个优势,那就是你可以展示你的第一个版本并获得反馈,或者提出问题。基于现有代码进行头脑风暴比基于图纸或概念更容易。
乐高比喻:你正在建造城堡的塔楼。图片中,高耸的塔墙由灰白砖块交错砌成。塔楼里锁着一位公主,屋顶上还有一条龙。
你找到了公主和龙,但要找到所需的灰白砖块却需要很长时间。正确的方法是用任意砖块砌墙,然后放置公主和龙。你可以留下一个 TODO,例如“改进墙砖”。
意思是,你已经发现了一个问题:建造一堵完美的墙将会非常困难。让我们接受这个事实,并继续探索所有我们尚未发现的障碍。接受妥协可以防止你陷入困境。
即使你永远无法完成待办事项,你也可以告诉你的客户:“这是完整的城堡。我们知道需要改进塔墙,但它已经建好了。” 这比“我们花了很长时间才找到合适的塔砖,所以进度严重延误了。不过你看,塔很完美,和你发给我们的图片一模一样。现在我们要开始建造城堡的其余部分了。” 要好得多。
重要的是:不要混淆妥协和草率。
塔的关键元素是公主和龙。别以为把农夫放在塔里,把猫放在屋顶上,就觉得这样可以妥协。这根本行不通 :)
3. 从外部世界开始
有很多事情你可以掌控,也有很多事情你无法掌控。十年前,我作为开发人员接到的第一个任务就是吃一堑长一智。任务是集成一个外部 API 并处理数据。我只有一周的时间。即使对于像我这样经验不足的人来说,这个时间安排也非常合理。
这是我所做的(你不应该这样做)。
星期一早上。我花了十分钟阅读 API 文档。看起来很简单。我创建了一个测试数据集,然后开始编写代码来处理它。完成后,我会用真正的 API 进行测试。
周三早上,我快完成了。我觉得我的代码很简洁,设计得很好,其他一切都很好(其实不然)。现在我只需要集成 API,应该就能提前完成了。我不禁感叹“我太棒了”。
我很快就到了 API 部分。我试着用我的代码访问它。但是不行。出了点问题。我浪费了一整天时间反复检查所有内容。使用了不同的 API 客户端。毫无进展。时间一晃,已经是星期三晚上了。
我陷入了困境,感觉一点也不好。
周四上班时,我向一位同事求助。他告诉我,访问 API 可能会受到 IP 限制,我需要联系公司,把 IP 添加到白名单。太好了,我找到了解决办法。
我给 API 的所有者发了一封邮件。当时大概是早上 8 点。我傻乎乎地指望着几分钟内就能得到快速回复和解决方案。我整个上午都在冒汗,直到中午才终于拿起电话拨打了客服电话。我解释了我的问题,并尽可能地强调这是多么“紧急”的情况(其实不然)。
电话那头的人跟我解释说,白名单的生效时间通常是一两天。我现在很郁闷。一两天?这怎么可能?我的任务是世界上最重要的(当然,只有我自己才这么认为),他们却告诉我“一两天”?
突然之间,我不再领先了。我迟到了。我失败了。我去找老板,告诉他我搞砸了。我应该周一早上检查一下 API,这样我就能当天就申请访问权限,同时还能写好代码。他只是笑着回应:“是的,你应该这么做。”
我终于在周五拿到了访问权限,不得不加班到很晚才能完成工作。我根据API数据带来的诸多意外情况调整了代码。再见了,设计精良、简洁的代码。之后我会解释道:“没时间做这些”(我的确有时间)。
当时我太天真了,觉得访问权问题和文件错误都是运气不好。现在看来,一切如常。
教训是,要从你无法控制的事情开始。确认你对环境的每一个假设。尽早使用手动且低成本的方式进行尝试。
乐高类比:想象一下,你正在搭建城堡,一切进展顺利。现在,你已经把盒子翻了大约100遍,寻找积木。你不禁想:“我从来没见过图片上那条坐在塔上的橙色巨龙。”
你忽略了这些信息,专注于你取得的良好进展。这是人之常情。前进比解决问题更令人兴奋。最后你不得不承认龙不见了。而且很晚才告诉你的客户,套装中较大的那部分不会出现。这可不是什么好事。
你应该做的是继续跟进这个提示:“龙在哪儿?” 花时间确保它不在那里。立即提出问题。告诉你的客户:“嘿,盒子里没有龙。我无法用其他积木拼出一条龙。我们该怎么办?”
令人惊讶的是,人们在及早发现问题时,往往能够坦然面对。及早发现问题,就能带来更多可能的解决方案。“我们还要继续知道没有龙吗?”“我们可以单独买龙吗?”“我看到盒子里有只恐龙。我们可以用它来代替吗?”
4. 划清界限
当你开始为现有系统开发新功能时,首先要定义它如何与现有代码交互。当然,你应该尝试遵循 SOLID 原则等,但关键部分比这更简单。只需尽量减少接触面即可。
清晰定义切入点的简单过程将提升你的解决方案。它将迫使你解决关键问题:用户或系统将如何使用我的代码? 我将获得哪些输入?我应该生成哪些输出?它能帮助你保持专注。如果你对正在使用的系统还不太了解,这一点就更加重要。在深入研究已知知识之前,这是一个很好的机会来探索未知领域。
它还可以轻松启用或禁用该功能。您可以使用布尔标志或更高级的功能切换机制。
乐高类比:假设你需要扩建城堡。要求相当高,所以有足够的发挥空间。但你不能触碰现有的城堡。
你可能会想建一个很棒的扩展部分,结果却发现根本没有地方把它连接到城堡上。这很遗憾。你得赶紧修改扩展部分,让它能装进去。
正确的方法是先考虑接触面。扩展部分应该放在城堡的哪个位置?我可以把它固定在哪些积木上?它们的形状是什么?把连接扩展部分的几块积木拼在一起,确保它们连接牢固。这样,你就可以自由设计任何你想要的扩展部分了。
5. 不要太干
DRY 代表“不要重复自己”。这可能是最容易遵循的规则了。只要发现重复的代码行,就应该进行抽象。抽象可以是基类、辅助方法,等等。
接下来会发生什么?下一个人来了,通用代码需要修改以涵盖更多情况。他或她添加了参数和 if 语句来应对新出现的复杂性。很快,最初简单的 5 行代码就变成了 30 行,让人难以理解到底发生了什么。
较差的可读性对于代码重复来说不是一个好的选择。
最好保留重复的行。这样你就可以随意修改每个实例了。
下次你提到“抽象胜于重复”时,问问自己:你见过多少次有人放弃抽象?比如删除基类,再把通用代码放回继承类。我敢打赌,答案是从来没有。
原因是抽象和设计模式通常很酷很复杂。如果存在,那么“一定有充分的理由”。所以,一旦你引入了抽象,它很有可能永远存在。
这是否意味着你永远不应该使用抽象?不。当抽象符合需求时,才使用它们。例如:
- “我们希望记录对此方法的每次调用,包括输入和输出”
- “我们希望记录每个包含数据 a、b、c 的 HTTP 请求”
- “每次创建用户时,我们都需要做这做那
这些都是很好的抽象候选对象,并且还有更多示例,但请注意,这些需求的技术性比业务相关性更强(例如日志记录、安全、分析等)。抽象友好的需求很少出现在你的业务领域中。
为什么?因为业务领域与现实世界息息相关,而我们无法掌控。项目初期的假设往往很快就会失效。不要为了避免重复而过度设计代码。
乐高类比:无。乐高积木中没有 DRY 概念。
总结
聪明地工作并不是为了写出更好的代码,而是为了弄清楚需要做什么,并安全地朝着目标前进。
大型且富有挑战性的开发任务总是充满未知。拥抱它,学会应对它。
如果您保持事情简单,并与您的团队、老板、客户,最好是每个人对结果的期望保持一致,那么您的工作效率将会更高。
感谢阅读!
最初发表于The Fire CI Blog。
鏂囩珷鏉yu簮锛�https://dev.to/jpdelimat/the-top-five-developer-skills-that-ll-make-you-a-hero-hint-involves-legos-5ff7