请停止使用 Ruby
我知道你在想什么,但首先我想说我爱 Ruby。Ruby 语言让我在失去编程兴趣多年后重拾了编程的乐趣。Ruby 让我的职业生涯焕发了新的活力,并让我成长为如今的高级软件工程师。Ruby 是一门很棒的语言。我只是希望你不要再使用它了。
对我来说,Ruby 是在 2008 年接触的,当时我在当地一个计算机科学系担任系统管理员。我之前用过很多编程语言写过代码,甚至还挺喜欢其中的一些,但我从未像刚开始使用 Ruby 时那样感到高效,也从未真正融入这门语言。当时我和另一个团队成员合作,用 Rails、PXELINUX和 BitTorrent 的组合构建了一个系统,用来重新镜像实验室的计算机。这真是一门疯狂的科学。而且它运行得非常好——不过那是以后再说的故事了。
自从第一次接触 Ruby 之后,我坚持学习并最终成为一名专业的 Ruby 程序员,原因有很多。以下是我继续学习 Ruby 的原因,我想你也应该如此。
Rails——最适合这项工作的工具
如果你想在 2008 年开发 Web 应用,那么 Rails 绝对是不二之选。当然还有其他选择,但几乎没有哪个既主流,又有合理的人才储备,而且经济实惠。
Rails 推动了后全球金融危机时代的创业经济。这不仅是因为那些“潮人”的涌现,更因为在当时,它是唯一一个能够让你如此快速地提高生产力,并在你那位“潮人”CEO决定将你的狗粮订购网站改造成有机麻跑鞋电商网站时,轻松调整应用的框架。
当时,“这项工作”是一个相当受限的问题集:从数据库中获取一些数据,将其压缩到括号中并提供给用户。然后获取一些用户输入并将其填充回数据库。
如今,Web 应用的范围已扩展至 WebSocket、单页应用、自带查询语言的 API、WebAssembly 以及一系列被冠以“云”称号的技术。如今的 Web 应用的外观和行为与 2008 年的同类应用截然不同——除非你看看 Rails 应用。Rails 应用仍然需要在数据库和扩展程序之间来回传输数据——只是如今扩展程序的种类更多了。
语义 - 只能运行
Ruby 的 DNA 源自Perl和Smalltalk的混合。与 Smalltalk 一样,Ruby 也具有动态类型的经典面向对象范式。
Ruby 是一种脚本语言,这意味着 Ruby 解释器读取源代码,进行解析并立即开始执行。没有正式的编译步骤。运行时代码定义(并重新定义)常量、类、方法以及其他所有内容。运行时代码可以编写更多运行时代码,也可以修改或删除现有的运行时代码。在某些情况下,同一个类的行为可能会有所不同,具体取决于您所处的词法作用域。Ruby 的语义非常灵活,难怪 Rails 会告诉所有人,他们的代码必须适合三个文件夹之一。
在用 Ruby 设计系统时,你会经常听到关于鸭子类型的说法——它指的是你不需要关心对象的类型,只要它能响应你需要的方法就行。这是一种无需事先定义协议的方式,这很有意义,因为没有“事先”,只有运行时。在常规情况下,它运行得很好,因为大多数 Ruby 开发人员都知道,只要你实现了each
它,就可以把它变成一个可枚举对象。
这在实践中往往意味着,动态类型会导致一些微妙的 bug,因为错误的对象在错误的时间出现在了错误的位置。最常见的情况就是臭名昭著的NoMethodError on NilClass
。这句话的意思是,你试图调用一个不存在的方法,nil
但实际上,在堆栈的某个底层,某个方法返回了nil
你期望的结果。它不会告诉你 bug 在哪里,只会告诉你它在哪个地方最终导致了系统无法容忍的后果。
因为 Ruby 的语义几乎无法进行任何类型的静态分析,而另一种语言的用户可以添加类型约束或至少模式匹配来避免这种情况,所以 Ruby 程序员只剩下一个选择:编写大量的单元测试,以形式化地确定哪些数据可以流入和流出方法和对象,并且当给出错误的输入时它不会爆炸。
鸭子类型的另一个副作用是,object && object.method
在调用协作对象的方法之前,通常会使用反类型检查来验证其至少为真值。这种情况非常普遍,以至于 Ruby 2.3 添加了&.
安全导航操作符,将这个设计问题转化为优点。
性能——只能有一个
首先,我们先来搞清楚一点。Ruby很慢。传统观点认为 Ruby“足够快”,而且你总是可以投入更多硬件来解决问题。这在一定程度上是正确的,但如今大多数机器都是多核的,而且……
我不认为自己是线程专家,所以我认为我无法对 Actor 库和线程库做出正确的选择。Matz
- Ruby 的创始人
Ruby 也不擅长并发。MRI 有一个全局解释器锁,这意味着一次只能运行一条 YARV 指令。有很多地方可以解决这个问题——比如让休眠线程等待 IO,但从根本上来说,如果你想同时做多件事,你需要运行多个解释器进程。
最后,Ruby 往往会占用大量内存,而且它不喜欢将内存返还给操作系统。虽然有一些方法可以在一定程度上缓解这些问题,但大多数 Ruby 开发者并没有意识到这一点,或者只是习以为常。
Ruby 核心团队在过去几年里投入了大量精力,我承认 Ruby 2.5 比以前快了很多。很多人会说它已经足够快了,但我不这么认为。
韧性——特殊情况
如上所述,即使在多线程运行时,Ruby 本质上也只运行一个解释器,没有内置的容错原语,因此未处理的异常将导致整个虚拟机崩溃。社区在编写 Actor 系统方面做出了巨大的努力,但最终这些团队放弃了,转而使用那些能够提供所需工具来构建产品可靠性的语言(例如Celluloid)。
工具 - 从 0X 到 DX
Ruby(尤其是 Rails)社区在提供优质工具帮助开发人员快速取得成果方面处于领先地位。Rake、Bundler、RSpec 和 Rails 命令行等工具确实为开发人员体验开辟了道路,然而其他语言在工具方面也迎头赶上,甚至超越了 Ruby。Rust 的cargo、Elixir 的mix和 Scala 的sbt都将这些工具组合成一个命令,并进一步将它们紧密集成到语言中。不仅语言提升了工具性能,客户端框架、DevOps 系统和云提供商也同样如此。
套餐——超越无限
Ruby 拥有大量的软件包(或者说是 gem),几乎涵盖了你能想到的所有功能。虽然不如Ruby那么多,但我认为它们的平均质量要高得多。需要一个开箱即用的身份验证系统?这里有一个 gem 可以满足你的需求。需要一个夸张到极致、架构上存在问题、并且与数据库绑定在一起的状态机?这里也有 gem 可以满足你的需求。
在 Rubygems 之前,实际上只有CPAN,但无需提醒任何人。Ruby 易于使用和集成的软件包模型铺平了道路,并已被许多其他语言采用,例如:crates、hex、CocoaPods和elm/packages。
那么 2018 年我应该使用 Ruby 做什么呢?
对我来说,irb
它仍然是我快速解决问题的首选工具,我仍然会编写一些 Ruby 脚本来协调一些一次性的小任务,例如移动文件或吸收 API 响应。
说到 Ruby,Rails 总是绕不开的。所以我想,问题应该是“2018 年我应该用 Rails 做什么”?如果你的应用符合以下条件,那么 Rails 依然会很适合你:
- 几乎完全由数据库驱动,
- 在服务器上渲染页面或生成 JSON,
- 大部分可以用现有的宝石组装而成,
- 没有复杂的业务逻辑,
- 不需要很快,
- 不需要同时服务很多人,
- 永远不会改变。
但我仍然认为你有更好的选择。
鏂囩珷鏉ユ簮锛�https://dev.to/jimsy/please-stop-using-ruby-4lf1