Skip to main content
友田 陽大
Coming soon

The bug I shipped is still in the code

Making an offline review queue idempotent, keeping an async pipeline under API Gateway's 30 seconds, and encoding a patent freedom-to-operate analysis as constraints in code. How MemoryHack AI is built.

MemoryHack AI — photograph a page and AI generates flashcards

Coming soon; not yet listed on the App Store. Japanese-language product.

Why this is in a consulting portfolio

Serverless is cheap to start and then jams on exactly three things: the 30-second ceiling, retries turning into double processing, and breaking offline. MemoryHack solves all three up front.

Why I built it

Studying for a certification falls apart fastest for people who can't predict when they'll be at a desk. If photographing a page turns it into questions and the review dates schedule themselves, a few minutes on a commute still moves you forward.

Technically, I chose it because vision-capable generative AI, a spaced-repetition algorithm, offline support and ad revenue all live in one app while jamming in completely different places.

The hard part

Three things.

  1. API Gateway's 30 seconds. Turning an image into questions blows straight past it.
  2. Synchronising offline reviews. Reviews continue on a train with no signal, and must not be recorded twice on reconnect.
  3. The legal position of spaced-repetition algorithms. There are patents here, and some implementations walk into them.

And on the second one, I shipped a bug.

The call I made (and what I gave up)

The 30-second problem was solved by not being synchronous: upload straight to S3 with a presigned URL, register a job, respond immediately, and let the device poll for the result.

For offline reviews, an id minted on the device the instant the user taps travels as an idempotency key on every retry.

Press the elevator button ten times and one elevator arrives. However many times the same action is received, the result is one action's worth.

Both SM-2 and FSRS-5 are implemented as published, with the constraints identified by a patent freedom-to-operate analysis written into the code's docstrings — no server-side retraining on user data, no subject-specific target-retention curves, no exam-score prediction.

What I gave up was headroom to improve the algorithm. Learning weights from user data would raise accuracy; that is deliberately not done.

What actually ships

And here is the real point. The offline queue still carries the record of a bug found after shipping.

The API client was collapsing every network failure and every timeout into one identical error. As a result, offline reviews incremented the retry counter on each flush and were silently deleted once they hit the limit. Roughly two and a half minutes offline with the app open was enough to lose review records and corrupt the spaced-repetition schedule.

The fix was to separate permanent rejections (400 / 401 / 403 / 404 / 409 / 422) from everything else, treating all 5xx and network drops as transient. Leaving behind why it broke is worth more on the next project than the fix itself.

Ad rewards verify Google's SSV callback with ECDSA P-256, and the failure handling is deliberately asymmetric. An invalid signature returns 200 — Google should not retry a request that is guaranteed bad — while a failure to fetch the public key returns 503, because that is transient and a genuine ad view must not lose its reward.

Numbers counted from the code

122 commits between 2026-02-16 and 07-29 — the longest-running of the five, with a history that includes responding to App Store review rejections.

Infrastructure is 4,329 lines of Terraform, managing 8 Lambda functions, 2 DynamoDB tables, Cognito, 3 CloudFront distributions, 5 EventBridge schedules, 5 SQS queues and 17 CloudWatch alarms, plus monthly budget alerts at 80% actual and 100% forecast. The API surface is 58 paths in OpenAPI.

Tests are 255 Python files and 107 TypeScript files, with 100% branch coverage set as the CI failure line across three packages.

Generation cost is written into the config with its arithmetic as a comment, noting explicitly that raising the limits without redoing that arithmetic can make a heavy subscriber unprofitable.

What this proves for your project

A worked example of the three places serverless jams — the 30-second ceiling, retries double-processing, and offline corruption — solved as an async job split, idempotency keys, and separating permanent from transient failures.

And with 4,329 lines of Terraform, 17 alarms and monthly cost alerts, the premise is building it in a form someone else can operate after handover. Projects that also need ad revenue, including server-side reward verification, are in scope.

Encoding legal constraints as prohibitions in code is an approach that works well in regulated domains.

AWS serverless architecture falls under rapid MVP / serverless development. See also the client engagement running voice AI in production.

Numbers counted from the code

Lambda functions
8functionsAPI, retention job, deletion resumer, push dispatch, job expiry, rollups and the admin API
Terraform
4,329linesManaging DynamoDB, Cognito, CloudFront, EventBridge, SQS and budget alerts as code
CloudWatch alarms
17alarmsAlongside monthly budget alerts at 80% actual and 100% forecast
Python tests
255files100% branch coverage enforced across three packages

FAQ

Is MemoryHack on the App Store?
No. It is at the coming-soon stage and is not yet listed.
What scheduling algorithm does it use?
Both SM-2 and FSRS-5 are implemented from the published papers, and can be switched in the app.
Can I review with no signal?
Yes. An offline-first review queue syncs using an id minted the moment you tap as an idempotency key, so the same review is never recorded twice.
Is my study data used to retrain AI?
No. No server-side retraining on user data, no subject-specific target-retention curves, and no exam-score prediction — stated in code as constraints derived from a patent freedom-to-operate analysis.
How does turning a photo into a quiz work?
The device uploads straight to S3 with a presigned URL, a job is registered, a response returns immediately, and the device polls for the result — never crossing API Gateway's 30-second ceiling.
How is the infrastructure managed?
As 4,329 lines of Terraform, covering 8 Lambda functions, 2 DynamoDB tables, Cognito, 3 CloudFront distributions and 17 CloudWatch alarms, plus monthly budget alerts at 80% actual and 100% forecast.

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

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

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

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

View all indie products