[This article is the companion to my presentation for CodeBEAM America 2024, Elixir is the One-Person Stack for Building a Software Startup. You can download the slides as a PDF or view them in Google Slides.]
我想分享为什么我选择 Elixir 作为 SleepEasy 的编程语言(实际上,正如我们将讨论的,完整的堆栈)。 我将尽我所能专注于该语言的客观特征,这使得它特别适合小型、灵活的团队启动软件业务。
由于 SleepEasy 是 B2B 软件,因此绝对需要 Web 应用程序。 在遥远的未来的某个时候,移动应用程序可能也是如此,但我希望在很长一段时间内没有移动设备。 即使有一天我确实需要一款移动应用程序,一个简单的 Web 视图包装器可能就足够了。
事实上,我正在引导这家公司(即自筹资金启动并仅从企业自身利润中发展壮大),这一事实提出了另一个主要要求:该应用程序需要能够由一个团队来构建和维护,至少在最初几年或每月收入超过 1 万美元的情况下是如此。
谁关心单人框架?
查看全栈开发人员的招聘信息,并考虑他们需要具备多少专业知识。每个雇主都在努力寻找一位懂得以下内容的独角兽:
图为:一名全栈开发人员编排了两打工具来构建单个 Web 应用程序
- 超文本标记语言
- CSS
- 顺风
- 前端语言(JavaScript/TypeScript)
- 后端语言(Ruby、Python、Go 等)
- 前端框架(React、Vue 等)
- 前端状态管理框架(Redux、Jotai、Vuex 等)
- 一个后端框架
- 休息
- GraphQL
- SQL 数据库
- NoSQL 数据库
- 后台作业系统
- 像 Redis 这样的内存缓存
- 服务崩溃恢复系统(PM2、Upstart等)
- 消息队列(RabbitMQ、Redis 等)
- 像 Nginx 这样的网络服务器
- 码头工人
- 库伯内斯
- 云平台(AWS、GCP、Azure)
- 无服务器
- 微服务
- 扩展服务
🥴
太多了! 指望一个人能够完成这一切是不合理的。 对于创办一家独立软件公司的人来说更是如此,你在哪里 还 负责客户开发、营销、销售以及业务的所有其他部分。
所有这些都导致了一个不可避免的结论:
我们必须折叠堆栈!
我们需要 戏剧性地 减少构建一流的 Web 应用程序所需学习的不同技术的数量。 这就是 Elixir(特别是 Elixir 加上 Phoenix Web 框架)的用武之地。
Elixir 如何瓦解 Web 应用程序的技术堆栈
Elixir 通过三种主要方式帮助简化 Web 应用程序开发。
- 完全删除堆栈层
- 将更多堆栈构建到语言、标准库或 Erlang 的 BEAM + OTP 平台中
- 使用您已知的工具构建更多堆栈
让我解释…
删除堆栈层
凤凰城直播 得到了大量的积极关注,这是有充分理由的。 其要点是,您可以在编写时创建丰富的交互式客户端体验(可与 React 或 Vue 等 SPA 框架相比) 只是 “后端”代码。 通过构建 Phoenix 出色的 WebSocket 支持,LiveView 提供:
- 类似 SPA 的页面转换(即仅替换页面中发生更改的部分,而不重新加载整个页面),
- 当后端状态发生变化时,客户端视图的实时“反应式”更新,以及
- 服务器交互,无需构建 API 或编写 JavaScript。
而所有这一切或多或少都是免费的。 无缝、低于 50 毫秒的页面转换? 0行代码。 从前端触发后端事件? 3-4行代码。 订阅前端以获取某些后端作业的进度更新? 4-6行代码。
当然,也有一些警告。 LiveView 本身有一个很大的学习曲线,我不建议尝试构建基本上没有文档的东西。 (我们建造是有原因的 毛毡 作为 SPA 通过 WebSockets 与我们的 Phoenix 后端进行通信。)但是,如果您正在构建 B2B SaaS,那么 95% 的情况下,该产品会归结为管理仪表板、CRUD 应用程序或电子商务平台……而不是下一个图玛。
LiveView 是否已经 100% 取代了 JavaScript? 当然不是。 但值得庆幸的是,它附带了对“hooks”的支持,这样您就可以将部分功能委托给客户端 JavaScript(包括 SPA 框架,如果您喜欢的话),同时将应用程序的其余部分保留在 Elixir 中。 经过四个月的 SleepEasy MVP 开发,我的项目中总共有 16 行 JavaScript。
除了只需要掌握更少技术的具体好处之外,很难夸大在绝大多数开发过程中将注意力集中在一个地方(在本例中为后端)是多么美好。 必须考虑客户端 JavaScript、REST API 和后端之间的相互作用,就像从出生起就穿着一件加重的背心。 我并不认为这是一种拖累,但一旦将其移除,我感觉就像在空中行走一样。
将更多堆栈构建到平台本身中
除了 LiveView 之外,Elixir 也具有类似的堆栈缩减优势。 BEAM 和 OTP 为许多必须附加到其他生态系统中的并发和容错工具提供内置支持。
- Elixir 的容错原语( 进程隔离和监督树模型) 消除了整个服务级别的崩溃恢复需求
- Erlang 的 ETS 表提供了大多数应用程序需要从 Redis 获得的内存缓存功能,但不需要启动单独的服务(并处理在这样的分布式系统中可能出现的所有问题)
- Phoenix PubSub 提供了一个内存消息队列,可以替代 RabbitMQ 之类的东西
- 该平台周到的并发设计可防止任何单个进程导致系统其他部分资源匮乏,因此您可以在一台计算机上处理数千个并发请求,而不必担心它们相互冲突。
使用您已知的工具构建更多堆栈
最后,Elixir 通过建立一个基于您已知的工具的生态系统来简化应用程序。 这听起来有点奇怪,但考虑一下作业排队系统。 Elixir 处理后台作业的方式主要有两种:
- 一种是使用 BEAM 内置的、轻松的并发模型(通常通过 任务 或者,以迂回的方式,通过 基因服务器)—这适用于任何不需要对服务器重新启动具有鲁棒性的临时任务。
- 另一个是使用一个名为的库 奥本,与 Ruby 的 Sidekiq 相当。
Oban 运行在 Postgres(或 SQLite,如果您喜欢的话)之上,这与 Sidekiq 和由 Redis 支持的类似系统不同。 这将您需要学习(以及部署和管理!)的技术数量减少了一项,因为您可能已经需要了解您的 SQL 数据库。
Elixir 还通过这种方式简化了我的部署模型。 由于我一直在讨论的奇妙并发模型,当您增加系统上的 CPU 核心数量和 RAM 数量时,Elixir 的扩展性非常好。 像这样垂直缩放就是这样, 方式 这比扩展到运行应用程序的更多机器(或更糟糕的是微服务)更容易!因为您可以避免引入分布式系统问题,从而拖累所有未来的开发。 它需要零行代码更改和零额外测试才能为更大的机器支付更多费用……这不是你可以说的关于扩展分布式系统的事情! (作为一个额外的好处,部署与单个数据库通信的单个整体非常便宜!)
Elixir 堆栈在您已知的工具中构建更多堆栈的最后一个领域是测试。 虽然 ExUnit 是 惊人的 我可以连续几天赞扬它的可读性(有多少其他生态系统让整个社区都使用该语言附带的测试工具?),事实上, 一些 Elixir 中的单元测试框架并不是那么出色。 令人惊奇的是围绕 LiveView 的测试故事。
还记得 LiveView 如何让您从后端构建前端交互性吗? 它还可以让你写 测试 ExUnit 中的前端交互,而不是需要浏览器自动化,后者本质上既慢又不稳定。 您可以做出诸如“当我填写这些表单字段并单击此按钮时,我应该被重定向到标题为 _______ 的页面”之类的断言。 编写这些集成测试的成本(在运行时、开发时间、认知负荷和一般的麻烦因素方面)或多或少与我在业务逻辑中测试纯函数的成本相同,并且我发现自己在写作 方式 比我对 React SPA 做过的测试还要多。 如果我必须多次手动测试某个东西,你可以打赌它将成为集成测试。
它加起来是什么?
让我们回到全栈开发人员应该了解的原始技术列表,看看我们可以用我在这里描述的 Elixir 堆栈替换或删除其中多少技术。 根据我的统计,一个 Web 应用程序合理预期需要的 23 种东西减少到了 8 种(将 Elixir 中内置的任何内容都算作一项需要学习的技术,将 Phoenix 中内置的任何内容算作另一种需要学习的技术):
- Elixir(包括用于容错的监督树、并发原语,例如
Task
,以及用于缓存的 ETS) - Phoenix(包括 LiveView 和 PubSub)
- 奥本(Oban)提供强大的后台工作
- Postgres
- 您选择的 PaaS(我更喜欢使用 Dokku 自托管,这是一个类似 Heroku 的自托管 PaaS;其他人更喜欢 Render、Fly.io 或 Gigalixir)
- 超文本标记语言
- CSS
- 顺风
这还不错,特别是考虑到你进入 Elixir 时可能只拥有这些技能的一半。
要完整了解 Elixir 生态系统对全栈开发人员需要处理的原始列表中的每一项问题的答案,请参阅 下面的附录。
SaaS 初创公司的其他一些加速器
使用我上面列出的堆栈,您可以构建 95% 的 B2B SaaS 应用程序,并且比我见过的任何其他生态系统更快、更可靠。 也就是说,Elixir 生态系统还有其他一些领域非常适合自力更生的初创公司,如果我不强调它们,那就太失职了。
购买 200 小时领先优势
第一个是 Petal Pro 框架。 “Petal”引用了 PETAL 堆栈:Phoenix、Elixir、Tailwind、Alpine JS 和 LiveView。 (这是一个很好的缩写,但自从 LiveView 推出以来 LiveView.JS
早在 2022 年,您就可以处理纯粹的客户端交互,例如切换模式的可见性,而根本不需要 Alpine。)
Petal Pro 让您在实施绝对 吨 这些功能要么是每个 SaaS 应用程序的绝对要求,要么是 极其 非常适合监视、调试和提供支持。 我过去从头开始构建了其中的大部分,它们都是完全可行的,但需要时间。 花300美元就可以不用再去想它们,这是绝对的 偷。
对我来说最节省时间的一些:
- Stripe 集成用于订阅计费
- 用户分组的组织(包括发送和接受组织邀请)
- 管理仪表板(以及用于构建自己的管理仪表板的工具包,使我可以在一小时内制作出新的仪表板视图,这本来需要我 天 前)
- 用户模拟,以便当用户报告问题时,我可以登录并准确查看他们所看到的内容
- 精心设计的 LiveView 组件库,包含页面布局、菜单和所有内容的暗模式支持
优雅地使用 OpenAPI 规范
其次,人们总是担心生态系统的规模,确实,Elixir 的生态系统比 NPM 或 PyPI 小得多。 现在,在实践中,我发现软件包生态系统中的漏洞并不算太糟糕。 如果您只需要来自第三方服务的一些 REST 端点,那么编写这些集成并不难。 (不过,我在 C++ 中崭露头角,在 C++ 中,编写自己的依赖实现不仅受到鼓励,而且通常是最简单的路径!)但是,如果您需要与庞大的第三方 API 进行深度集成,那么这可能是行不通的。
这就是 AJ Foster 的所在地 open-api-generator
与大多数 OpenAPI 生成器不同,它提供了一种对自动生成的代码进行深度定制的方法,以生成符合人体工程学的 Elixir API。 生成器不是为您的第三方使用 OpenAPI 规范并将其批量丢弃(导致人类永远无法手工生成的糟糕 API),而是为您提供了以下方法:
- 重命名 API 的组件
- 将模式分组到模块命名空间中
- 将多个几乎同义的数据结构合并为一个
- …以及更多
可以看看对比一下 AJ 的 GitHub API 包装器 到你吐出 GitHub OpenAPI 时默认得到的东西,而且是日日夜夜……而且其规模是无偿志愿者尝试手工包装 GitHub API 时永远无法比拟的。
AJ 在去年的 ElixirConf 上做了一次精彩的演讲,展示了这个东西的力量:
随着时间的推移可维护性
我想提到的最后一件事是 Elixir 生态系统中的流失率非常低。 与我工作过的其他堆栈形成鲜明对比,在这些堆栈中,即使对框架进行“补丁”更新也可能需要专家来安装 小时 令人沮丧的调试(如 加里·伯恩哈特最近哀叹),更新 Elixir 或 Phoenix 并不是什么大问题。 如果您像我一样将警告视为错误,那么您会经常遇到一些弃用等问题,但这些几乎总是很容易解决。 这反映在我最近进行的两项民意调查中。 绝大多数用户使用的是过去一年左右发布的 Elixir 和 Phoenix 版本,只有不到 5% 的用户使用的是 3 年以上的版本。
Elixir 和 Phoenix 重视稳定性,因此通常很容易获得新功能,而不会遇到很多麻烦。
包起来
我没有资格说 Elixir 是世界各地所有应用程序的正确语言选择。 我从未在大公司工作过,我使用 Elixir 的经验主要集中在网络和网络方面。 我 做 不过,我可以放心地针对我现在正在从事的项目评估它,并且为了满足构建 B2B SaaS 的单人开发团队的需求,我没有看到任何其他堆栈能够同时提供入门速度和能够向您的业务发展的任何方向发展。
我很想听听您的任何反馈 – 您可以通过以下方式联系我 推特, 乳齿象,或电子邮件(我在此域中的名字)。
附录:Elixir 对常见 Web 开发要求的回答细分
技术 | 典型的方式 | 我所提倡的 Elixir 方式 |
---|---|---|
超文本标记语言 | 一定要学起来 | 还是得学一下 |
CSS | 一定要学起来 | 抱歉,还是要学习一下 |
顺风 | 可选,但很好 | 可选,但很好 |
前端语言 | JavaScript/TypeScript | 凤凰城直播 |
后端语言 | 红宝石、Python、围棋 | 灵丹妙药 |
一个前端框架 | 反应,Vue | 凤凰城直播 |
前端状态管理框架 | Redux、Jotai、Vuex | 不适用于实时查看 |
一个后端框架 | Rails、Next.js、Django | 凤凰城直播 |
休息 | 客户端与服务器通信所需 | LiveView 不需要 (Phoenix,如果您因产品原因需要) |
GraphQL | 可能需要客户端-服务器通信 | LiveView 不需要 (如果您因产品原因需要苦艾酒) |
SQL 数据库 | Postgres、MySQL、SQLite | Postgres |
NoSQL 数据库 | 蒙戈、CouchDB | Postgres JSONB 列或使用 ETS 进行内存缓存 |
后台作业系统 | Sidekiq、芹菜、BullMQ | 内置 Task 或奥本图书馆 |
内存中的缓存 | 雷迪斯 | ETS,或 ETS 的薄包装,如 Cachex |
服务崩溃恢复系统 | PM2、新贵 | 通过 Supervisor 树进行内置故障恢复 |
一个消息队列 | 兔子MQ、Redis | 凤凰城PubSub |
网络服务器 | Nginx、阿帕奇、Gunicorn | 凤凰 |
集装箱化 | 码头工人 | PaaS,如 Render、Fly.io、Gigalixir 或 Dokku,可对容器(或裸二进制版本部署)进行抽象 |
容器编排 | 库伯内斯 | PaaS 或裸机部署 |
一个云平台 | AWS、GCP、Azure | PaaS 或裸机部署 |
无服务器 | AWS Lambda | 垂直缩放的整体 火焰 如果您确实需要类似无服务器的扩展或在不同硬件上无缝运行功能 |
微服务 | 疼痛 | 具有多个核心的整体架构 边界 如果您需要确保团队之间的关注点分离 |
扩展服务 | 水平的 | 垂直,如果您确实需要冗余或多区域部署,则仅水平 |
“BEAM”是构建 Elixir 的 Erlang 虚拟机的名称,OTP(“开放电信平台”)是一组核心 Erlang 抽象和库,用于进程隔离、网络和分布式计算等。
在 StackOverflow 的开发者调查中,Phoenix 连续两年被评为“最受欢迎的 Web 框架”(2022年, 2023年)。
诚然,这不科学,但 Elixir 民意调查有 200 多名受访者,Phoenix 版本有 100 多名受访者,这似乎是生态系统的合理快照。