如何成为一个卓越的程序员

jopen 10年前

        英文原文:How-to-be-a-great-software-developer

        作者是 Rails/Angular 开发者,企业家& YC alum。早先创建了 Clickpass.com 网站并出售。目前担任 Brojure.com 的 OTO(唯一O(only)TO),兼职 entrepreneur first

        免责声明这是一篇非常长的文章,比我通常会写的主题要长得多。我编辑文章并发给朋友评审,直到他们都觉得文章没有一字需要删改的。我希望你也这么认为。

        如果有一件事是开发者都关心的,那就是成为更优秀的开发者。那你应该从哪里开始呢?你是否应该积累一些附加的卖点:比如专研 Node 知识和 no-sequel?或者你应该死记硬背专业的网关问题答案,并能按要求立即写出冒泡排序算法和短连接算法吗?或者有其他更基本和关键的东西更值得你投 入?

        我相信,作为一个程序员的资历和价值并不是以你所知道的东西来衡量的,而是通过你做出多少成就来衡量的。这两者是相关的,但又有本质的不同。你 的价值是你如何推动项目前进,如何鼓励你的团队也这样做。在我十五年的开发生涯中,我从来没有需要实现一个冒泡排序算法和短链算法。不过,我花费了成千小 时来编写和重构账户管理工具,编辑套件,缓存逻辑,邮件接口,测试套件,部署脚本,JavaScript 分层,分析架构和文档。这些都是有价值的事情,完成了这些事情推动了我们的前进。

        那些微小的组件是构建项目的砖瓦和沙砾,需要成百上千小时的辛劳工作来组建。尽管它们被用来组装复杂系统,它们本身却不应该是复杂的。你应该将 简化这些组件作为目标。多年来,我学会简单可以通过假以时日的不断工作和重构来达到,而这比纯粹“灵感一闪”的思考更容易得多。

        简单和卓越通过一些事情或者任何可以让工作完成并从回头重新审视这样的过程来不断完善,这是最可靠的路径。这也是那些公司和 MVP 试图深入我们意识观念的真谛,软件也是如此。从一些可工作但丑陋的解决方案开始,你不断应用这个丑陋和怪异的方案,不断重构它为最简单的形式。简单从工作 中比“灵光一闪”来得更为可靠。通过写代码比费力思索更加可预期。简单来自努力。

衡量一个开发人员的价值,不是通过你能达到的某个点的高度值,而是在于你表现线下的面积值。— Peter Nixey (@peternixey) April 22, 2014

        对于聪明的懒人来说,他们可以轻易展现自己的卓越才华,亮瞎同龄人的双眼,然而公司却不能建立在这些人之上,产品也无法落实在那些“卓越的才 华”上。公司是由那些每天进出办公室,提交良好的代码,并且也让其他人也干同样的事情的团队和个人。伟大的产品由勤奋的驭马驱动,而不是由那些盛装的舞步 马推动的。

        多年之前,Joel 创造了“明星程序员”这个词多,它依赖公司对需要这样缺乏合作的极客来完成所有事情的误解。的确存在拥有这样特质的人,但是人数不多。你发现他们的聪明缺 乏规律——让他们感兴趣的事情表现得惊人的聪明,然而在和其他人协作或与团队无缝合作时,又显得无法抱有期望。

        他们的成果是无法预期的,但是他们的成就又让人艳羡,而且更具传染性。他们的傲慢可能伤及到团队的其他人员。它大声而且响亮地释放这样的信号: 如果你够聪明,你可以选择你何时工作和你干什么。你成为了一个“Developer in Residence”。你不但吸取了薪水,也扭曲了你周围工作人员的价值。

        所以现实是,你和你的团队更可能依靠或者说应该依靠于那些值得依靠并可靠工作的人,而不是那些自认为是“明星程序员”或者“编程忍者”的人。

        卓越的开发者不是那些上手就可以写出冒泡排序或者短链接算法的人。他们是你一旦将其安排在项目中工作,就会不停推动和鼓舞周围每一个人来做到一 样的人。让明星程序员滚蛋,雇佣那些可以驾驭的驭马(Fuck Rockstars. Hire workhorses),下面一些方法值得借鉴:

        为你的函数和变量取个好名字(编写见名知意的代码)

        这是一个难以置信的简单开始,但是我认为它是编程中最重要的技巧之一。函数命名是问题定义直接表现,坦白的说,是编程最难的部分。(编注:ITWorld 在 2013 年发起的一个投票,结果显示:《程序员最头疼的事:命名》)

        名字是你代码的的边界条件,命名是你应该解决的首要问题。

        如果你的命名是正确的,也解决了边界条件,使用这样的名称,你几乎不可避免编写出了高功能的代码。

        考虑这样的函数:

