The First 4 Years
兜兜转转一转眼都已经转行做开发这么老多年,经常会感慨时光飞逝,日子真的是越来越快了,很可惜事到如今我仍是一介菜鸡,也不知道后起追赶的速度有没有快过年龄的增大。四年时光不短也不长,心态也有各种变化,想要表达的东西其实很多,但是各种因素下我并没有什么倾诉的方式,很遗憾只有电子垃圾一直陪伴着我,交流表达能力不可避免地下降,想说却又无从说起。恰巧这两天看到一篇老炮的总结博客,描述了他的10年生涯体会,甚得我心,遂翻译到这里,顺便借此附上我的感受,算是借花献佛了。
原文地址:https://thorstenball.com/blog/2022/05/17/professional-programming-the-first-10-years/
上个月,2022年4月,是我成为一名专业程序员的10周年纪念日。
我在那之前就开始编程了,但是没有薪水。我在青少年时期建立了网站和 IRC 机器人,还写了微型 Python 脚本。然后我停下来弹了几年吉他。在我20多岁的时候,当我被要求建立另一个网站,并且发现我不在的时候网络发生了多大的变化(现在是 HTML5了!) ,我相当巧合地重新发现了我是多么喜欢编程.
这让我怀疑编程是否是比继续在大学学习哲学更好的职业选择。Robin慷慨地给我提供了一份带薪实习的机会来回答我的问题。
现在已经过去10年了,说实话,这既不是我作为一个程序员成长的重要标志,也不是我职业生涯的重要标志,但是意识到已经过去10年让我停下来反思。
以下是我回顾过去10年时想到的一些杂乱无章的想法。我学到的东西,我忘记的东西,改变了我看法的东西,从没想过会相信但是现在相信了的东西。
它们是我在帮助开发软件的环境下所得的产物: 作为 Robin 的实习生,然后作为 Robin 的初级开发者,作为一个小型德国初创公司的软件开发者,作为一个大型德国公司内部的一个初创公司的高级软件开发者,现在作为一个完全远程的,异步的美国初创公司的工程师。就当是免责声明吧。我敢打赌,如果我在一个游戏工作室、一个硬件公司或是一个大型科技公司工作,这篇文章将会非常不同。
8月,是我转行成为码农的第四年,毕业后我先是在船厂做CAD软件二次开发,而在之前,我根本没想过要去做一名程序员,甚至在填高考志愿的时候第一个排除的就是计算机。只能说造化弄人,当我真正体会到开发带来的快感,当我真正感受到制造行业和科技行业对知识的不同态度,当我发现行业间的巨大差距,一切都晚了,但反过来说当时也是决定改变的最早时刻。
在船厂呆了3年,传统型IT公司呆了一段时间,创业型公司呆了一段时间,2.5线厂呆了一段时间,看着也没什么东西,但其实已经会给人跳槽频繁的感觉,可是这就已经4年了,我有巨大的沉没成本,依然是一个性价比选手。如果我是科班毕业呢,我敢保证而不是打赌,一定不会是现在这副狗样,这篇文章也许就不复存在。我还有复杂的家庭背景,糟糕的生活琐碎,这是我的免责声明。
无所畏惧的品质被低估了
我尊敬的或以之为学习对象的大多数程序员,他们身上都有一个很少被提及的特点: 无所畏惧。
他们毫无畏惧地进入一个未知的代码库,无所畏惧地打开被怀疑为不合理的依赖库代码,在不知道如何完成目标的情况下开始工作。
看到别人无所畏惧是令人鼓舞的,但是让自己变得无所畏惧是我发现的最好的学习加速器之一。
现代大型软件往往是一个团队经年累月地开发完成,并且持续迭代,无论是业务系统还是中间件,当然,不排除天才效应。这样一份代码摆在个人的眼前,在你阅读的第一个瞬间感到望而生畏是一种很正常的现象,太庞大或者太复杂了,“我能看明白么?我能重新造个差不多的轮子出来么?”会有怀疑自己的想法。我想这是无畏被原作者提及的原因之一,很多时候确实是干就完了,但是现实情况往往更加复杂,软件工程并不能只是干就完了。我本身是一个悲观主义者,很多时候会给自己施加不必要的压力或者困难,尤其是在刚启动的时候,很难改,但是有意识地在改变。
当然,我编写代码是为了便于测试
我编写的代码很容易阅读和理解,或者很容易删除,或者很容易修改,或者很容易检查。我写代码不仅仅是为了让计算机执行。
这让我想起一句话:Programs are meant to be read by humans and only incidentally for computers to execute. —— Donald Knuth
如果不是极端场景,多考虑下代码可读性吧,不差这几ms。
没有什么真正重要的,除了给客户带来价值
类型安全,100% 的测试覆盖率,用代码流畅地表达业务逻辑的能力,完美的开发工具,不浪费资源的高效系统,使用最好的编程语言,优雅的 API 设计,快速反馈循环,编写优秀的代码 —— 这些都不是目标。
这才是真正的目标: 通过持续产出解决客户问题的软件,为客户提供价值。
上面这些事情可以帮助你做到这个真正的目标 —— 更快、更便宜、更有效率、更安全、更快乐 —— 但它们不是真正的目标。真正的目标是为你的客户提供价值。
陷阱: 编写软件往往比交付软件容易,但交付软件才是关键。
成年人的世界为什么黑暗,小说动画动漫偶像剧电视剧为什么这么美好,其实就是一句话:要恰饭的嘛。软件带来实际效益价值的时候是成就感最高的时刻,这是最高级的动力,满足最高层的需求:自我实现。不过如今精细化的分工,码农往往接触不到这个层面,所以最爽的事情就变成了流量的上涨,反之大流量反过来又能刺激技术提升,形成正向循环,平台的优势显露无疑。有很多项目死在半路上,到头来敲了个寂寞。最近注意到一本书《毫无意义的工作》,感觉写的不错,找时间看一看。
完美是不可能实现的
我不确定我曾经认为可以实现完美,但现在我确定它不是。一切都是权衡的结果。
你永远不可能在你所关心的每个指标上达到100% 。有些东西必须放弃。当你认为你做得很完美时,你很快就会意识到你忘记了一些东西。
我的审美观也变了。我现在不再寻找完美中的美,相反,我认为尽管有缺陷但仍然成功的程序是美的。看看这个小程序,把互联网连在一起,尽管里面有17个TODO命令。
软件工程、软件技术没有银弹,找到最适合的技术解决问题才是最重要的,权衡利弊,获得最优解。当然这是入职之后应该采取的行动,这对找工作却不是最重要的,很割裂也很现实。陷入对完美的追求会影响工作完成的效率,若是公司推崇结果导向,追求完美一定会拖累你,得不偿失。
如果你不能把它和生意联系起来,那都无所谓
你可以重构一个代码库,并对其进行显著的清理,使其更容易为所有人理解,更容易扩展,但是如果这个代码库在四个月后因为项目没有帮助业务而被删除,那么所有这些都将无关紧要。
你可以花几个星期的时间在你写的所有代码上添加监控追踪和提升可观测性,但是最后却只发现没有人会去查看它,因为这些代码一天运行三次,从来不会引起任何问题。
你可以调整和优化您的代码,以便高效运行,使公司可以将运行代码所需的机器数量减少一半,然后看到你节省的成本与你在优化过程中获得的工资相比不值一提。
你可以把你的时间花在做出色的技术工作上,但这些时间仍然是被浪费了。
我个人其实还是希望能够有所追求吧,很多人都丧失了对代码的热情,不是说他们不负责任,他们也很好地完成了任务,尽管是在屎山上继续堆叠,继续“同流合污”。这或许是新人与老油条最大的区别。保持热爱是一件稀罕的事情,也许是我的起点低,可以不太客气的说我身边基本上没有这种人,也很少遇到过。可能他们都在更好的环境里吧,那种在技术上发光的人,我不知道该怎么描述,并不是说要技术多么牛逼,一种源自内心的追求与灵性吧。
慢慢的,代码优化都是因为有激励、有评比、有KPI。我也曾一门心思不计所求的去优化,也真的把机器数量减少了一半,但这并不是多神的事情,很多时候是取决于项目是不是存在优化的空间,也许你只是把前人的债给还了。这确实很爽,但是优化完以后,公司出了对应的降本提效激励政策,那自然是没我的份,因为我已经把性能压榨的所剩无几了,那这要我如何感想呢?。。如果有下一次,我还会不顾一切的去优化么,真的不好说。那激励政策有错么,也没错,毕竟重赏之下必有勇夫。
就现实来说,漂亮代码真的无所谓,高性能代码很多时候也无所谓,但是个人真的很喜欢。
弄清楚规则试图阻止什么,然后选择性地考虑这个是否使用规则
如果你5年前问我 TDD、整洁代码、软件工艺和其他学派的思想是否教条,我会说“不!你看不出来吗?干净和好的代码很重要!”
现在回想起我认为“方法不应该超过5行”这样的规则是有用的的那些时候,我摇了摇头。
重要的不是规则本身!而是这些规则试图阻止的问题。如果你没有他们试图阻止的问题,或者你可以用另一种方式来阻止,你就不需要这些规则。
很多人会吹嘘各种各样的代码规范,引入代码检查规则,导致写个常数比较判断都要写成if (a != Constants.ONE)
,因为代码检查规则认为1是magic number不应该出现。但是要明白做这一切都是为了提高效率和体验,这反其道而行之了。当然这也不是绝对的,项目越变越大、人员越来越多,精心调制的规则无法完美适配任何情况,此时添加大而全的严苛规则或许是相对而言更好的选择,断绝腐烂滋生的机会。
编写那些让你确信系统按照既定方式工作的测试
不要过分担心测试是集成测试还是端到端测试、单元测试还是功能测试。不要和别人争论是否应该测试私有方法。不要再担心是否应该在测试中请求数据库。
相反,应该编写那些能够告诉你系统能按照它应该的方式工作的测试。理想情况下,3次敲击键盘以内,不超过1秒。
为了意识到这个问题,我花费了很长时间,很多最终没有任何作用的讨论,以及我的代码中的 bug 。
说句实话,单元测试这个东西我还真没有机会实际好好应用过,自己写代码测试自己写的代码,我总感觉怪怪的,是我太年轻了吧。
最佳实践通常基于这样的假设: 你清楚代码应该做什么
如果你确切地知道你想要构建什么,那么最佳实践和设计模式可以在如何构建程序上帮助你。
但是如果你还不知道这个程序应该做什么,或者在四周内它会是什么样子,那么一些最佳实践可能会使事情变得更加困难。
有些实践在代码重写时使用是最好的,但在探索阶段时使用则是最差的。
在初始阶段就惦记着使用各种花里胡哨的设计模式往往适得其反。其实不光是初始阶段,决定什么时候重写也是一门学问,很多时候业务代码不一定需要这么高的可扩展性,重构完了才发现你以后根本不会去扩展它,也许你疲于应付一个接一个定制化的、变动的需求,但是真的很难判断这是不是最后一个变动,变动会持续多久。
使用别人的代码没有我想的那么好
我的职业生涯始于写 Ruby 和 JavaScript,它们都有可用的包管理器,“难道没有一个包可以做到这一点吗?”这个问题总是悬在我的脑海中。
常识告诉我们: 如果可以,尝试使用库,而不是自己写。尽可能多地重用代码。不要重新发明轮子。不要复制粘贴。这是我多年来的信念。
但是这也有不利的一面,有时候自己写一个函数可能比添加一个依赖项要好。
依赖不是免费的。你必须让它们保持更新。它们会增加你的编译或加载时间。它们会在你的stacktrace上添加奇怪的东西。很多时候,他们做的比你需要他们做的更多,这意味着你付出的比你得到的更多。
当你将其他人的代码粘合在一起时,存在一个非常真实的危险,即粘合剂是复杂性积累的地方。但是粘合代码是你最不希望复杂性存在的地方。它隐藏了复杂性。你想要的是让复杂性尽可能地显现出来,用光照射它,希望它变成尘埃然后消失。
有时候自己写比用别人的代码更好。
用与不用也是考量。我喜欢那些专精的三方库,引用优雅的库也是一种优雅的做法,而那些大而全的、妄图按照它的方式解放你的库,不是很感冒,比如 Hutool 这种。这仅仅是个人观点。
有些公司明白,有些不明白,但没有人是完美的
为软件公司开发软件和为不得不雇佣软件开发人员的公司开发软件之间有很大的区别。在一家领导层可以获得软件以及清楚软件是如何生产的公司工作是一种乐趣。
话虽如此: 我不认为任何一家公司都能做到这一点。在某种程度上,每个人都在即兴发挥。
我还没到这个Level。
投资于反馈循环永远不会浪费精力
我从不后悔改进了反馈回路。更快的测试,更好的测试输出,更快的部署,把一个手动反馈循环变成一个只有一个键绑定的信号。
但是要小心: 一旦你看到了用真正快速和高信号的反馈回路开发软件的光芒,你就会永远渴望它。
没接触过,不太懂,用户反馈快速响应?敏捷开发?
总是在一天结束的时候留下一些未完成的事情
一个失败的测试,一个编译器错误,一个半完成的语句——用这些来结束你的一天,第二天早上你可以坐下来继续你中断的地方,完全跳过“嗯,我今天应该做什么… …”。
没有什么能比一个不及格的测试更能让我迅速开始。
是个好想法,但是这个真的很难忍,回去总是会惦记,影响休息么不是哈哈。可以试试。
完美主义是个陷阱
完美主义建立在谎言之上。你永远也不会到达你认为完成的地步,坐下来休息,然后说“啊,现在它是完美的”。总会有一些状况出现。你知道,我也知道。没有完美(见上文)。接受它,然后继续前进。
以 80% 为目标,另外20% 作为可选项。它释放了你,让你有了呼吸的空间。你可能最终会达到99% ,谁知道呢?
追求完美,但不陷于完美。
磨利斧头
我在我的工具上投入了很多: Vim、 git、 shell、 Unix 环境、测试框架。我真的很喜欢用我的 Vim 配置度过一个星期天的早晨。
但是有可能做过头,陷入配置阶段,无休止地修修补补。你必须使用你的工具来获得关于如何最好地配置和使用它们的反馈。
我也很喜欢搜集各种各样适用或者奇奇怪怪的工具,有的真的很惊艳,带来不少乐趣。也许有机会可以分享一下。
招聘很难
我现在已经做了数百个面试,我得到的最重要的见解是,招聘是非常非常困难的。一次采访的结果有如此多的随机输入,以至于一切都在强烈的“是”和强烈的“否”之间摇摆不定。
我经常希望有一种方法可以发现人们是否有“搞定一切”的基因。
站在应聘者的角度,我也觉得很招聘很难,工作需要的品质和面试判定的品质注定无法统一,但是大厂不在乎,反正有的是人,挑选最有效、最简单的方式就行,比如算法题,比如八股文。极少有面试让我感觉真的不错,但是还是有的,虽然最后都是gg,呵呵。有时候会觉得不甘心,觉得凭什么,但也只能接受事实,我就是不太会表达,还是没本事罢了。
开发人员最重要的特点是: 卷起袖子,因为它必须完成
和我一起工作的所有人都有一个共同点: 他们都在工作。他们知道有些任务并不有趣,不迷人,也不有趣。但必须有人去做,所以他们去做。
打工嘛,必须的。中年危机多也是这种日积月累,到头来什么也没剩下,却也无法改变。
与其他人一起在代码库上工作更长的时间
在软件工程方面,没有什么比多年来与一群人在同一个代码库上工作更能帮助我了。
你会看到决定是如何执行的。
你会看到什么最终是重要的,什么没有。
当你的同事在你编写代码3年后试图修改它时,你会看到你的代码真正具有多大的可扩展性。
你会看到你的预测“我们现在有两个这样的,但是我确信将来会有五个”是否会成真,并且在做其他预测时会考虑到结果。
你会后悔编写了一些代码,并且会为自己编写了其他代码而感到高兴。反思两者之间的区别,你会学到很多。
你将看到,工具出现故障仅仅是因为某个地方发生了变化,而你与此无关,但是你必须修复它。
你会说对一些代码片段说“我在这3年内从来没有想过这些”并珍惜它们。
你将看到代码库的哪些部分是新同事难以理解的,以及他们在哪些部分立即获得了生产力。
你可以看到你编写的代码在四年之后是什么样子。
希望我能有这种经历吧。码农的变动还是偏于频繁,而且大部分软件项目活不到这么长岁数。
了解完整的堆栈
没有什么事情比听到“你不需要知道它是如何工作的… …”更能激励我的了。
当然,我可能不需要知道,但是如果我没有尝试去了解 GC 是如何工作的,或者 Unix 是如何工作的,或者多线程是如何工作的,或者数据库是如何存储数据的,或者解释器和编译器是如何工作的,我就不会做我今天的工作。
这对我的工作也有好处。我可以更准确地进行权衡利弊来做出更好的技术决策,因为我知道引擎盖下面发生了什么。
多了解一点总是好的这没错,但是我觉得关键在于个人的精力是有限,生活也不只是代码。慢慢就偏向于有需要的时候再去详细了解,可能这就是我平凡的原因?
打字可能是瓶颈
我以前说过,不要让打字成为瓶颈。
确实,我也觉得练好打字还是挺重要的。我尝试过改变一些错误的打字指法,改变习惯并不是一件容易的事情,但是我认为相当值得。推荐一些我觉得不错的打字练习工具:DosBox TT、Typing Club(需要翻墙)。
代码审查不是防水的
在很长一段时间里,当一个 bug 通过我的一次代码审查时,我认为这是我的错。我漏掉它了!我怎么会没注意到呢?太明显了!
后来我发现这不仅仅是我一个人会出现的问题: 其他人也会在代码检查中漏掉 bug。事实上,他们接受并且自由地谈论代码审查并不是绝对正确的。我松了一口气。
它改变了我对代码审查的看法: 作为一个不完美的东西,它需要与其他验证代码的方法相结合。
关于CR,之前团队有CR的习惯,后来慢慢就没了,因为很少有精力、时间去过别人的业务代码,特别是作为一个固定的、日常的流程,如果是偶尔处理一下,那没什么问题。
并非每一次代码审查都值得付出努力
不是每个代码都需要彻底检查。有时候,如果风险是可以接受的,那么快速地回复一个“LGTM!”是没问题的。它解除你的同事的障碍,保持动力,并以某种方式建立信任。
做CR的人要对代码有热情,这是个稀罕东西,前面说了。
消极导致消极
你越是向消极妥协,你得到的就越多,总是比你想要的多得多。
这是致命性的。一开始是尖酸刻薄,然后变成愤世嫉俗,最后变成“一切都糟透了”。不久之后,“为什么还要费这个劲呢?”开始附着在所有东西上。结局是人们对你隐瞒兴奋,欢乐和想法。
消极太容易了。在某一时刻,我意识到,指着某些事情,说出它们的不好之处,然后耸耸肩,因为我没有预料到这会是不好的(所有的事情都是不好的,对吗?),这么做很简单。Easy to do and easy to mistake 被认为是一种能够发现缺陷和最坏情况的工程思维(事实并非如此)。
难的是看到事物的本来面目,看到它们的美。鼓励想法,即使它们几乎不值一提。创造和培养快乐。这很有挑战性。
所以在某个时候,我觉得我已经受够了,并且尝试着做一些有挑战性的事情。到目前为止,这对我很有帮助。
我是一个悲观消极的人,尽管我知道没有人会愿意看到别人的一张臭脸。很羡慕乐观主动的家伙,带动气氛,快乐生活。有的东西我很清楚,但是对于我来说,有点难,太难了。
并不能每个维度都时刻保持100%
我不可能每件事都做得一样好。我不能同时写一本书,在我的职业生涯中取得进步,成为一个伟大的父亲,在健身房设置PR,读两本书。这种情况不会超过一两个星期。这是不可持续的。
现在我让我的兴趣轮流上阵: 当我想在一件特定的事情上取得进展时,我会专注一段时间,并接受其他事情必须进入维护模式。
有时候会觉得自己时刻维持在50%,东西也没学好,休息也没休息好,无效焦虑,这也是一个值得注意的问题。
代码是有质量的
代码是有质量的。你不需要的每一行代码都是重担。它会拖累你的代码库,使得当你需要控制或改变方向时更加困难。你需要的代码越少越好。
代码必须被读取,必须被测试,必须保持兼容,必须保持安全,必须保持工作。即便它没有做任何有用的工作也需要如此。让这些留在身边也没什么坏处,不是吗?是的,没错。删了它,然后继续前进。如果有需要可以从版本控制系统中还原。
测试也是如此,只是我意识到这些时太晚了。
把旧版本的代码留在原地然后加以注释,是一种很常见的现象,核心就是不确定以后是否会用到或者怕需要对比的时候麻烦。我们知道git这些版本控制工具不会让你的任何修改丢失,讲道理你应该直接删掉它,若有必要时从版本库中取即可。唯一的问题在于删除的代码可能会被遗忘,而注释掉的老代码就像伤疤一样留在那里,虽然丑陋但是时刻提醒你它的存在,可能会影响你后续的动作或决策。话也不能说的太绝对吧,定期清理无用代码是一件值得去做的事情。
编程是我生活的一部分
自从我开始做实习生以来,我在工作之外花了相当多的时间在编程上: 阅读技术书籍,写书,做副业,写博客文章,做演讲,参加会议,学习新的语言和工具。
如果你能证明你真的很擅长编程,一些公司不会在乎你的大学学位,这是我多年来的动力。
我喜欢在工作之外花时间编程,但不是所有时间。有些时候感觉像是在工作。阅读一些技术书籍是需要付出努力的。但是有些事情在你做的时候并不需要有良好的感觉。
如果我只是在日常工作中编程和学习编程,我的职业生涯将完全不同。
作为码农,我相信或多或少编程都会“侵入”你工作之外的生活,尽管本身工作时间就会比较长一些。一般来说这个比例会逐渐下降,因为生活之外的事情增多或者对熟悉的生活感到疲倦。想当初真的是有点疯狂,尽管这些疯狂没有带来什么实质性的东西,但是仍然让我成长了不少,不论是技术上的还是其他方面,比较崇敬当时的自己,现在更多地是抵抗惰性,想办法延续学习热情。
至于学历,相对其他行业而言码农确实对学历的看重会低一些,但是卷嘛你懂得,学历不可能不重要,而且会越来越重要。我的学历并不算差,只是不是对口的,有利有弊吧只能说,利有可能在我看不到的地方发挥作用,而弊真的让我很头疼,这个与正统科班的差距也许我一辈子都抹不掉,不自觉地又悲观了哈哈。
电脑很快
构建网络应用程序让我觉得100ms 很快,而50ms 是真的很快。编写一个编译器教会了我,对于一台现代计算机来说,1毫秒是永恒的。
有的时候费了老半天劲可能耗时也就降低了几十毫秒,甚至更多时候运行耗时根本没有改变,对于用户来说并没有什么体感区别。性能发生质变往往是改变了方案,或是减少了调用,或是修正了Bug,或是调整了三方库的参数。对性能的追求是码农的基本素质,尽管用户无感,尽管不能让你多赚一点,但这真的很爽。
我依然非常喜欢编程
我写的一些东西可以理解为我这些年来变得愤世嫉俗了。我的意思是: 什么都不重要,完美是不可能实现的?好吧。
但恰恰相反,我还在乎,我非常在乎。但是我关心的东西越来越少,我仍然非常喜欢编程。
有句话叫做认清生活的真相之后依然热爱生活,之前没什么感觉,现在觉得这是一件困难的、值得尊敬的事情。我不知道我还能热爱多久,但起码现在还算是。其实这个博客也算是热爱的证明了,我写文章又慢挺累,也没有任何商业化甚至一丝丝曝光,基本没什么人看,我看过很多不错的个人博客,但慢慢的都停更了,而且本身独立博客就少,这里也算是个奇葩了,那应该就是热爱吧?菜、忧愁并热爱着,也不知会燃烧到几时。