关于最佳实践的一些碎碎念,言辞有点激烈,可能会因此得罪一些人。


Hacker

这篇文章是有感而发,也算是自己软件开发生涯的一些所思所想的总结吧。IT科技在这几十年来发展迅速,各个领域都萌生出所谓的“最佳实践”。今天主要想来谈谈这些所谓的最佳实践可能并不是那么的“最佳”,甚至有的时候它们还会是“最糟”的存在。

表明立场

首先需要表明一个立场,我并没有排斥最佳实践的意思,我跟许多人一样是Ruby On Rails,Django这些Web开发最佳实践的全栈式框架的受益者。这些框架的开发者根据自己或者社区的经验把一些能够方便开发者的,比如,数据库操作,目录结构,路由定制等特性进行有效组织并集成到框架当中,虽说不同的社区侧重点有所不同,但是总体而言并没有脱离MVC这个基础模型。或许我可以认为即便它们“长相”各异,写法各异,分属于不同的社区,但他们的核心灵魂以及概念是想通的。

然而如果要脱离基础概念,区别对待,硬要从Rails,Django这些框架中选择出一个所谓的“最佳实践”就有点耍流氓了。前面也说过每个社区的侧重点不同,我以前写Django的时候还不知道有静态资源编译这回事,那时候的Rails早就有了AssetsPipline。另外,Django可能是为了让i18n的性能更加出众,相关的翻译资源都需要经过预编译的,而Rails则是运行时直接从yml文件中读取。那到底谁的做法更佳?似乎是个见仁见智的问题了。

为此请允许我简单把最佳实践归纳为

特定领域最佳思想之实践。

跟今天许多人所寻求的“最佳框架”,“最佳语法”的实践似乎不是同一个频道。你会以运行速度不够快来抨击Ruby,他会以啰嗦来抨击Java,还有人会以语法过于复杂来抨击C++。在这种层面上谁才是“最佳”真的能够争出个结果来?合适的才是最佳的。

下面我从几个方面来谈最佳实践,以及一些最佳实践为团队带来的负面影响。

最短即“最佳”?语法糖的滥用

今日之开发,抽象程度越来越高了。人们之间似乎流行出一种“最短”即“最佳”的想法。

想想刚入前端圈那一年工作组为了“掩盖”语言本身的“丑陋”,发布了ES6,确实带来了许多好处,比如总算有块级作用域了,class关键字也能用了,生成器,装饰器都有了。有了这些利器我们可以让代码变得更简短,优雅,这本身并没有什么问题,但是这似乎也为过度封装大开方便之门。

拿装饰器来说事,它实质上就是一层语法糖衣,它的作用大概如下

@decorator
class A {}

// 等同于

class A {}
A = decorator(A) || A;

这是一个不错的改善,能够精简语法。但是一旦被过用,代码将会变得有点奇怪了,我曾经见过这种代码

@a
@b
@c
class A {}

好好的糖衣变成了糖精-或者说语法糖浆(英语:syntactic syrup),指的是未能让编程更加方便的附加语法。这似乎应了那句话

金钱使浅薄的人更浅薄,使深刻的人更深刻。

最早接触装饰器是写Python的时候,我记得当时有个人说过:“即便装饰器很好,但是如果多了它会使代码变得晦涩难懂,所以请尽量限制层数”。作为程序员应该要在代码易读的基础上让代码变得更简洁,脱离易读性而单单追求简短的代码纯粹是炫技,如果真的只是为了炫技而写代码,何不直接写二进制?那就真的没几个人能看懂了,而且还会显得自己很高端。

统一写法等同于最佳实践?

最近许多人都追求写法统一

Write Once Run Everywhere

美其名曰提高开发效率,能够使得程序员更高产。React Native这些技术的出现,让JavaScript编写移动端应用成为可能,这似乎是好事,我可以只学会React技术就去编写移动端软件了。更有甚者MPVUE以及Taro等框架的兴起,似乎意味着只需要学React或者Vue就能够编写各种小程序或者APP了。假设你有一个React的团队,或许采用Taro这些框架将会比写原生的小程序原生语法更爽,许多人把这看成是最佳实践。不过据我观察,世界比你想象的要残酷。

毕竟我没能够开发出这种类型的框架,没资格对这些框架评头论足,但我只想就事论事。关于统一写法我有以下的考虑

1. 统一写法重要吗?

我记得有一些这类框架的拥戴者的说法是“微信的语法很多坑,所以我要上这个框架。”,“应该把注意力放在用React或者Vue来写特定业务,而不是小程序的坑爹语法。”等等。

对这些说法我觉得用Erlang语言的发明者Joe大大的话来回答最好

Erlang并不适合处理所有场景,如果你发现有其他语言更适合某种场景,我将会是第一个支持你使用别的语言来解决这个问题的人。

