メインコンテンツへスキップ
友田 陽大
近日公開

出荷後に見つけたバグを、コードに残してある

オフライン復習キューの冪等化、API Gatewayの30秒を跨がない非同期パイプライン、特許FTOをコードの制約にする。MemoryHack AI の実装記録

撮影するだけでAIが一問一答クイズを生成する暗記アプリ「メモハック」

近日公開です。App Store にはまだ掲載されていません。

なぜポートフォリオにあるか

サーバーレスは安く始められますが、「30秒で切れる」「再試行が二重処理になる」「オフラインで壊れる」の3つで必ず詰まります。MemoryHack ではその3つを先に解き、出荷後に見つけた不具合の記録もコードに残してあります。

なぜ作ったか

資格試験の勉強は、机に向かえる時間が読めない人ほど続きません。参考書を撮るだけで問題に変わり、復習日が勝手に決まるなら、通勤中の数分でも前に進みます。

技術的には、画像を扱う生成AI・間隔反復アルゴリズム・オフライン対応・広告収益という、詰まりどころの違う要素が1つのアプリに同居する題材として選びました。

いちばん難しかったこと

3つあります。

  1. API Gateway の30秒。 画像から問題を生成する処理は、これを平気で超えます。
  2. オフラインの復習をどう同期するか。 電波が切れた電車の中でも復習は進みます。復帰したときに二重に記録されてはいけない。
  3. 間隔反復アルゴリズムの法務。 この領域には特許があります。実装の仕方によっては踏みます。

そして、2番目については出荷後にバグを出しました。

どう決めたか(と、捨てたもの)

30秒問題は、同期処理をやめることで解きました。端末から署名付きURLでS3に直接アップロードし、ジョブを登録して即座に応答を返し、端末が結果をポーリングします。

オフラインの復習には、タップした瞬間に端末側でIDを発行し、それを冪等キーとして毎回の再送に付ける方式を採りました。

エレベーターのボタンを何回連打しても、来るのは1台だけ。同じ操作を何度受け取っても、結果は1回分にする仕組みです。

アルゴリズムは SM-2 と FSRS-5 の両方を論文どおりに実装し、特許の自由実施(FTO)分析で特定した制約をコードの docstring に書き込みました — ユーザーデータでのサーバー側再学習をしない、科目別の目標保持率カーブを持たない、試験得点を予測しない。

捨てたものはアルゴリズムの改良余地です。ユーザーデータで重みを学習すれば精度は上げられますが、それは意図的にやっていません。

実際に動いているもの

**そして、ここが本題です。**オフラインキューには、出荷後に見つけたバグの記録がコード内に残してあります。

APIクライアントが、あらゆるネットワーク失敗とタイムアウトを同じ1つのエラーに畳んでいました。その結果、**オフラインの復習は再送のたびに「失敗」として再試行カウンタを増やし、上限に達すると静かに削除されていました。**アプリを開いたまま2分半ほど圏外にいるだけで、復習の記録が消え、間隔反復のスケジュールが壊れます。

修正は、恒久的な失敗(400 / 401 / 403 / 404 / 409 / 422)とそれ以外を分け、5xxとネットワーク断はすべて一時的として扱うことでした。直したことより、なぜ壊れたかをコードに残すほうが、次の案件では価値があります。

広告のリワードは、Google の SSV コールバックを ECDSA P-256 で検証します。失敗処理は意図的に非対称です。署名が不正なら200を返し(保証された不正リクエストをGoogleに再送させない)、公開鍵の取得に失敗したら503を返します(一時的な障害なので再送させ、本当に広告を見た人の報酬を落とさない)。

コードから数えられる値

2026-02-16 から 07-29 までの122コミット。5本の中で最も長く手を入れているプロダクトで、その履歴にはApp Storeの審査却下への対応も含まれます。

インフラは Terraform 4,329行。Lambda 8本、DynamoDB 2テーブル、Cognito、CloudFront 3ディストリビューション、EventBridge のスケジュール5本、SQS 5本、CloudWatch アラーム17本を管理しています。加えて月次の予算アラートを実績80% / 予測100%で設定しています。APIは OpenAPI 上で58パス。

テストは Python 255ファイル・TypeScript 107ファイル。backend / admin_backend / sns_pipeline の3パッケージで、ブランチカバレッジ100%を CI の失格ラインとして設定しています。

生成コストは設定ファイルにコメントとして計算根拠ごと書いてあり、上限を上げるときに再計算しないと赤字になる旨が明記されています。

これは、あなたの案件の何を証明するか

サーバーレスの3つの詰まりどころ(30秒の天井・再試行の二重処理・オフラインの破損)を、非同期ジョブへの分離・冪等キー・失敗種別の切り分けとして解いた実例です。

そして、Terraform 4,329行と17本のアラート、月次のコストアラートまで含めて、引き渡した後に他の人が運用できる形で構築するのが前提です。広告収益(リワード広告のサーバー側検証)まで必要な案件にも対応できます。

法務側の制約をコードの禁止事項として符号化する進め方も、規制のある領域では有効です。

AWSサーバーレスの構成は、高速MVP・サーバーレス開発としてお請けしています。音声AIを本番運用した受託事例もあわせてご覧ください。

コードから数えられる値

Lambda 関数
8本API・保持ジョブ・削除再開・プッシュ配信・ジョブ期限切れ・集計・管理API
Terraform
4,329行DynamoDB・Cognito・CloudFront・EventBridge・SQS・予算アラートまでコードで管理
CloudWatch アラーム
17本月次の予算アラート(実績80% / 予測100%)と併用
Python テスト
255ファイル3つのパッケージでブランチカバレッジ100%を強制

よくある質問

メモハックは App Store で配信されていますか?
いいえ。現在は近日公開の段階で、まだ App Store には掲載されていません。
復習スケジュールのアルゴリズムは何ですか?
SM-2 と FSRS-5 の両方を論文から実装しており、アプリ内で切り替えられます。
電波がない場所でも復習できますか?
できます。オフライン優先の復習キューを持ち、タップした瞬間に発行したIDを冪等キーとして同期するため、同じ復習が二重に記録されることもありません。
学習データがAIの再学習に使われることはありますか?
ありません。ユーザーデータを用いたサーバー側の再学習を行わないこと、科目別の目標保持率カーブを持たないこと、試験得点の予測を行わないことを、特許の自由実施分析に基づく制約としてコードに明記しています。
写真からクイズを作る処理はどう動きますか?
端末からS3へ署名付きURLで直接アップロードし、ジョブを登録して即座に応答を返し、端末が結果をポーリングします。API Gateway の30秒上限を跨がない構成です。
インフラはどう管理していますか?
Terraform 4,329行でコード管理しています。Lambda 8本、DynamoDB 2テーブル、Cognito、CloudFront 3ディストリビューション、CloudWatch アラーム17本に加え、月次の予算アラートを実績80% / 予測100%で設定しています。

同様の課題、抱えていませんか?

あなたのビジネス課題も、最新の技術で解決できます。 まずは30分の無料技術相談から、状況をお聞かせください。

自社の課題もSaaS化できるか相談する

プロジェクト単位(請負)・技術顧問、どちらにも対応可能です

自社プロダクト一覧を見る