程序员如何成长

June 07, 2023

做技术是打怪兽不是养宠物,为什么要打怪兽?因为难;为什么难很重要?因为难的事情才能带来成长;为什么要成长?承认吧,因为「如何成长」是当代人,包括你我他在内焦虑的源泉。

过去几个月内我在写一系列主题为「NodeJS实战」的文章,内容来源是过去两年独自开发和运维 site2share 网站的经验,本篇文章是对这个系列的一个暂时收尾。

今天我不聊代码,聊些更重要的事情

养宠物

从两件事情开始说起

其一是在此之前,我直接或间接听到了一些来自早已离开项目同学的声音,陈旧的项目技术栈和代码是驱使他们离开的原因之一。

其二是在偶尔浏览掘金网站的内容过程中,有一类让我印象深刻的文章标题,大意诸如「教你用xx + xx + xx 打造一个开源系统」,因为我关注前端领域的关系,标题里的 xx 通常是围绕某个前端框架的时髦技术栈,从点赞和评论数来看它们颇受欢迎。

对于前者我当然理解:一方面脱离当下容易让自己丧失竞争力,不管你愿不愿意承认,简历驱动型开发是所有程序员秘而不宣的默契;另一方面陈旧代码给开发工作带来的挫败感不言而喻,我相信每个程序员面对「屎山代码」都有宁愿把它重写也不愿意修改一行的冲动。第二件事的出现也就顺理成章了:我想毫无负担地学习新技术,还能抛开白天工作中的螺丝钉角色,体验一次愉快的项目实战经历。

我把从零开始做新项目比喻为「养宠物」,因为它能给你带来无与伦比的掌控感。假设一个代码库完全是由你一手搭建的话,那么关于它的一切,例如如何启动、如何部署、它适用于什么场景又无法解决什么样的问题你都心中有数。如果你恰巧又在Thoughtworks 工作,那么Thoughtworks 工作体验更增强了这种掌控感的正当性:对于有坏味道的代码我们允许用工作时间进行重构,对于代码内不懂的知识点,只要提出问题就一定可以得到回答。

也许是我运气不够好,我的工作经验告诉我,「养宠物」般的工作机会是可遇而不可求,在大厂晋升靠造轮子而不是填坑是公理人尽皆知,但造轮子的机会屈指可数。维护遗留系统依然是我们大部分人的工作。这也就是我接下来想说的「打怪兽」,此时我们面对的系统哪怕只上线一年,源代码也可能是满目疮痍。

这里是对于下面不中听的一些话的免责声明,我不是在否定精通

React 没有价值,我也不认为简历驱动开发有什么错,只不过要小心它们让我们的眼界变得狭隘

打怪兽

真正的常态是我接下来想说的「打怪兽」。

之所以把它称为「打怪兽」,不仅仅因为你接触的代码会超出你的预期,你甚至想象不到你会遇到什么样(啼笑皆非亦或是让你无从下手)的困难:

- 这个一千行代码的文件应该从哪开始读起? - 我如何才能让代码进入这个分支? - 你发现项目用到的一个框架没有任何文档,在 github 上也找不到源码,原来是上一个离职的老大自己写的 - 项目的打包工具用的既不是webpack 也不是 grunt ,而是 shell 脚本 - 现在需要你优化一个超过包含上百个组件的 React 应用的性能

「怪兽」依然是一个友好的比喻,此时此刻你至少还能够把它具象化,将它和某些电影或者游戏里的角色联系在一起,这意味着它造成破坏的手段和范围是可以预知的。但工作中我们实际遇到的问题无法预测。

你一定想象不到在编写 site2share的过程中,困扰我最久的问题背后的罪魁祸首竟然是 ExpressJS 里的 trust proxy 参数,它导致 API 从来无法访问到部署在 Azure App Service 上的后端服务

为什么要打怪兽

实际的出发点正如上面所说,如果我们工作中绝大部分人、绝大部分时间面临的都是怪兽,那么逃避它就是自欺欺人。

说点不实际的,是因为打怪兽比养宠物更难——为什么「难」重要?因为难的事情才能带来成长。为什么要成长?承认吧,因为「如何成长」是当代人,包括你我他在内焦虑的源泉。

除此之外,我还想强调的是它在锻炼解决问题能力本身

随着工作的深入,越来越发现我的角色从「解决技术问题的人」变成「解决问题的人」:从 Javascript、SQL Server 到代码设计、代码规范,再到团队方向、团队培养。整个过程其实不允许你循序渐进地去适应,可能明天醒来新的问题就摆在你面前,你也永远也没有准备好的那一天。也许可以把团队管理当作一门新技术用学习编程语言的方式去学习,也许求助对的人是当务之急,也许有的问题压根可以不解决。但无论如何,思路不会有如神助般凭空出现在你脑海里,举一反三需要的是练习。问题的多样性在练习的过程中起到非常大的作用,解决新问题会带给你明确的反馈:我的经验可以移植到这个领域,亦或者我的工作模式需要调整。

