❝
哈喽👋,我是树酱。今天,我想向各位介绍一款人工智能工具——Podwise。经过他们两个月的运营,这款工具已经成功实现了每月12,000美元的年回报率(ARR)。对于一个独立开发者推出的海外产品而言,这无疑是一个令人鼓舞的起点。接下来,我将详细说明Podwise的产品是如何构建的。

技术栈
先说最终选型结果,只列比较关键的。
-
Web 前端:NextJS 、TypeScript 、TailwindCSS 、shadcn/ui -
Web 后端:NextJS 、TypeScript 、Prisma -
纯后端服务:Golang -
移动端(计划中):CapacitorJS
虽然在选型过程中也有一些 feature 上的考量,但最核心的原则还是:选择擅长且方便的。
很多人在做一个新项目的时候会倾向于选择新技术。虽然有时候新技术本身能带来一些优势,但因为我们不熟悉这项新技术,或新技术本身不成熟而存在的问题,都会导致我们在这方面付出很多时间成本。而除非新技术带来的优势就是你的产品的竞争力,否则这些优势几乎不可能超过你因为使用新技术而产生的额外成本。
Web 前后端为什么选择 JS/TS 技术栈?
我们团队比较熟悉的技术栈是 JavaScript/TypeScript 、Golang 、Java 、Ruby 、Rust 。
对于开发 Web 应用来说,显然使用 JS/TS 全栈一站式搞定是最快捷方便的。尽管 Java 、Golang 包括 Rust 在性能方面相对 Node 会更有优势,但对于大部分开销都在数据库和网络 I/O 上的 Web 后端来说并没有多大意义。另外 Web 前后端开发由同一个人承担,那使用同样的技术栈肯定也会更加方便。所以 Web 前后端就决定使用 JS/TS 技术栈。
为什么选择 TS 而非开发速度更快的 JS ?
JS 在一人工作以及 POC 阶段效率确实很高,但当工程开始变得复杂且经过长期迭代之后,TS 会更具优势。当工程已经颇具规模之后再切换到 TS 会是一个很痛苦的过程。我一般仅在知道工程规模会长期保持轻量的时候选择使用 JS 。
很多人被 TS 灵活的类型系统搞的无所适从,花费很多时间在类型适配上,或把TypeScript 写成了 AnyScript 。但实际上对于上层应用开发来说,我们大部分时候只需要使用第三方包暴露的类型就够了。同时我们也可以通过一些技巧来提取未被暴露的类型,例如使用 Utility Types Awaited<> Parameters<> ReturnType<> 等。
为什么选择 NextJS 作为 Web framework ?
NextJS 提供了一些吸引人的特性,例如:前后端同构、SEO 友好、ServerAction 、Vercel 快速部署等。
当时我们因为已经脱离一线开发一段时间了,其实对当时主流 Web framework 的细节也并不了解,在快速评估了 NextJS 、Astro 和 Remix 之后,我们认为 NextJS 更加符合我们的需求。尽管这个选择帮助我们快速开发并上线了 Podwise.ai ,但也同样给我们带来了不少麻烦,在后面我们会讨论这些麻烦。
前后端同构在一人开发时效率很高。举例来说,所有接口的出入参类型你都只需要定义一份,而无需在两种不同技术栈下分开定义。当你需要修改接口时,同一份类型也可以确保你同时修改两边而不会发生遗漏。
SEO 作为一种出海场景下有效且相对便宜的营销获客手段,是非常必要的。如果你的 SEO 内容都是 blog 文章,那完全可以为 blog 站点选择一个独立的技术栈,或者干脆使用 medium 这样的服务作为你的 blog 站点。但当你的应用内容本身就需要被 SEO 时,为你的应用开发选择一个支持 SEO 的技术栈就比较有必要。
-
Astro 本身定位自己是用来做 “content-driven websites” ,内容驱动网站,也就是说像营销站点、文档站点、blog 、landing page 这种场景。对于功能比较复杂的应用网站,Astro 官方也并不推荐使用 Astro 来构建。
-
Remix 看起来是一个不错的选择,但它的 action 将一个页面的所有请求混合在一起的写法让我有点不太能接受,而当时 NextJS 的 ServerAction 看起来会更加吸引人。
纯后端服务为什么选择 Golang 技术栈?
深度的 Go 经验和积累
对于做应用层的产品创业,根据自己或团队选择最熟悉,最擅长的编程语言的决定是一定不会出错的。对于独立开发者更是如此。podwise 是一款 AI 播客应用产品,所以我们也选择了积累最深,经验最丰富,使用起来最顺手的 Go 语言开发了 SaaS 的后台服务。这绝对是一个正确的选择,只管专注开发产品功能,解决问题,快速构建自己想要的基础设施,这一切都不需要花任何时间在学习、摸索、解决未知问题上面,只需要将自己大脑中的知识和经验倾注而出就可以完成。(当然,从某种方面来说,这或许有点无聊)
我们根据自己的积累、经验和能力,选择了最保险的方案。我们也极力推荐大家采用这一准则进行编程语言的选择。但你并不一定完全需要遵照这一准则。有很多的领域或者产品类型,你甚至可能需要靠其他的指标和维度来做选择,比如你正在构建的是需要高性能的 infra 产品,那你有可能会选择像 Rust,C/C++ 这类编程语言,哪怕你并不是很擅长它们。
资源
Go 比 Python 可以消耗更少的资源。如果你有 “白嫖” 过 AWS 的免费 EC2,你会发现这些免费的 ec2 只有 1vCPU 和 1G 内存。cpu 其实还好,毕竟作为独立开者新发布的产品,可能也没多少人使用,也没什么流量;但 1g 的内存极有可能会是一个硬伤,1g 内存被操作系统占用后,剩下给到你的也就是只有几百兆了,这远低于我们今天使用的笔记本电脑,像 python、java 这类带虚拟机的编程语言,一启动就可以占用上百M、甚至几百M 的内存,所以 1g 的 “白嫖” ec2 总体来说是捉襟见肘的。
Go 语言在这方面的表现就是非常优秀了,轻松把内存稳定控制在几十M 内,只要我愿意做,控制在十几M 也不是不可以。
资源就是成本,是钱。作为独立开发者或startup,我们可以不在意编程语言的性能,但不能忽视资源消耗的多少问题。特别是刚起步的时候,能够让你的产品轻松的部署在任意环境,节省每一分钱都是有意义的。
部署运维
Go 程序具有极大的可部署运维性。除 Go 以外,其他的所有主流编程语言的程序或多或少都需要解决部署环境的依赖问题,当然在 docker 出现以后,这个问题明显减少了。
大家是否想过,云计算领域诞生了云原生技术,驱动云原生应用开发的云原生技术,为什么大多数都采用了 Go 开发,包括 docker,kubernetes等等。这其中就有 “Go 程序天然具备高可运维性” 的功劳。这种可运维性,给我们节省了大量的琐碎时间,在 Mac 电脑上采用交叉编译成一个二进制文件,然后一键将二进制文件 copy 到任意机器上就可以直接运行,甚至都不需要事先在目标机器上安装任何软件和库。
并发
Go 具有比 Python 强大得多的并发能力,这种强大的并发能力不只是 “cpu上的效率” ,还体现在 “语言级别原生支持 goroutine 的语法表达能力”。这就是编程体验和性能都双双兼得。
并发为什么重要?Podwise 后台服务有太多的地方需要并发处理:
-
podcast 节目同步需要并发 -
节目进行 AI 处理需要并发 -
长 transcript 文稿分片后,需要并发总结 -
等等
AI 模型已经足够慢了,好的并发流程设计可以避免产品陷入 “更慢” 的窘境。所以,值得我们使用更好的并发语言。
开发效率和性能平衡
选择 Go 就选择了开发效率和性能的平衡,较高的运行时性能和非常不错的开发效率都兼顾了。但这个理由对独立开发者的一般应用来讲,我觉得不是特别的强烈和重要
-
再好的开发效率,也抵不过自己真正擅长的编程语言。几年前,在阿里云 cdn 团队的时候,要开发一个面对大流量的安全防御系统,一部分人主张用 Go,但另外有个别人主张用 C ,主张用 C 的同学在平时开发中已经用事实证明他用 C 的效率同样很高。 -
一般的独立开发者产品,很难有大流量的机会要靠编程语言的性能来顶,一般来说优化/调整一下逻辑就足够好使。
❝
我把这段话写到这里的目的,是为了更好的告诉开发者们,一般情况不用痴迷于编程语言,选择自己最擅长的,真正能拿捏的语言就是最好的选择。
当然,我们应该追求个人工具箱里有多门擅长的,真正掌控的语言和工具栈,这能让你在构建产品的过程中做到真正的随心所欲。虽然我们没有选择用 Python 作为 SaaS 后台服务的核心语言,但构建 AI 产品的过程中,我们还是会碰到 Python,多语言本就是产品开发过程中的常态。
其它选型:
-
TailwindCSS 的就地编写样式的方式带来了非常高效的开发体验,免除了在样式文件和 TSX 文件之间反复切换并查找彼此关系的过程。选择 TailwindCSS 并不代表你就放弃了对样式的抽象和复用,你仍然可以命名并复用你的样式。但我的经验是仅在真的有必要时才这么做。尽管可能你的强迫症会让你不太愿意接受到处散落的 class name ,但一旦接受之后你会真正感受到效率提升带来的愉悦。
-
shadcn/ui 是一个优秀的 React UI 组件库,样式简洁美观,组件可以按需安装和更新。更重要的是它基于 TailwindCSS 编写样式,并通过复制的方式安装到你的工程中。因此我们可以很轻松的使用 TailwindCSS 来覆盖样式,或直接修改代码来改变它的默认样式和行为。除了 shadcn/ui 之外,radix-ui themes 和 NextUI 也是不错的组件库选择。
这个技术选型下遇到的问题
最大的问题来自对 NextJS 和 CapacitorJS 的不熟悉。原本我们计划使用 CapacitorJS 来包壳开发 App ,避免重复同样的业务逻辑开发。对于小团队来说,在多端用不同语言重复实现的时间成本有点不可接受,同时也是非常不利于体验一致和长期维护的。
但 CapacitorJS 需要被包壳的 Web 应用是一个标准的 SPA(Single Page Application),而 NextJS 是一个对 RSC(React Server Component)和 SSR(Server Side Render)高度优化的框架。
尽管 NextJS 也可以用于开发 SPA 应用,但很多 NextJS 的有用特性就无法使用了。
我们在最初并没有关注到这个限制,结果直到开始准备包壳的时候才发现。这直接导致我们需要对整个 Podwise 的 website 应用进行改造和重构,才能适用 CapacitorJS 来包壳。
与此同时,我们还要确保 web 端的 SEO 内容可以被继续输出。目前我们计划通过自定义打包脚本,在尽可能复用代码的同时保留一定的差异化。
一个和 App 包壳相关联的问题是 NextJS 提供的 ServerAction 特性。ServerAction 可以让开发者以类型安全的方式,像调用本地方法一样从 browser 端调用 server 端的接口。ServerAction 使用起来非常方便,然而这也同样是一柄双刃剑。ServerAction 本身不被 NextJS 的静态导出方式(导出成 SPA)所支持,同时也不是一个标准的 RestAPI ,无法被其它二方或三方调用。我们最初用 ServerAction 用的多爽,当决定弃用它的时候就有多麻烦。
Web 启动是比较轻量的,也便于试错。但如果你在后面的计划中有 App 的需求,那么我会非常建议在动手 Web 端编码之前先预研一下后面的 App 方案。
此外 Vercel 作为我们的主要部署平台,也给我们带来了一些麻烦。像 Vercel 以及 Netlify 这些类似平台,都使用 AWS lambda 作为他们 Serverless 能力的底层设施。AWS lambda 在安装它的基础镜像不包含的依赖库时需要使用自定义 layers 来完成,而 Vercel 和 Netlify 等都不支持自定义 layers 。
这就导致当我们开发的功能需要使用到一些 AWS lambda 基础镜像中并没有预先安装的依赖库时,无法被成功部署到 Vercel 或 Netlify 中。例如我们的 DAI(Dynamic ADs Insertion)检测功能需要使用到 libasound.so.2 而 AWS lambda 基础镜像中没有,我们尝试了很多种方式都没有成功完成部署,包括 Vercel 的官方 support 给出的结论也是需要等待他们支持 layers 才可以。最终我们只能把这部分功能剥离出来单独部署,我们选择了 zeabur 来部署这一小部分。
技术选型建议
-
选择你擅长的,而不是选择最新的 -
在 PMF(Product Market Fit)之前,可以只关注开发效率;在 PMF 之后,应当考虑长期的可维护性 -
善用基础设施 SaaS 服务,尽量只关注开发本身 -
不要在技术服务上花过多的成本,能省一分是一分
文章转自:https://book.hardhacker.com/build/buildstack
原文始发于微信公众号(前端那些趣事):AI工具:两个月 $12000 ARR 技术实践之路
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/283225.html