代码与质量的思考与随笔

2,419

让我们从正经的聊聊 API 设计开始

在上一篇文章里,我建议 API 应该围绕消费端需求展开设计。“以消费者为中心”只是我个人的经验之谈。但是如果你有心去发掘,你会找到更多 API 相关的设计指南

在 《Designing Web APIs》 一书的第四章里,作者提出了他认为的 Design Best Practices. 包括以下几点:

  • Designing for Real-Life Use Cases
  • Designing for a Great Developer Experience
    • Make it Fast and Easy to Get Started
    • Work Toward Consistency
    • Make Troubleshooting Easy
    • Make Your API Extensible

在实际的叙述过程中,他举了几个来自专业人士的建议:

When we asked Ido Green, developer advocate at Google, what makes an API good, his top answer was focus: “The API should enable developers to do one thing really well. It’s not as easy as it sounds, and you want to be clear on what the API is not going to do as well.”

No matter how carefully we design and build our core API, developers continue to create products we’d never expect. We give them the freedom to build what they like. Designing an API is much like designing a transportation net‐work. Rather than prescribing an end state or destination, a good API expands the very notion of what’s possible for developers. —Romain Huet, head of developer relations at Stripe

Don’t overcomplicate your API and don’t future-proof it too much. Often by future-proofing your API, you make it too generic and/or too complex. Developers building applications on (or using) your platform are building stuff for the “now.” They like to move quickly and are not always thinking 10 steps ahead. Your API should cater to this mindset. —Yochay Kiriaty, Azure principal program manager at Microsoft

他们更倾向于关注当下,关注开发者。而并非以提供者为中心

而在 《APIs: A Strategy Guide》 一书第五章 Key Design Principles for APIs 中作者也同样花费了一章的篇幅来表述他认为的 API 设计原则。其中非技术性的原则有

  • Design APIs for Specific Audiences
    • Designing for Developers
    • Designing for Application Users
  • Best Practices for API Design
    • Differentiate Your API
    • Make Your API Easy to Try and Use
    • Make Your API Easy to Understand
    • Don't Do Anything Weird
    • Less is More
    • Target a Specific Developer Segment

在这两本书中,作者们提倡一种“渐进式”和“基于反馈”的开发模式:

Successful APIs often start with the absolute minimum amount of functionality, and then add functions slowly over time, as feedback is collected.

When talking to companies in the midst of launching an API, we often ask “Who are your target audiences?” If they answer by saying “Everybody,” we get worried. Similarly, if we ask them “What kinds of apps do you expect to see built?”, we get worried if their answer is, “All kinds.” Why? It’s really hard to create an API to meet the demands of every possible constituent and every possible use case. And even if you had the perfect API, you can’t market effectively to all these segments.

如果把以上种种称之为理想流派的话, 在真实的世界里,我不止一次听到的是另一种声音是:接口应该是尽可能稳定且一成不变的基础服务,包容来自不同客户端的消费。这又回到了上一篇文章我反对的起点:即一种大而全的渴望一劳永逸的 API 设计。

但是我们不应该批评这种做法,你可以把这种做法当作称之为现实流派,这其实是目前绝大部分互联网公司的现状。想象一下你随便在公司找一位研发同学和他聊我们应该精细化迭代和运营 API ,虽然他嘴上不说但是他的表情已经仍不住想告诉你:你疯了吧。这里的“疯”隐射的是不现实:一方面我们没有足够的时间和人力分配到这样的工作中来;另一方面即使我们这么做了,我们又能从中得到什么呢?KPI 依然没有完成,用户增长依然停滞不前。所以折(底)中(线)的方案是,简单粗暴,能用即是好用。更重要的是,你身临其境的看到这样的 API 开发也是奏效的。

关于 API 设计的讨论此为止。在这里我想引出另一个问题:既然我们理想和现实如此分裂,甚至无力反抗现实时,我们是否仍然关心设计?

“关心”这个词很微妙,它同时表达了两层含义:Why and How


在《The Mythical Man Month》一书第一章的开头,作者 Brooks 对 Program 和 Programming Product 进行了区分,前者可以仅仅是程序员个人在车库里的产出,而后者则是

This is a program that can be run,tested, repaired, and extended by anybody. It is usable in many operating environments, for many sets of data. To become a generally usable programming product, a program must be written in a generalized fashion.