def process_text string   …  end

        它几乎没有透露它准备做什么或者是怎么实现的,但是:

def safe_convert_to_html string   ...  end

        它告知了接下来会发生什么,通过这个函数你可以预期它会完成什么,并且你可以多大程度重载这个函数。

        开发者可能会很高兴重构一个“process_text”函数,同时将字符串转换为 HTML 标签并自动嵌入视频。不过,这在一些使用这些函数地方时可能是完全不期望的。一旦你改动了它,你就制造了 bug。一个清晰的名称,不但保住函数会做什么,也表明了它不会做什么。

函数名称为函数和调用它们的代码之间创建了契约关系。好的命名定义了好的架构。http://t.co/6JMyGvGuzl

— Peter Nixey (@peternixey) April 22, 2014

        一个好的函数,承诺了它会交付什么并如期望地交付了。好的函数和变量名称,不但使得代码更加清晰易读,也为你纵横交错的代码库建立了成千上万的约束。草率的命名意味着草率的约束,缺陷,甚至建立在它们之上更加草率的契约。

        不仅仅是函数命名可以衡量你的代码质量,变量命名也应该加强。有时为了简化,值得创建一个变量名来自注释代码逻辑。

        以下面的内容为例:

if (u2.made < 10.minutes.ago)      && !u2.tkn      && (u2.made == u2.l_edit)    ...

        糟糕的变量名称使得你很难弄清楚到底正在发生,甚至哪怕你这样做了(成功了),你也难以保证 100% 清楚原作者意图是做什么。它什么也没告诉你。

        “and not”声明经常让人困惑(请永远不要编写“and not”与名称结尾的代码),如果你的工作是重构这样的代码,你不得不做一些复杂的猜测来推断它原始的含义。

        不过:

if (new_user.created_at < 10.minutes.ago)     && !new_user.invitation_token     && (new_user.created_at == new_user.updated_at)

        我们将这些变量修改为一些更有意义的名字,代码的含义立马就变得更清晰了:

user_is_recently_created = user.created_at < 10.minutes.ago  invitation_is_unsent = !user.invitation_token  user_has_not_yet_edited_profile = (user.created_at == user.updated_at)   if user_is_recently_created     && invitation_is_unsent     && user_has_not_yet_edited_profile    ...

        我们还可以进一步要求对 if 语句声明中的每一部分进行分离,命名组件并做文档注释。

        需要一些勇气来写像“user_is_recently_created”的变量名,因为这样的命名逻辑模糊。不过我们时不时这样做了,并且承认这告知了代码阅读者你做了什么假设。

        注意,这些方法比使用注释要更加有效。一旦你改变了代码逻辑就会要求你改变变量的命名,而使用注释却无法强制这一点。我很认同 DHH,注释是危险的并且容易腐朽——最好是编写自注释的代码。

        代码自注释越充分,其他人就更可能按照其初始的意图来实现它,代码也会有更好的质量。记住,在计算机科学中只有两类问题最难:缓存失效、命名和 off-by-one 错误。(我怀疑作者是写错了,这是三个…)。

“如果你想成为一个伟大的开发者,请确保你写的代码让人见名知意:也就是说代码精确地完成了它名字告诉的东西” http://t.co/6JMyGvGuzl

— Peter Nixey (@peternixey) April 22, 2014

        在学习广泛之前先精深某一方面——从里到外学习你选择的技术栈。

        只有非常少的编程问题是真正新的。很多公司所做的技术工作,是之前的许多团队已做过的。在 Stack Overflow 上吸引眼球的问题很少没有在其他地方遇到过。

        因为这个确切的原因,你正在努力做的大部分事情,已经被你当前使用的技术栈解决过了。我有一次使用了 Rails 自带的简单而强大的方法,将其他人写的 60 多行 Rails 代码重构为一行语句,

大多数程序浪费了大量的时间,用于重新实现那些现有的功能。http://t.co/6JMyGvGuzl