每门技术都有它擅长的事情,什么都想干,最终可能会导致啥都干不好。即便工作组不断在“优化”JavaScript语法,JavaScript也不可能一统天下。统一写法并不是银弹,能够用Vue和React语法来写小程序似乎是一件令人开心的事情,但回过头来想,学习小程序难度大,还是学习React难度大?答案显而易见。

难道上了一个框架写法统一了之后我们不需要学小程序了?可能性不大。即便是移动端这个相对稳定的平台也尚且不能做到,根据我一些移动开发同事的反馈,React Native确实能完成许多的业务场景,但有不少问题你还是需要通过编写的Object-C或者Java代码才能够解决(抱歉,我们公司还没有上Swift以及Kotlin)。

这似乎得替移动开发群体喊冤了,以前做移动开发懂Object-C或者Java即可,然后便是深入相关领域,学习调优,工资还蛮高,工作也好找。如今所谓的统一写法的“最佳实践”流行之后,除了懂这些之外还得另外学JavaScript,React Native,以及一堆的预编译工具。工资水平却似乎却不如从前了,工作还不好找。但本质上要解决的问题似乎跟以前没什么差别,这想想不也挺折腾的吗?

2. 真的解决了问题?

我们都知道一个叫做80/20的定律

80%的事情其实花费了你20%的时间,另外20%的事情将会花费你80%的时间。

请允许我将这个理论用在小程序上,我个人比较幸运,没什么机会开发小程序,以至于我可以用旁观者的身份来观察身边同事的工作。从他们的开发经历来看,开发一个微信小程序,只要是客户还算理智,正常的业务逻辑其实并不会耗费掉多少时间。小程序的价值不就是简单轻量吗?

然而小程序有个问题就是平台不够稳定,更新频繁。这个过程可能会修复掉一些Bug,但也可能会引入一些新的Bug。往往一个业务不怎么复杂的小程序为了“掩盖”这些问题所花的时间都要赶上业务开发的时间了。试问上一个封装程度更高的框架就能够“修复”这种问题吗?我感觉这像是沙堆上建房子。另外,大家遇到的业务场景不一样,你遇到的问题他不一定会遇到,因此我不相信一个基于不稳定平台的抽象能够自动处理该平台本身的问题。

这让我想起美剧《硅谷》里面盖文.贝尔森的对白

请大家想想有这么一个革命性的功能,一旦整合进“纽核力”平台,可以修复平台发布过程中出现的任何问题。

gb.png

他真不愧是21世纪最不切实际,最会忽悠并压榨员工的科技公司的管理者,而且这种类型的管理者似乎也不在少数。可悲的是,这也是很多人上一个框架的初衷,觉得框架会帮我们修复一些我们自己不愿意修复的问题,别做梦了,世界还是很残酷的。另一方面,使用一个基于不稳定的平台开发出来的框架,出bug的几率还是比较大的,当bug出现的时候,你可能还要花时间去定位是微信自身的bug还是开发框架所带来的bug,这都是比较耗时的工作。而这种时候,到底框架帮你解决了什么问题呢?

关于写法统一的问题,在2016年的RubyConf China里面一个叫Terry的演讲者(他现在是Nervos Network的CEO)在会上讲过一句话对我影响很大,大意如下

我发现今天的Web开发跟十年前相比并没有什么不同,既然十年前我们就用Ruby来写后端,用JavaScript来写前端交互,为何十年后的今天我们一定要统一写法呢?

回过头来看,这些业界所强加给你的最佳实践可能并没有你想象的那么的“最佳”。作为程序员我们是被雇佣来解决问题的,请尽量别让自己把问题复杂化。

前后端分离是最佳实践?

前后端分离到底是不是最佳实践?不同人会有不同的看法。在我看来解决问题才是目的,分不分离都只是手段罢了。 随着React,Vue这些框架的盛行,不分离者成了众矢之的,因为业界都觉得分离才是“最佳实践”,确实前后端分离可以让前端专注于自己的开发流程,但有的时候前后端一旦分离,挣脱了后端约束的前端便犹如脱缰的野马,一小心就成了Rei所说的前后端分裂了。

在充分利用Webpack大杀器之后,我们甚至可以用自己喜欢的任何语法来写前端程序,渐渐地可能会发展成一种只有自己才能够看懂的DSL,熟悉的人自然得心应手然而其他工程师要学习,理解,并且提出质疑都会花费彼此不少的时间,这都是成本。我始终觉得如果一个前端工程师写的代码后端需要花费很大的力气才能够读懂的话,那或许应该稍微反省一下了,是不是某些地方过度工程了?