作者之所以对这两类程序做出不同的划分,是想说明它们的生产代价不同, Programming Product 的成本至少是 Program 的三倍以上 。而在我看来,它们存在着性质上的不同。

每天你都能在掘金社区或者 Medium 网站上看到诸如 Vue + Webpack + Express + GraphQL 全家桶组合教程。Wow,全栈、 最新最夯的技术、融合官方推荐的最佳实践,无懈可击简直无敌了。新生、完美、纯净,但它只是一个 DEMO。我把它划分为 Program

而实际上这么多年我每天工作中需要维护的前端系统像是变异的怪物:可能 RequireJS 和 ES6 共存,可能 Grunt 和 Webpack 共存,还有许多团队老大写的半死不活的框架也被揉入其中,导致每次增改代码都如履薄冰。丑陋、臃肿、禁锢、可它是实实在在的线上产品。我把它归类为 Programming Product

而它们的根本不同点之一,就是代码的可维护性不同。毫不客气的说 DEMO 通常是一次性的,我尝试过了,证明它们的组合是可行的,再证明我能力就足够了。但线上产品中,你找不到任何一个不需要二次维护的程序。

软件行业有几条公认的真理:

  • 唯一不变的就是变化本身
  • 代码不可避免的会走向腐烂。

所以你们看到的所有“好”的东西,比如重构、TDD、DDD都是在尽可能的减缓和改善代码的腐烂,设计也是

腐烂并不意味着不可用,而是意味着维护成本极高。但是在互联网公司吊诡的事情是,维护性高并不算是一个问题,或者说它是个问题但不在解决的范围之内,一方面相比这样无法量化并且只有基层关心的与上线产品没有半毛钱关系的问题,跑马圈地的速度更显重要;另一方面如果因为员工离职率高真的到了无人能维护的地步,重写通常是最佳的选择。我很难想象一个系统在互联网公司内部能够维护 5 年乃至 10 年以上。即使有通常的结果也是名存实亡,它确实坚挺了很多年,但是已经不知道经历了多少次重写多少代团队的手了。However, 这样的做法无可厚非。


我不相信代码质量存在文化相对主义,代码质量是有绝对的好坏之分的。好比你无论如何都无法说服自己这段五百行的函数是一段好代码,无论是在任何编程语言中。

承认吧虽然我们每个人都不希望接手维护的是烂代码,但事与愿违总是在发生。以至于每次我都忍不住打开 git blame 看一眼究竟是谁的“好事”,然后默默把这个人记在自己的小本本上,也难免有几次发现是自己

但是你有没有想过,我们为什么要把代码写好?

我可以简单粗暴的回答没有为什么,这是职业道德。无论从事任何职业如果凡事只想敷衍了事就是不对的。但是站在软件工程的角度上看写好代码能够让程序维护性更好,仅此而已。

仅此而已,却难以上青天。

你可能会反驳我上面刚刚不是说了绝大部分公司不在乎程序的可维护性吗?站在公司的视角的确如此,但现在你我都不是公司的经营者。作为程序员此时此刻你是在乎的,我也是在乎的,我们都认可这件事,这就够了。

对个人而言这还关乎看来这关乎程序员的 respect —— 什么是 respect ?

如果你读过类似于 《Peopleware》之类 IT 企业管理的图书的话,你会发现 IT 团队管理和传统的企业管理有非常大的不同。有一篇很有意思的文章 《Opinion: The unspoken truth about managing geeks》 戳破了程序员不同与传统公司员工的另一类心态:

Few people notice this, but for IT groups respect is the currency of the realm. IT pros do not squander this currency.

Gaining respect is not a matter of being the boss and has nothing to do with being likeable or sociable; whether you talk, eat or smell right; or any measure that isn't directly related to the work. The amount of respect an IT pro pays someone is a measure of how tolerable that person is when it comes to getting things done, including the elegance and practicality of his solutions and suggestions.

IT pros always and without fail, quietly self-organize around those who make the work easier, while shunning those who make the work harder, independent of the organizational chart.

While everyone would like to work for a nice person who is always right, IT pros will prefer a jerk who is always right over a nice person who is always wrong. Wrong creates unnecessary work, impossible situations and major failures. Wrong is evil, and it must be defeated. Capacity for technical reasoning trumps all other professional factors, period.