— Peter Nixey (@peternixey) April 22, 2014

        这不仅浪费了他们的时间,而且他们新构建的代码冗长且含有很多错误。新的代码需要新的文档注释,新的测试来检查,也让代码充满噪杂,并且难以阅读。和其他新代码一样,也容易产生 bug。使用技术栈经过严谨测试并且实际验证的代码,很少产生 bug。

        如果你是一个 Ruby 的开发者,请花时间好好学习 Ruby,尤其是数组的那些惊人多的方法。如果你是 Node 的开发者,请花时间了解它的架构、方法及理念体系。如果你是一名 Angular 的开发者,你应该敢于挑战,并理解正由核心团队锤炼的那不可思议的架构背后的逻辑。在你重新发明之前先询问。你行走在巨人的阴影下,花费一些时间找到他们 的踪迹,然后你会对他们已经建立的东西感到惊叹。因为,如果你不这样做,你只是把问题推给了后面的人,别人会指出为什么你偏偏选择了你自己的小路走。

        学会辨识糟糕的代码

        有时候,我注意到一些程序员挺不错的,只不过他们趋于安逸,并没有意识到他们的代码其实可以写得更好。这对于你个人的发展是一件极为糟糕的事 情,在你知道如何改进之前,先要知道什么是需要改进的。知道好的代码应该是怎么样的,坏的代码长得怎么样。据说,象棋大师比普通棋手花费多得多的时间,来 学习其他优秀棋手的如何下棋。我非常确信这对于开发者来说也是对的。

        能觉察到代码异味,是提升你能力的重要的武器之一——哪怕只是有一点点异味或者闻起来可能有点异味。异味代码,你可能不知道为什么,仅仅是觉得哪里不对的代码。

        你可能做某件事情用了 60 行代码感觉这很简单,这也可能让你觉得应该交由语言本身来处理却被程序员手动处理了的感觉,也可能是你觉得这段代码糟糕透顶且难以阅读。这就是代码异味。

        这不是件容易的事情,但是经过一些年,你会发现哪些代码存在异味,漂亮的代码应该是怎么样的。你会开发出对代码的审美观,对于丑陋事物所带有的丑陋理论会让你感觉很不舒服。简单即是美,而简单正是我们所需要的。

        真理是,真实有时候是丑陋的,但是你应该不断地追求美丽,并且当你感觉丑陋无法避免,你知道如何优雅地展示它。如果你不能编写出优雅的代码,最 少创建一个史莱克式的代码,而在这之前,你需要培养出对代码异味的感觉能力。如果你不知道好的代码是怎么样的,坏的代码看起来是怎么样的,那你怎么会想到 去改进它呢。

        编写可读性良好的代码

        我有一次听 Joel Spolsky 说,Stack Exchange 不是针对提出问题的人进行优化,而是对阅读答案的人进行优化。为什么呢?因为相较于提出问题的个人,有多得多的人会去查看答案——应用最大化原则应该对读者进行优化,而不是提问的人。

        我觉得你可以同样的方式对待代码。它可能只是由你一个人写一次。但是它可能被其他许多人阅读和编辑。你的代码具有两个功能:其一是满足你当前的工作,其二是面对在你之后的每一个人,因此代码应该始终对可读性和可理解性进行优化。

        “代码编写一年后,从原作者眼光来说,也是全新的代码 — Peter Nixey

如何成为一个卓越的程序员

        你代码里面假定什么?你的方法实际返回什么?这个四层嵌套的 if/else if and not/unless 声明究竟是在区分什么?

        有时候你需要的不仅是好的变量名,你也要围绕着代码进行测试,看它究竟需要什么,并使得代码经久耐用。有效的代码是可以工作的代码,并且始终工作,即使被公司里每一个人都改过,都还能如常运行。

        写的每一行代码,其读者会是那些对此不感兴趣的,或者时间紧迫的团队成员,他们可能要在接下来一年时间扩展这些代码。请记住,那个不感兴趣,或时间紧迫的人,或许就是你自己。

        根据代码的生命周期来评估特性价值,而不是它的实现成本。

        新的开发者总是喜欢探索和发挥。他们喜欢最新最炫的东西。无论是 Nosql 数据库,还是高并发的移动服务,他们想尽快了解和掌握所有这些东西,用完这些玩具,就撇下一堆垃圾给下一个开发人员来擦屁股。

