An Indie Developer's Painful Post-Mortem: Why I Abandoned Mobile SaaS

Indie dev's PhotoLang post-mortem: mobile-first squeezed by platform cuts, policy risk, and vendor lock-in.
An indie developer spent 12 months building the language-learning app PhotoLang before declaring failure, attributing it to the "mobile-first" path. He reviews costs, control, and architecture: the 30% Google Play and App Store cuts, publishing and annual fees, and harder B2C conversions all compress margins; platform algorithm and policy changes can zero out the app overnight, like building a house on rented land. Technically, the Firebase login, Durable Object credit ledger, and Gemini plus Google TTS stack is clean but leads to deep vendor lock-in, and he suggests Hono and Serverless Redis for portability. He concludes that AI SaaS moats are hard to build and token costs are high, but AI-assisted coding is valuable for those with engineering foundations, offering three lessons: don't prioritize mobile, value portability, and use AI by scenario.
An indie developer spent 12 months building a language-learning app called PhotoLang, and it ultimately failed. His conclusion is blunt: if he could start over, he would never choose mobile-first again. This isn't an emotional rant — it's a technical post-mortem grounded in real architectural decisions and cost accounting.
Two Features Forced Me Into Mobile
Choosing mobile wasn't blind trend-following — it was forced by two core features: camera access and direct Anki integration. Anki is the most commonly used flashcard tool among language learners, and PhotoLang wanted users to sync cards directly into Anki to reduce the friction of manual transfer. A standard web app simply can't achieve this kind of local integration.
The requirement itself was reasonable, but the implementation path cost this developer far more than expected. He broke down the systemic disadvantages of mobile for indie developers across five dimensions.
Why can't a standard web app connect directly to Anki?
Anki's core capabilities are built on the local file system and local processes: the desktop client reads and writes .apkg/.colpkg collections through Python add-ons, and can expose an HTTP interface listening only on 127.0.0.1 via the AnkiConnect plugin. Browsers are constrained by the same-origin policy and sandboxing — they can't freely access local ports or read and write the local file system, so a pure web page can't automatically handle card import/export.
There are usually three ways around this limitation: have users manually download and then import, wrap local capabilities in a desktop client (such as Electron/Tauri), or switch to AnkiWeb's official sync protocol. The first offers the worst experience, the second requires handling account credentials and sync conflicts, and the middle path means you still have to ship an installable desktop program. Understanding this is what makes sense of the author's claim of being "forced into mobile by features" — the root cause is the lack of local integration capability, not any inherent product advantage of mobile.
The Triple Squeeze: Platform Cuts, Fixed Costs, and User Composition
The most direct impact comes from platform cuts. Google Play and the App Store take a 30% cut on every transaction. Although Google Play reduces this to 15% for the first $1 million in revenue, that still severely squeezes margins for a SaaS with ongoing server costs. By comparison, Stripe or Shopify charge only about 3% — that 27-point gap means pricing competitiveness is drastically undermined.
Fixed costs are equally hard to ignore. Google Play charges a one-time $25 publishing fee, plus another $25 for an organization account; an Apple Developer account costs $99 per year. Stripe has no upfront fees.
User composition is the third squeeze. Mobile users are mostly B2C consumers whose decisions rely on emotion and impulse, making conversions harder. B2B clients are harder to reach but focus on ROI — as long as you can prove value, they'll almost certainly buy. The developer admits he realized only in hindsight that he actually prefers the B2B market.
Additionally, Google Play individual seller accounts publicly display the developer's address; if you want to hide it, you have to register a company and use a business address.
The Real Risk: Building a House on Rented Land
More fundamental than cost is the issue of control. The "Law of Control" from The Millionaire Fastlane is on full display here: choosing mobile means choosing to survive under Google's and Apple's rules.

If the platform tweaks its search algorithm, your traffic can drop to zero overnight; if it changes its policy terms, your app can be taken down outright. The developer calls this "building a house on rented land" — the foundation isn't yours and can be reclaimed at any time.
PhotoLang's Technical Architecture: Elegant but Locked In
The app's technical implementation itself is quite clear. Users sign in with Google via Firebase; a Worker verifies identity and then requests a Durable Object. A Durable Object is a special type of Worker provided by Cloudflare with single-threaded guarantees, well suited to calculations like credit increments/decrements that require strong consistency. The Worker then calls Gemini and Google TTS to generate content, stores the output in an R2 bucket, and returns it as JSON.
The payment flow uses Google Play one-time purchases: the client obtains a purchase token, the Worker calls Google's purchase API to verify it, and upon confirmation updates the Durable Object balance. Refunds are notified via Webhook and routed to the Durable Object for recording. The credit system uses an append-only ledger rather than simple integer storage.

