The Stack
My actual Stack
(Thou shall not judge)
TL;DR: I designed, built and released a media tracker for movies, TV, books and games with AI coding agents. Web is live at onthestack.app, iOS is in App Review, Android is next. It could cost $0 to build and test, but it'll cost to make live, details below. This page is the what, why and how. Learnings in posts will be linked at the bottom!
What and why?
There's a note on my phone that is over ten years old. It's a list of movies, shows, books and games that people recommended to me, or that Reddit told me I needed. I've opened it maybe six times a year to write something down, but I always forget to mark something as done. And it's just a pest to browse. See for yourself.
The Stack is that note, productised, and I'm the ICP: a dark-mode-first app for tracking everything you want to watch, read, and play in one place. Instead of juggling a separate tracker per medium (or a Notes app), it uses one universal vocabulary across all four (Pending, In progress, Done, Dropped) and asks one binary question everywhere: am I done with this or not? More a to-do list than a media tracker. Built for iOS, Android, and web from a single codebase. And built for me, first and foremost. But I decided to share with the world because it had been a while since I'd battled web deployments and app submissions this closely. And I missed it!
I'm building it myself, end to end, for two reasons:
The app itself. I wanted a tracker that matches how I actually think about the pile of things to "consume": one shared to-do list, not half a dozen trackers with tons of features I don't need.
The process. This is a firsthand experiment in what an AI coding agent can do entirely on its own versus where a senior product manager's judgement adds true value: sequencing the work, catching tradeoffs an agent won't flag unprompted, holding one coherent architecture together across hundreds of small decisions, and painstakingly trying to break everything during on-device QA.
The rule I set from day one: understand how the pieces fit together and how the code works rather than execute blindly. Every architectural choice (the data model, the backend, the security posture, the accessibility commitments) is one I can explain and defend, not one I accepted on faith from a tool. That discipline shapes everything below:
Favour the least complexity that still respects the full product vision.
Build the expensive-to-retrofit things (data model, internationalization, accessibility) correctly from the start rather than undertaking wild refactors later.
Treat security surface as a design criterion, not a checklist: fewest dependencies, least data exposure, narrowest permissions. Anything that widens it is a decision flagged out loud before it's built, never a side effect discovered inside a diff.
Do the localization and accessibility QA on a real device, VoiceOver on, language set to Spanish. Every time it frustrated me, I thought about how a visually impaired person or a Spanish-only speaker would feel, and that lit a fire in me.
Under the hood
If you're not an engineer: I've tried to give explanations in brackets, may the Force or web search be with you.
Frontend: React Native + Expo. One codebase that ships to iPhone, Android and the browser. The foundation phase ran in Expo Go (point your phone's camera at a QR code and the app runs on it, live-reloading, no native build), which is why I could iterate on a real device from day one and catch all those yummy UI bugs. Once native modules came in (they need native code, which Expo Go can't run), the project moved to development builds, a real installable app that still live-reloads, and store submissions run through EAS, Expo's build-and-submit service.
Design system over custom components. The UI is built stock-first on gluestack-ui v3, a React Native component library: I compose its stock components rather than writing bespoke styled elements. There is exactly one owned primitive (a bottom sheet component) as the exception, earned only after the stock one failed a device-verified accessibility test that no setting could fix. Design tokens (the named values for colour, spacing and type) were extracted directly from Figma's variables, not eyeballed from screenshots, into one semantic token system that's dark-mode-first but built to support light mode without a rewrite. Figma gave the values once; from then on the design-system MD file is the source of truth, not Figma.
Backend: Supabase. A hosted Postgres (relational, tables-and-rows) database with auth built in. Chosen over a NoSQL document store because the data is inherently relational: type-aware items with per-type fields, statuses and tags, queried and filtered across types. Row-Level Security, meaning the database itself decides who can see which rows, enforces "users see only their own stack" regardless of what the app code does. Anonymous Auth means people can use the app fully (add, track, browse) before ever creating an account. When they do, sign-in is passwordless (email plus a one-time code), and deleting the account is one in-app action.
Email and domain: Resend + onthestack.app. "Just email them a code" turned out to contain a supply chain. Supabase's built-in mailer is development-only and its templates can't be edited: the default sign-in email sends a link, not the six-digit code the app asks for, and it's English-only. So auth mail goes through Resend (EU region, free tier, thousands of emails of headroom) as custom SMTP (the app's own mail server, basically), with custom-made bilingual templates that branch on the account's language. Sending from your own domain is what makes that mail look legitimate to spam filters, which meant buying one. The thestack.* variants were squatted or brokered ("request pricing"), so I walked away on principle and price (the domain vultures had taken everything else), and as a language nerd, I just loved onthestack.app. The web app's subdomain is its.onthestack.app and that makes me giggle. I bought it through Vercel, because the domain that sends auth email and the domain that serves the web app should be one thing in one dashboard. Two deliberate choices: NO click tracking (it rewrites links through a redirect and can break the very auth flow the email exists for) and NO open-tracking pixels (GDPR-disclosable data collection for zero product value). And the addresses receive, too: a free forwarder routes hello@ and privacy@ to a real inbox, because a privacy policy with a gmail.com contact is a trust problem, and a deletion request on a 30-day clock must never sit unread.
Data sources. TMDB (movies/TV), IGDB (games), Open Library (books), Metron (comics) and AniList (manga), normalised into one common item shape so search and storage never branch by media type. Every search runs against exactly one source, chosen before anything fires, which keeps third-party API budgets... manageable.
Supporting layers.
Caching: TanStack Query. Perceived-instant search, and "don't choke on a subway signal drop".
Languages: i18next. English and Spanish from day one; adding a language is adding a file. BAM! I'm a former Localization Manager.
Analytics: Amplitude. Behind one helper file, so the SDK (the vendor's code library) can be swapped without touching the app.
Tips: RevenueCat, plus Stripe on web. A tip jar, not a subscription or a paywall: three amounts, repeatable, never recurring, unlocks nothing, ever.
Tips go through the App Store on iOS, the web tip sheet on web, and a link to the support page on Android. The app stays entirely free for the end user; the costs come from my "fun money" budget. RIP takeaway food.
Security posture. The guarantee lives in the data layer, not in remembering to filter correctly on every screen. Secrets never touch code or the app bundle; client-safe keys and server-only secrets are two different classes with two different homes, and anything that needs a real secret (the games API, account deletion) runs server-side behind a small function. Sign-in is passwordless through one-time codes, so there are no passwords to leak on the Dark Web. The web app ships an enforcing Content Security Policy (the browser's allow-list of what a page may load), and the test suite mocks the backend at the network edge, so a test run can never create a row in production.
Accessibility. A first-class, day-one commitment (WCAG 2.2 AA, and the EU's Accessibility Act, which covers mobile apps) rather than a pass at the end: screen-reader labels, proper touch targets, and a rule that no action is ever gesture-only.
How it gets built
The operating system is a folder of markdown files. Every decision and its rationale lives in DECISIONS.md, every design token and mismatch in DESIGNSYSTEM.md, every rule the coding agent must follow in CLAUDE.md. Agent sessions share no memory, so the documents are the only thing every session starts from. I don't prompt my agents so much as onboard them. That's the subject of the first learning post below; here's the loop those documents run.
Design direction. Figma (the Obra shadcn/ui kit) defined the tokens and layout direction for the foundation phase. It was deliberately never treated as a pixel-perfect spec: every screen still had to be translated into real gluestack-ui v3 components, since Figma's raw values don't map one-to-one onto what the library actually ships.
Designing beyond the foundation. Figma earned its keep establishing the component vocabulary and the discipline of mapping every design decision to something the code library actually offers. But once that design system existed, mocking new screens in Figma became double work. Newer flows (search, the media card) skip it entirely: Cowork sketches interactive layout options directly from the established tokens and components, I pick and push back like I would in a design crit, and the winning option is written straight into the ticket as the spec. Design stopped being drawing and became composing known parts, and the real mockup tool is the app itself, hot-reloading on my phone.
Spec discussion. Specs get settled in Cowork, Claude's planning workspace, deliberately outside the coding agent. Early on that meant pulling the Figma kit's real variables through its connector and mapping each onto a real component. Now it means talking through options and tradeoffs (layouts, interaction patterns, edge cases like "what does a screen reader hear?") until a decision exists that's worth writing down. Sometimes that's a different model altogether: Fable when I need a "co-founder's" judgement, Sonnet when I just need research. Either way the output is the same: a written spec that the coding agent can build from without ever needing its own opinion about design.
Ticket handoff. Once a spec is settled, Cowork writes it into the design-system doc and, connected to Linear, opens a scoped ticket from it: one ticket per build step, not per line of code. The ticket carries the evidence behind the decision, because the session that builds it will inherit no context. Bugs and parked decisions get their own tickets, that good old tech debt.
Build. Claude Code, running in Zed's integrated terminal (the editor is the cockpit, the agent is the engine), builds directly from that ticket: the coding, refactoring and git operations. It never needs its own Figma connection; it builds entirely from the written spec. I don't review code diffs; I tried in the beginning and it wasn't conducive to learning, just created cognitive load and frustration. I review the app, and I use different models to translate my findings or requests to blocks ready for my Opus coding agents. Every feature branch merges only after a device round I run myself, and the device round is the gate no amount of agent agreement can replace.
Guardrails for the agents. The coding agent is fast and eager, so the boundaries are written down where it reads them before every task (Linear tickets, MD files in the repo). Secret values never appear in a chat, a ticket, or a diff. Credentials, account creation, payments, and anything destructive or irreversible in production are human-only, mine, always. The agent may deploy a server-side function only when the change is additive, reviewed in a plan, and immediately gated by a smoke test that has to pass. And any dependency, permission, or data exposure it wants to add is a judgment call it has to surface, not a line it gets to slip in. Speed is the agent's; the blast radius is mine to keep small.
Shipping. Everything lives in a private GitHub repo. You can see how I was grinding in July/August here. The web app is deployed on Vercel at onthestack.app; mobile builds and store submissions go through EAS, with iOS in App Review and Android following.
The result is a project that moves at the pace of one person building on the side, but with the architectural discipline of a team that plans before it builds: every decision logged, reasoned about, and revisited only when new information actually warrants it. It's also, quietly, a running log of where an AI agent is genuinely capable on its own and where it still needs a product leader protecting the bigger picture and the end user experience.
If you’re a PM thinking of doing this
You can. Just be prepared to feel like your brain and wallet are bleeding.
What it costs. $0 to build and test: Expo, Supabase, Vercel and Resend all have free tiers that comfortably cover a launched indie app. The Vercel domain is about $12 a year with tax. The real pain is Apple's $99 developer account, at launch. Google was $25 once but I won't be able to support tipping there for reasons I'll explain in another post. Claude for coding and project management was €15/month for Pro at first, then Max €90/month when I got serious with the coding.
How long it took. Eleven weeks from first commit to iOS App Review, building a few hours a day, during a career break.
Where the time goes. Deciding, specifying, QA, and waiting on the agents. Most of it waiting on the agents, so prepare parellel workstreams that do not overlap. Other fun things:
Reading what the agent built and finding out on a real phone where it doesn't work
Swearing at regressions
Writing things down so the next session doesn't re-open a settled question and decisions are captured.
If you've ever run a roadmap, a design crit, and a bug bash, you already have most of the muscles. Also, for better or for worse, Code is less cantankerous than your average engineer.
What you need. Claude Code or whatever your poison is, a planning space (Cowork), a place for tickets (Linear), and an optional code editor (Zed, but I used Cursor before it got acquired). Four tools, and a habit of never letting a decision live only in a chat window or in context (yikes x1000 to context creep).
What is forever hard. Knowing what to build and what to cut (RIP my grand LLM import plans for now). Catching the confident wrong answer from the agent. Holding one architecture together across a few hundred small calls. The agents made the typing cheap; they made judgement the premium skill. And, let's be real, not everyone can spend between 500-1,000 euros a year on a passion project.
The learnings
Coming soon, starting with the biggest.
Rely on artifacts, not on context (or models) — coming next.
..
Questions? Say hi via the Contact page or message me on LinkedIn if we're connected.