问题及其起源
Spooki 的想法并非诞生于白板前,而是在高中时期与朋友们的聚会之夜。每一次都会出现相同的冷场:我们在找该看什么,打开三个片库,读了一堆剧情简介,结果连片头都还没开始,兴致就已经消退了。尤其是,我们真正想问的那个问题,从来不是各大平台所回答的那个。我们不只是想知道该看哪部恐怖片,而是想知道哪一部真的吓人,以及它带来的是哪一种恐惧。这个差别很关键。一个十分制的评分说不出一部电影是否吓人,也说不出它怎么吓人。这正是 Spooki 要回答的问题。
这份直觉在地铁上逐渐清晰,然后在写下第一行代码之前,早已在脑海中变成了一份需求说明书。那时我的技术底子很薄:一些在 Discord 机器人和自动化脚本上学到的 JavaScript,以及 HTML。一款完整的移动应用,带有后端、数据库并发布到应用商店,显然遥不可及。我之所以着手去做,恰恰是因为它很难。这既不是一个学校项目,也不是一份委托,而是我因为它能让我进步而主动选择的一片试炼场。
没有什么方法论。技术是边做边学,需要什么就学什么。对我来说,界面一直和滚动的流畅度、渲染的质量同样重要。
赌注与社区
最初的愿景不止于一款应用。我设想的是把一个发现工具和一个影迷俱乐部结合起来,一个恐怖片爱好者彼此推荐电影的社区。社区这一维度并不是后来添加的,它从一开始就构建了整个项目:价值不仅来自算法,也来自那些拥有相同品味、彼此信任、会指出什么真正吓人的人。
为了同时给开发和影迷俱乐部筹资,我在 Ulule 上发起了一场众筹,于 2023 年 12 月 11 日结束。结果:在 3 000 欧元的目标下筹得 474 欧元,共 23 笔捐助。因此众筹并未达标,也没有收到任何资金。展示给捐助者的界面对应的是当时的 React Native MVP,这让承诺变得具体,但仍不足以在目标规模上说服人。
我对这次失败的回应,定义了项目的后续走向:无论如何都要把 Spooki 构建并交付出来,独自一人、自筹资金,一直到发布到两大应用商店。这种资源上的约束强加了简朴的选择和很少的付费依赖,这种纪律长久地塑造了架构。而社区这一维度并没有消失:它在产品中以一种由社区供给的恐惧评分来做推荐的形式存活了下来。
从 React Native 转向 Flutter 的转折
我从 React Native 起步,对一个 JavaScript 背景的人来说很自然,很快就有了第一个能用的 MVP。它验证了概念:发现流程站得住脚,这个问题值得做成一款真正的应用。
我是个完美主义者,我想要一款和原生应用一样流畅的应用。我会注意到细微的卡顿、不顺畅的转场、滚动不够完美的列表,正是这些细节决定了用户是留下一款应用还是删掉它。在 Android 上的真实使用中,滚动和动画没有达到这个水准。iOS 和 Android 之间的差别可以说是天壤之别。我当时的经验大概还不足以做好优化,但用 React Native 在 Android 上做出流畅的效果本身就非常难。
于是我做了一个短期代价高昂、长期回报丰厚的决定:把整个前端用 Flutter 重写,它的引擎自己绘制界面,从而提供像素级的控制以及 iOS 和 Android 之间的一致性。这个权衡是明确的:接受一条真实的学习曲线和现有 MVP 的舍弃,换取高得多的质量上限和一个能由一个人维护的单一代码库。与此同时,我在后端采用了 TypeScript,把同样的严谨要求从客户端一直贯彻到服务器。
全栈架构
整条链路都由一个人把控,从移动客户端一直到基础设施。前端用 Flutter 和 Dart 编写。后端基于 Node.js、TypeScript 和 Express,以 Prisma 作为 ORM,之上是一个 MySQL 数据库。
在基础设施方面,服务运行在 Docker 之下以保证环境之间的可复现性,Nginx 承担反向代理的角色,Cloudflare 位于最前端。应用还集成了众多外部 API,这在调用的韧性和聚合数据的一致性上需要特别的用心。整体刻意保持经典且可由一个人掌控,这在这里是一个工程目标,而不是对简单的认输。
工程化与持续交付
交付是自动化的,这在个人项目上很少见,却是一个速度倍增器。GitHub Actions 会自动把内部测试版发布到 iOS 的 TestFlight 和 Android 的 Play Store。具体来说,一次经过验证的改动会触发向测试者的分发,无需人工介入,这消除了一个经典的错误与迟缓来源,并让人能够紧接着在真实设备上测试一个修复。
除了部署机制本身,即便是一个人工作,我也保持了专业的 Git 组织方式。我使用 GitHub issues 来追踪工作,用 pull requests 来合并改动,尽管一次简单地向主分支推送就足够了。这个选择是刻意的:它强制了可追溯性,迫使人为每次改动阐明意图,并创建了一段我可以重读的清晰历史,这在停顿之后重拾项目时非常宝贵。
这种纪律刻意再现了团队的工作条件,以便日后过渡到协作环境时无需改变任何习惯。相比产品本身,它更是我工作方式的一个具体证明。
产品埋点与度量
我为 Spooki 做了端到端的埋点,并为自己搭建了一个自建的分析看板,命名为 SPOOKI Analytics V4,运行在 Grafana 上。它由我自己的后端指标供给,这些指标从 Node.js 和 MySQL 中暴露,可观测性由 Prometheus 和 Loki 保证。目标不是收集一堆曲线,而是能够回答关于产品的精确问题:谁在用它、怎么用,以及核心功能是否很好地回应了最初的需求。我因此度量获取(每日和累计注册数)、参与度、留存、按平台、按语言和按应用版本的分布,尤其还有对恐惧强度的偏好。
截至 2026 年 9 月,看板显示 1 397 名用户、107 075 次滑动、4 639 部已观看电影、3 026 条评分,以及目录中 19 898 部活跃电影。注册在加速:2025 年底每月约三十人,2026 年 7 月 123 人,8 月 130 人。受众以法语用户为主且遍及多国,大多在法国,也有科特迪瓦、喀麦隆、加拿大、加蓬、刚果、美国和英国。
最有意思的一点闭合了高中时开启的那个循环:偏好的分布显示约 57% 的用户在寻求最高的恐惧强度。因此数据验证了创立这个项目的那个需求。我坚持对这一切的范围保持诚实:我所主张的价值不是受众规模,它仍然不大,而是度量的能力以及由此而来的数据驱动文化。产品还年轻且由一人推进,我不夸大它的势头,也不去凸显脆弱的指标。
工程挑战
第一个挑战是学 Flutter。我花了好几个月才上手:它构建界面的方式和我熟悉的 JavaScript 完全不同。
身份认证构成了另一块重要的部分。我从未实现过它,这是一次真正的后端学习,伴随着安全、会话管理和边界情况的种种问题。同样地,设计一个稳固且可演进的数据库 schema 需要超越眼前的需求来思考,以免几个月后被自己的选择所困住。健壮性和稳定性一直是贯穿始终的主线:处处处理错误情况,同时兼顾用户、管理员、未来的可演进性以及前端的各种状态。
让这项挑战可以承受的,是我在后端和前端上获得的乐趣同样多。驱动我的不是某一特定层,而是结果:探究、理解,然后完成。正是这种动力,让人能够在需要独自兼顾一切时坚持到底。
成果与证据
Spooki 由个人独立设计并交付,从原型一直到发布在两大应用商店。这是我首先记住的一点,因为它概括了项目的本质:独自一人走完一条从想法到安装在移动端的完整链路。应用在 App Store 上显示 5,0 分(满分 10 分),基于 10 条评价,我特意注明数量,以免让人误以为它具有它并不具备的代表性。
截至 2026 年 9 月,后端记录了 1 397 名用户,这个数字直接来自我自己的看板。商标已在 INPI 注册,业务由一家个体微型企业承载。Spooki 还于 2024 年出现在 France 2 的 Télématin 节目中。
这个项目说明的,比它的数字更重要:我能独自设计、交付并维护一款线上运行的应用,即便没有融资。