前后端分离是把双刃剑,需要前后端人员相互协调项目才不至于失控。需要考虑分离带来的好处是什么,是为了表单提交更方便?还是为了页面交互更加流畅?并不是说Facebook出了一个React,而它用这个框架重写了Facebook还有instagram这些网站我们就一定要跟从。有时候还得考虑Facebook这些公司什么量级自己什么量级。如果只是需要提升部分页面的交互效果,或许Gitlab这种只把关键部分的资源进行分离的做法会更适合我们。不考虑实际情况,只是为了脱离后端把控而实行的分离在我看来都是搞事情。

最佳实践有时候会让你远离问题的根源

这是最近公司进行容器方面尝试时遇到的问题。最近升级官网服务,想要基于这个项目做一些容器化的探索。初步的预想就是脱离原始的capistrano部署方案(这不是一个好主意)然后

  • 对官网项目容器化。
  • 基于容器实现自动化部署。

OK,理论上我只需要学习如何用Docker相关的技术来容器化整个项目,然后再使用辅助工具来实现自动化部署即可。而这个时候运维小伙伴为我引进了Kubernetes(K8S),这个业界容器化的“最佳实践”。这是个好东西,但同时也给我带来了其他的问题

  • 根据K8S提供的最佳实践,我们需要把配置文件存在Etcd(一个配置管理服务)里面。
  • SSL证书要如何管理,是否应该采用更流行的Caddy服务?
  • 服务多了要怎么做反向代理?K8S有现成的东西可以用,以后做扩容也方便。
  • 有那些服务需要挂载数据卷?

好吧,在对K8S没有什么了解,并且没什么运维经验的人来说要消化这些问题难免需要点时间,我就花了一两周的空闲时间去看K8S的文档,其实要熟悉K8S的命令不是很困难的事情,但是要理清不同服务之间的关系,并实现对应的“最佳实践”却是比较头疼且耗时的工作。这期间还有一些其他项目的干扰,导致了新版官网迟迟没有得到部署,因为精力都花在别处了。

后来才醒悟过来,好像我们都沉浸于业界的最佳实践当中,被一堆所谓的最佳实践的概念所淹没,而忘记了一开始我们需要做的事情以及要解决的问题。其实我们想要做的本质工作只是把项目部署出去,最好是能够自动化。K8S以及相关的最佳实践只是可选项,我们沉浸于最佳实践当中,却没能尝到它带来的甜头,离问题的根源越来越远。

大家都倾向于实现一个100分的解决方案,却没有考虑到要做到100分所要付出的时间还有精力,或许很多时候一个60分的不甚完美的解决方案更适合自己。因此最终我们还是决定只完成容器最基础的部分,然后尽快把项目部署出去。K8S的事情延后,在以后的日子里再慢慢去优化这个只有60分的解决方案。

最佳实践让你束手束脚

这是一个“先有鸡,还是先有蛋”的问题。以前的人做开发普遍都是撸起袖子就干,等到经验有所积累之后慢慢重构已有的东西就得到了我们手上的框架。也就是说前人是先解决问题,然后把通用的解决方案抽象成框架,现在的人似乎不太一样。而如今许多开发者是遇到问题,先找最佳实践,如果没有最佳实践甚至不敢着手去解决问题。或许这也是Linux内核的开发者林纳斯曾经怒斥最佳实践的原因吧。他说

最佳实践很多时候就是狗屎。

当“最佳实践”成了一种思维的束缚,成为解决问题的巨大门槛的时候,它还算是最佳实践吗?当一个开发者被一个框架的思想所束缚,离开了指定的框架就无法完成任何工作的时候,那这个框架存在的意义又何在呢?还记得《倚天屠龙记》里面张三丰教张无忌太极拳还有太极剑的时候让他干嘛了吗?忘掉所有招式。如果这些所谓的招式对一个人来说只是束缚,那么还要它何用?

实际上很多时候你能够想出的解决方案已经足够好了。不可能有人一开始就能够把事情做得完美,Ruby On Rails经过了十几年的洗礼,至今还有许多可以优化的空间,市面上那些琳琅满目的框架有几个经历过这种时间的沉淀?

Erlang语言的创始人Joe老爷子曾针对一门语言不够快而发表过这样的言论

先把程序写正确,然后慢慢优化,许多年后它就会变快了。

比起一开始就追求高性能,最佳实践,或许有些时候我们可以先考虑如何把“程序写对”,哪怕这个解决方案不是最佳,起码你为这个问题思考过,而不是想着拿来就用。如果以后遇到更好的解决方案,我相信到时候再优化也为时不晚。

总结

以上是我对软件开发过程中“最佳实践”这个观念的一些碎碎念,我个人并不是完全排斥最佳实践,但是我认为所谓的最佳实践是有场景限制的,考虑到时间成本,实现难度,人员调配等各方面因素,在某些场景下的“最佳实践”在另一些场景下或许会成为“最糟实践”。什么都不想就随大流,强上“最佳实践”的做法无论对自己还是对团队都是极度不负责任的。