程序员打心底有一种特殊 “尊重(respect)” 观念,这种对人的尊重和人的地位、职位、爱好、谈吐一点关系都没有,只和这个人搞不搞得定事情有关。 他们会自发天然的围绕在这种人身边和他一起共事,哪怕他是一个混蛋,只要他拥有把问题处理的有好又漂亮的能力。

很显然写出烂代码不是一种能够赢得尊重的行为,相反可能你反而会被边缘化。(当然也不排除在某些公司会劣币驱逐良币)

而设计是另一种形式的编码,从架构的角度影响着整个程序的可维护性。如果说架构需要设计,数据库需要设计,程序需要设计,为什么 API 不需要设计?


另一个 DEMO 的局限性在于,它无法体现无论是编程者还是技术本身解决现实问题的能力。例如任何一个新进框架总喜欢用 TODO APP 来证明自己的可行性和新特性。但是一个离线的有限个组件的应用和实际上它要解决的用户场景相差太远了。量变产生质变是一个说烂了的梗,但现实的情况就是如此。维护 1 个组件和维护 1000 个组件的方法是不同的;要解决一个用户和解决一万个用户的问题是不同的。以及当引入这个技术栈时,需要关心对团队会有什么样的影响,产能爬坡的阶段需要持续多久。

我个人的感受是,随着需要解决问题的复杂度加深和 case 定制化,个人经验乃至大众经验能够带来的帮助是越来越少。而你每天又不得不面对不同的技术方案决策,那么你如何驱动你的决策?至少需要一条原则,哪怕这条原则只是底线而已。说的轻松点,原则决定你走哪条路;说的严肃点,原则决定了你什么能做什么不能做。公司是这样,产品是这样,代码也是这样

我们认可了万物皆可设计,但是设计的原则应该是什么?

我最近看了两本非常好的书《Code Simplicity》和《Understanding Software》,它们不是在谈某一门编程语言或者编程技术,而是聊编程这件事。我非常认可书中作者提出的软件设计的终极目标:帮助他人。即使作为程序员你编写的程序类库,它们也是为程序员服务的,程序员也是人。

更有意思的事情是,作者认为一个人写出优秀软件的潜力,完全取决于他在多大程度上理解了 “帮助其他人” 的思想。所以当你下次想放松下来写一段烂代码时,多想想这件事,自然就被劝退了

这篇文章我只是想抛出把代码写好这件事很重要,而至于如何能写好的代码,这两本书里给出很好的答案。以及你能参考的各种最佳实践和方法论都是绝佳的材料


我最近参加了公司组织的几场培训,关于不同方面的能力建设。但之后的某一天我突然明白哪怕我参与了整整一周甚至一个月的培训,也不代表我立刻拥有了对应的能力。我保证各位的每一位领导公司都为他们提供了类似于提升领导力的培训,但是我们见过的混蛋领导还少吗。

程序员也是一样,会敲代码和合格的程序员根本就是两码事,会使用框架和合格的程序员也是两码事。那种已经有几年工作经验但是代码依然不堪入目的人我们也还见得少吗?在工作这么多年之后总是会不禁在想,我和坐在我斜对角那个维护着相同代码库的大三实习生相比竞争力在哪? 如果我的团队需要招聘新的成员,除了框架的熟练程度以外我还应该考察哪些方面?让程序跑起来一点都不难,这是底线。难的是如何让程序跑的更快,跑的更久,甚至通过一个程序让整个系统变得更好。

最后我想对我开头提出的问题自己做一个回答:我们应该承认和尊重理想流派的正当性。但是再实施层面,如果环境压迫着你必须走现实流派我也理解你。如果有机会的话,请尽可能的向理想流派靠拢,让代码变得更好

关于这一点在 《Understanding Software》一书当中有一段比喻特别好:

You have something like a house on fire, except that the house is the size of several mountains and it's all on fire all the time. You need to figure out which part of the "mountain" or "house " that you actually need right now, and get that into good shape so that it can be "used", on a series of small steps

Your first goal is to get the system into a place where it's getting better overtime, instead of getting worse.


本文同时也发布在我的前端专栏,欢迎大家关注