The intent behind this design: if you only store credits as integers, problems become a black box — you can't trace whether it was a failed purchase or a missed refund. The ledger records the ins and outs of every change, making it possible to assert the correctness of the balance when refunds or purchases go wrong.
But the cost of Durable Objects is deep vendor lock-in. The developer advises: if you use them, make sure to use SQL storage rather than KV-compatible methods, because KV mode can't run queries from the dashboard.
The difference between an append-only ledger and a traditional balance field
An append-only ledger means not maintaining a directly overwritable balance field, but instead writing every credit change as an immutable record, with the balance derived from the sum of all records. It shares the same lineage as double-entry bookkeeping in banking: any adjustment must be completed by adding a reversing or forward record, and historical records are never modified in place.
The benefit is auditability. When a user complains "the credits I bought didn't arrive," you don't need to guess whether it was a lost payment callback, a duplicate write, or a concurrent overwrite — just replay the ledger in chronological order and you can pinpoint the break. The cost is higher query overhead, requiring indexes or periodically generated snapshots (checkpoints) to avoid full summation every time. This is also why the author emphasizes that ledger-style design must be paired with strongly consistent storage — otherwise concurrent writes can corrupt record ordering, and the ledger's traceability loses its meaning.
Cloudflare's Lock-In Dilemma and Portability Options
Cloudflare Workers run on proprietary V8 isolates, and the code can't be directly migrated to AWS Lambda, which requires a full Node.js runtime. If you depend heavily on KV or Durable Objects, the data access layer has to be completely rewritten.

If he could start over, he would use a general-purpose web framework like Hono to make the logic more portable, or even package the Hono app into a Node image container for true "move-in ready" portability. On the data layer, using Serverless Redis instead of KV would make the data completely cloud-agnostic.
That said, Cloudflare's advantages are equally clear: 100,000 requests per day on the free tier, and paid plans that are more competitive than AWS or Azure. The developer says he would still use Cloudflare, but next time he'll design a clear exit strategy for the data layer.
What is a V8 isolate, and why isn't it Node.js
A V8 isolate is an independent execution context within the V8 engine that has its own heap memory and JavaScript global object but shares the same process. Cloudflare Workers are built on it to achieve millisecond cold starts and high-density multi-tenant isolation: each request runs in a lightweight isolate, and the context can be reclaimed once the request finishes.
The problem is that an isolate lacks a full Node.js runtime — there's no fs, net, child_process and other built-in modules, and no long-lived process model to depend on. Many npm packages fail to run on Workers not because of syntax incompatibility, but because they depend on these Node-specific APIs. AWS Lambda, by contrast, provides a full Node.js runtime and file system (read-only outside /tmp), so migrating from Workers to Lambda often requires replacing the HTTP framework, rewriting the data access layer, and even redoing the build pipeline. This is the practical meaning of "portability must be designed from the data layer."
The Reality of AI Entrepreneurship: High Costs, Hard Moats
The developer is reserved about AI SaaS. AI-centric applications struggle to build moats. While theoretically possible, it requires large amounts of domain-proprietary data that ordinary people can hardly obtain. Token costs are far higher than standard compute, further squeezing margins.
But he's positive about AI-assisted programming. Handing coding to large models can significantly accelerate MVP building and iteration speed. The prerequisite is that the developer has a solid software engineering foundation; otherwise it's hard to judge the maintainability and security of AI output. If the goal is to learn programming, he doesn't recommend using AI; if you already have the skills, you can use AI to save time and put energy into other key aspects of running the business.
Why AI application moats are especially hard to build
Traditional SaaS moats often come from three places: network effects from data accumulation, switching costs, and tacit knowledge embedded in domain workflows. AI applications are naturally weak on the first two — the underlying model can be swapped out, API vendors can launch competing features themselves at any time, and user switching costs are extremely low.
The only relatively reliable barrier is the third: domain-proprietary data that others can't obtain. Examples include medical imaging annotations, industry-specific compliance text, or long-accumulated user behavior feedback loops. But such data is either regulated or requires years of accumulation, making it nearly impossible for an individual developer to obtain in the short term. Add to that token costs that scale linearly with usage, producing a worse gross margin structure than traditional software, and "doing something with AI" becomes far harder commercially than it sounds.
Three Core Lessons
The developer's three takeaways are worth referencing for every indie developer: mobile should not be the default platform choice; architecture design should prioritize portability; and AI usage is highly scenario-dependent — only bring it in when it truly fits.
Related articles

Felix Rieseberg Personal Homepage
Felix Rieseberg's personal homepage sparks attention on Hacker News. This article covers the Slack engineer's technical background, the rise and fall of nativefier, and the value of a developer homepage as a tech calling card.

@ai-sdk/vue 3.0.306 Patch Release Syncs with [email protected] Update
@ai-sdk/vue 3.0.306 released, synced with [email protected]. A routine patch release for the Vercel AI SDK ecosystem with no public API changes; the Vue adapter layer stays version-aligned with the core package.

Gemini 4 Argon API Launches: The Era of Million-Token Output Begins
Google launches Gemini 4 Argon with sparse MoE architecture, up to 1M output tokens, and a 68.90% Vals Index score, available across API, AI Studio, and the Gemini app.