或者忘掉我上面的长篇大论,通俗点说,打完怪兽以后你就是见过「地狱」的人了,还怕什么。我想起来大二时候为了制作这款软件代码被推翻了无数次,从那之后就再也不怕重构了

另一方面,养宠物的风险在于,它让我们不自觉地陷入舒适区中。

我曾经有差不多有一年的时间可以自由选择技术栈来开发各式各样前端应用。最流行的框架和搭配起来最时髦的全家桶便成了我的不二之选。在热门冷门尝试了个遍之后最终我难免会对自己产生怀疑:我似乎永远都在被输入,我永远都在给某个工具打工,如果今天哪个框架告诉我它是业内明日之星那我就要去学它,因为 fear of missing out 是每个技术人的通病。我似乎能做的也只有如此了,但这就真的足够了吗?

工具正在变得自动化,并且「帮助」我们专注于业务开发这件事带有迷惑性。这里的陷阱在于他能替你做很多事,会让你以为你具备同样的能力的错觉。例如虽然 Parcel 可以无须任何一行配置就把脚本打包得漂漂亮亮的,但你可能对背后的缓存策略一无所知。当每个人都在简历上强调「精通 xx 框架」的今天,我们应该问自己除了框架我还有没有更有力的竞争力?

这类陷阱还有另一种变形是,在团队内你只做业务开发。身处大型开发组中会让你以为你有独立驾驭一个相同体量项目的能力,但实际遇到的问题会非常受限,因为功能性需求和底层设计已经交给你们团队的 Tech Lead 甚至是团队前成员去做了。(公允地说这不是完全负面,而是一件需要把握平衡的事情。虽然这会给团队成员的成长带来不利,但另一方面却可以让项目风险变得可控)

「打怪兽」也是在打破你的乌托邦

打怪兽的另一层含义是经历实战

「教你用 xx 打造 xx」这类系列教程的前置条件太美好了:你有无限的业余时间投入其中,你就是你自己的产品经理。但实际工作中我们永远是戴着镣铐跳舞。例如糟糕代码不一定是个人能力的结果,考虑到当时的交付压力,团队状态和历史包袱,换做你不一定能做得更好。所以大部分技术决策其实是在恶劣环境下做出的,然而如何学习在不同环境中作出恰当的反应,我不认为这是脱离实践可以达成的。

另一个问题是它缺少对方案的闭环验证:我不确定有多少此类项目投入到真实的商业运营中,如果没有,很遗憾它的代码就不一定是有效的。例如它设计有异常捕获功能,异常捕获的目的之一是帮助我们在实际运营过程中排查问题,那当异常发生时它可以提供什么样的信息帮助我们定位到错误代码?通常在捕获异常之后紧接着要把信息作为日志输出,有相当一部分公司其实购买的第三方日志系统,那集成难度如何?如果只有零星的用户上报了此类问题,我们可否在实际生产环境下,在每秒上千条日志增速的日志海洋里甄别到他们?

退一步说,即使方案完美无缺,我们还需要关注它的成本如何。再一次强调,实际工作中人力、时间都是有限的,假使我们能做到满分条件也不会允许。当你把方案拍到老板面前,但是他告诉你预算只有三成时,选择留下哪三分之一的功能,或者说如何用三成的预算做出来一个及格的功能比纯粹的编码更棘手。老板更多关心的是风险,说实话「时髦」技术表达的并不一定都是褒义,它意味着技术的关注度仍在持续提升中,意味着它还可能没有被大规模地应用,也意味着我们其实有更成熟的方案可供选择。决策者都厌恶风险,因此在推广新方案时风险可控也是因素之一。除此之外代码的学习曲线如何?代码库毕竟在依赖团队维护,你应当考虑到团队下限对于新技术的接受程度。

最后,我建议技术同学也需要掌握一些产品思维,它也是只有实战可以带给你的,只有你设身处地地把自己当作应用的运营者才会意识到某些问题,这会对技术选型带来帮助。具体请参见我的上一篇文章:《个人开发者如何选购云服务》


你可能会喜欢

全世界都想让程序员专心写业务代码

最近两年我不知道你是否和我拥有同样的感受,编程容易了许多,同时乐趣也少了许多## 一最近有两个契机让我写这篇文章,一是过去一年我陆续把我所有 side project 后端从 Azure App Service 迁移到 Digital Ocean(以下简称 DC) 的 Dr...… Continue reading

Copilot 结对编程指南

发布于 2024年01月18日

Tech Lead 要学会戴着镣铐跳舞

发布于 2023年11月26日