狗不是为圣诞节准备的,特性也不是为下一个发布版本准备的。— Peter Nixey (@peternixey) April 22, 2014

        功能和架构的选择,会影响所有你在这之上构建的东西。一旦抽象泄露,你在抽象中陷得越深,就有越多的东西会被玷污,或者这个泄露导致很多东西都“中毒”。

        实验性的架构和某些很炫的特性应该极为谨慎采用。优先添加你需要的功能,而不是你想要的功能。同时请注重架构。把一些实验玩具留给边缘项目。你 创建的每一个组件,每一个前沿模块合并到你的项目,会快速地改变你的软件,也会让你的项目受伤或直接破坏项目。如果你不希望在项目后期除了止血而干不了其 他事情,请不要首先将上述应用到你的项目中。

        了解和评估技术债务

        技术债务是你写的代码但没有达到最优化或你想要的。它包含一些错误,虽然烦人,不过还是可以用的。它可能是一个单应用程序,不过你知道它可能发展为面向服务的程序,也可能是一个 20 分钟的计划任务需要被重构为 20 秒。

        这些成本不仅仅是累加的,而且是复合累加。爱因斯坦曾经说过:宇宙中没有什么比复利更强大的力量了。同样,在大型软件开发中,也没有什么比复合 技术债务更具破坏性的了。我们见过或构建的大部分项目,哪怕最小的改变也会花费数月的时间。针对码基(code base),人们已经放弃了编写更好代码的想法,只是希望在修改的时候不至于导致站点整个崩溃。

        技术债务是项目中一个可怕的负担。

        除非没有技术债务。

和所有其他债务一样,应用合理的话,技术债务也会给你带来巨大的杠杆效应。— Peter Nixey (@peternixey) April 22, 2014

        不仅是这样,技术债务可能是世界上最好的债务,因为你不一定每次都需要偿还。当你开发一个功能结果发现它是错误的,或者当你开发一个产品结果不 能正常工作,你可以丢弃掉它们然后继续。你可能丢弃功能相关的所有优化,测试和重构。因为这些不是必须的,那就不要去写这些。这个时候就应该最大化你的杠 杆,留个空白,避免错误,仅仅为你需要的测试而进行测试。

        在一个产品和功能的早期,很可能你现在做的是错误的。你还处在探索阶段。你要同时以产品和技术实现为核心。这期间可以大量借用技术债务。这不是修改零星错误和进行大量重构的时候,这时候你应该集中火力猛攻直到你达到(成果的)另一边。

        不过当你发现,你确信你已经到了正确的位置并走出了另外一边,这个时候需要进行梳理,并加强你的地位。把事情做得足够好来推动你前进,偿还足够的技术债务来进入下一个阶段。

对于初创公司来说,技术债务和其他许多事情一样是一场跨越式的游戏。初始的代码是试探性的代码,它应该让你快速前进,发 现问题和解决方案,给你恰当的空间来建立营地。你呆的时间越久,营地系统就需要更多内容,而你需要构建更大更强来支撑它。如果你仅仅只需要停留一周时间, 不要浪费时间来构建一个可以支持十年的基础设施。

        检查,再检查你的代码,你的问题由你来修复。

        “把代码扔过篱笆”的工程师都是可怕的工程师。你应该保证你的代码是可以工作的,这不是测试人员或者你同事的工作,这是你的工作。懒洋洋写就的代码会拖延你,延迟周期时间,产生 bug,有让每个人恼火。

如果你一直提交破坏性的代码,那么你就在对团队其他成员不断征税。— Peter Nixey (@peternixey) April 22, 2014

        不要拿自己不当回事,觉得你只是个负担,问题应该自己解决。

        每天至少(只)花 4 个小时做实际工作

        对于讨论自我进步,关注和使用在开发者之间流行的生活技巧,简单的真理是:你不需要做大量的工作,就可以实现高效。真正重要的是,你能持续地做到这一点。每天花费最少完整 4 个小时来做恰当(proper)的事情,日复一日,你会成为团队中最有贡献力的成员。

        不过,每天都抽出 4 个小时来工作比看起来要难得多。

        恰当的工作意味着没有邮件,没有新闻,没有会议,没有杂七杂八的琐事。意味着一小时最少 45 分种的时间专注于(你正在做的事情)。一天 4 小时的工作意味着一天没有会议,没有漫长的午餐和休息时间讨论足球。我相信,一天扎实工作八小时几乎是不可能的。每天四小时也意味着你应该瞄准工作五或者 六个小时,这样你才可能得到四小时的认真工作时间。

        这也意味着你可以拥有丰满人生的同时,成为团队中一个卓有成效的贡献者。这意味着你不需要在 HN 上发表一个自我放弃“我忙死了,快来帮帮我”的帖子,这意味着你只要持续工作,你就能被重视和获得尊重。

        软件团队并不因为人们每天工作四小时而比工作七小时的团队进展慢(持续这样的方式是非常疯狂的)。他们慢下来是因为人们几周都没有找到方向,或 者那些响亮而空乏的嗓子,决定花费时间讨论 google vs 非死book 的获取策略而导致的无止境的咖啡休息时间。

        只要能工作就好,不要在乎你的进步看起来是如何缓慢或平庸…

