ひとりでBookverseを作り続けた半年間

ひとりでソフトウェアを作ることのロマンチックな版というものがある。きれいな デスク、エスプレッソ、そして昼までに人々が愛するものをリリースする、という もの。だが私が過去半年間に実際に生きてきた版は、むしろ 「七つ目のエッジ ケースに今しがた噛みつかれたせいで、同じNginx設定を四回書き直す」 に近い。

これは振り返りだ — リリースしたもののチェックリストでも、チュートリアルでも ない。ただ半年を過ごしたあとのメモにすぎない。

なぜ始めたのか

私は何年もの間、言語アプリを断続的に使ってきて、ある具体的なことに気づいた。 人気のゲーミフィケーション・アプリで30日連続記録を達成できても、いまだに ネイティブの文章を一段落読めなかったのだ。フィードバックループは緊密で、 ゲーミフィケーションは滑らかだったが、進捗の単位が 一文、ときには 一語 だった。そして言語を読むこと — 本当に読むこと — はページ単位で起こる。

私は、教科書を教科書として扱うアプリが欲しかった。第一章を開く。読む。 聴く。声に出して言い返す。残ったものを学ぶ。明日は第二章。

プロダクトの形は初日から明確だった。落とし穴は、「初日から形が明確」が ソフトウェアを作ることの5%くらいしか担っていないということだ。残りの95%は、 華やかさのない中盤だ。

華やかさのない中盤

今四半期に私がやったことの、どのリリースノートにも載っていない不完全な リスト:

  • モノレポのパスツリー全体を app_v54_01 から bookverse に改名した (そしてsystemdユニット名にアンダースコアを入れたくなって、もう一度改名 した)。
  • Nginx vhost用のTerraform設定を四つの版で書いた末に、null_resource の トリガーが入力変数だけでなく、レンダリング済みテンプレートのハッシュも 含める必要があることを発見した。
  • EXPO_PUBLIC_* の環境変数値が、iOSでは問題なく動くのにwebバンドルでは なぜ空文字列にresolveされるのかを突き止めるのに一週間費やした。(Babelの preset-exponode_modules に対するインライン化をスキップする。 公開パッケージでは環境変数ではなくファクトリ関数を使うこと。)
  • API URLヘルパーの呼び出し箇所17か所をリファクタした。最初に中央集約の 仕方を間違えていたからだ。

このどれも、アプリに関するマーケティング投稿には載らないだろう。だが、その すべてが実際の仕事だった。

ひとり開発について誰も警告してくれないのは、退屈な半分を引き受けてくれる人が いないということだ。「プラットフォームチームがデプロイを処理する」とは言え ない。なぜなら あなたがプラットフォームチームだから だ。あなたはすべての 帽子を下手にかぶる — 上手にかぶれるほど長くかぶり続けるまでは。

音声認識という底なし沼

過去半年で最も多くの学びをくれた出来事は、中国のスマホでスピーキング練習を 動かすことだった。

計画はこうだ: 一行をタップし、ネイティブスピーカーがそれを言うのを聞き、もう 一度タップして自分を録音し、どれだけ近かったかを見る。標準的なモバイル音声 認識でいい — iOSでは expo-speech-recognition、Androidではシステム サービス、これで終わり。

現実は: ターゲット層のかなりの部分が、中国地域向けOS版(gmsconfig.china オーバーレイ)のLenovo Motorola端末を使っていて、これはGoogle Mobile Servicesを丸ごと取り除いてしまう。システムの音声テキスト変換がない。アプリ はエミュレータでは完璧に動いた。北京のMoto XT2507では、静かにクラッシュ した。

修正には約三週間かかった:

  1. アプリがプラットフォームごとに音声バックエンドを差し替えられるよう、 アダプタパターンを構築した — 利用可能なところではシステム認識、フォール バックとしてクラウド書き起こし。
  2. Whisper風のクラウドバックエンドを追加した(DashScope経由のAlibabaの qwen3-asr-flash。中国地域の端末はVPNなしで到達できるから)。
  3. 話し終えたら録音が自動で止まるよう、音声区間検出を調整した。(-25 dBFSの しきい値、600msの無音ウィンドウ、五サンプルにわたって平滑化 — ほかの どの組み合わせも、単語の途中で切るか、まったく止まらないかのどちらか だった。)
  4. React Nativeの FormData アップロードから expo-file-systemuploadAsync に切り替えた。同じ端末でRNの FormData が音声blobに対して 不安定だったからだ。

この四つの箇条書きの半分は、私が普通の一週間で書くより多くのコードだ。 どれも「機能」ではない。だが、そのすべてが、それを必要とする人々のために その機能が存在するために必要だった。

教訓は — およそ二週間ごとに繰り返されるのだが — 難しい部分が、難しいだろう と思っていた部分であることはめったにない ということだ。私は音声認識に一日を 見積もった。二十日かかった。

やり切る規律

ひとりで作るときの誘惑は、新しくて輝くものを追いかけることだ。新機能は わくわくする。既存フローへの47回目の手直しはそうではない。だからあなたは 機能を積み上げていくが、そのどれも完全には仕上がっておらず、プロダクトは 半分だけ作られたものの墓場のように感じられる。

私が繰り返し学び直す規律: 次に取りかかる前に、ひとつをきちんと仕上げろ。 まずマンダリン。マンダリンを本当に良くしろ。それから韓国語。それから英語。 このプラットフォームは構造化されたコースを届けるために作られている — だが 最初の コースは目玉となるもの、学習者が実際にやり切れるものでなければ ならない。

5%しか作られていない、計画段階の韓国語コースは誰の役にも立たない。誰かが 最初から最後まで読み通せる、完成したマンダリンコースこそ、本物のプロダクト だ。

機能も同じだ。スピーキング練習はリリースまで三か月間ロードマップにあった。 遅れてリリースされたが、正しく リリースされた — 中国地域のケースに対処 することは、それが使えるものになるための耐荷重要素であって、後回しにできる 仕上げのディテールではなかった。

十一月の自分に伝えたいこと

三つ。この件を始めた自分の版に絵はがきを送れるなら。

名前を変えるのをやめろ。 名前は初日に間違えても構わないものだ。改名は たいていの場合、その価値より高くつく。(私は今までこれをちょうど四回 やった。)

構造化された本を買え。 私はフォーラムの語彙リストの間を行ったり来たり して何週間も無駄にした。HSK 3.0 の標準は、実際に 出版された文書だ。構造化された本を買ってそれに従うほうが、それらが何を カバーしているかをリバースエンジニアリングするより速い。

読者は辛抱強い。 私は、速くリリースしなければ人々が興味を失うとずっと 思っていた。真実は正反対だ: 言語学習者は定義からして辛抱強い — 彼らはすでに 一年の練習にコミットしている。彼らは三日目にデモを必要としない。三百日目に 動くものを必要としているのだ。

次は何か

マンダリンは成長し続けている — Band 1とBand 2は稼働中で、Band 3は今まさに 監査中だ。韓国語コースのコンテンツ制作は、マンダリンのBand 3がリリースされた あとに始まる。プラットフォーム側は半年前より落ち着いていて、つまり実際の 学習コンテンツにより多くの時間が注がれる — それこそが時間の注がれるべき 場所だ。

Bookverseを使ってくれているなら: ありがとう。まだなら: いつか 一章を開いてみて。最初の一章は無料だ。

← すべての記事