每天工作四个小时,日复一日你会成为团队中最优秀的人员之一。— Peter Nixey (@peternixey) April 22, 2014

        记录已完成的事情,并和团队成员分享

        不管你是如何记录文档,是通过类似 Copyin 的邮件列表,wiki 或者是代码中的内嵌注释,你应该花时间来解释你的架构方法,和团队其他成员一起学习。

        在安装 Postgres 或者 ImageMagic 时遇到了问题?如果你觉得这解决起来比较困难,团队的其他成员可能也会遇到这样的困难,花费一些时间记录下来并告诉团队其他人你是如何做到的,节约他们下一次遇到问题时的时间。

        程序开发时最糟糕的事情是,整天和 bug 作战或者处理安装问题。如果你花费时间来记录和分享你找到的方法,你可以从预先为你同事准备中赢得五倍你花费的这些时间。

        理解和欣赏处理太多测试和太少测试之间的微妙平衡

        测试是一个强大的工具。它允许你设置一个发布基准,你可以信赖你的发布,让你不那么害怕制造它们。对发布的恐惧越少,你越这样做你改进得就越快。

        不过,这也可能过头。测试需要时间编写,运行和更多的时间来维护。

可以想象测试是盔甲,你穿得越多你受伤的可能性就越小,不过也让你更难进攻。— Peter Nixey (@peternixey) April 22, 2014

        你负重太多而无法前进,阻碍你弯曲四肢,无法移动。太少的话,第一次跨过混凝土地板的滑动就会伤到你,让你流血。

如何成为一个卓越的程序员

        关于如何进行适量的测试,没有直观的答案,某些项目需要比其他项目更多的测试,测试是你专业化需要学习的一个全新的领域。

        花时间去理解什么是真正需要测试,如何编写一个良好的测试。花时间去查看当测试添加值,或者最起码你期望它们是怎么样的。不要害怕进行测试,也不要害怕不进行测试。正确的处理方式是平衡,花时间去探索平衡点在哪里。

        让你的团队更出色

        这不同于其他点,这不是你可以独自采取行动,也没有明确的指标告知你其他行动是否有效。

        你的存在,是让你的团队变得更好还是更糟呢?你的代码质量,你的文档和你的技术有米有帮助到你周围的人。你是否激励和鼓励你的队友成为一个更优 秀的开发者?或者你就是那个导致 bug 的人,或者你坚持自己的观点浪费数小时讨论架构无关的废话,因为它有助于掩饰你没有做实际工作的事实?

        你应该让你的团队变得更好,总是有一两种方法你可以让你的团队变得更好,通过你素养的熏陶而帮助其他人变得更强。然而,成为一个孤独的“智者”可能是最缺乏价值的,或者说你能选择的最有破坏价值。事实上,如果你选择的维度并没有让你觉得厌烦,这可能不是一个好的选择。

        It’s not who you are on the inside that defines you

        这是一个谦卑的智慧,在蝙蝠侠开篇就有这么一句,这句也一直伴随着我。在电影的某个时间,蝙蝠侠在闲逛,表演着一个亿万富翁的花花公子。克里斯蒂安·贝尔恳求凯蒂·赫尔姆斯相信:他内在仍然是一个好人,她只是说了:不是你穿了什么,而是你做了什么展现你的价值。

        你作为一个开发者的贡献,不是由你有多聪明或你知道多少来衡量的。这不是由你简历上的技术名称缩写,你工作的公司和你上过的大学决定的。它们暗示你能做什么,但是你的价值是由你做过什么,以及这些如何改变了项目和你周围的人决定的。

        如果你想变得更好,请做好准备。

        翻译: 伯乐在线 周昌鸿
        译文链接: http://blog.jobbole.com/72457/