I owned HymnDeck end to end: customer workflows, application architecture, AI orchestration, billing, infrastructure, deployment and production support. Every decision in this case study is one I made—and then had to live with.
- Role
- Founder and sole product engineer
- Timeline
- Core build: 1 March–1 April · iterated after launch
- Users
- Two churches weekly at peak
- Status
- Live · near-zero running cost
The problem
The capable person gets every job
The person I built for is a worship leader at a church of 20–150 people who is already doing four other jobs on a Sunday. Worship, tech, rehearsals, song sheets, AV and slides often land with the same person because they are the most capable person available—which is exactly why nobody takes the work off them.
They write a song sheet, turn it into slides, then change the arrangement: a verse gets cut, a chorus gets doubled or the song is abridged the night before. Last week’s deck can no longer be reused. In a larger church the AV team absorbs the work in 15–20 minutes. In a small church it takes the worship leader about an hour, or two to three when the operator is less confident or the design is elaborate.
The cost was not only time. Someone who spent Saturday night fighting text boxes was not fully present when they led on Sunday morning.
Delegation did not work because teaching the slide system was another task for someone with no spare tasks. Existing worship presentation platforms were not short of features; migrating a decade-old workflow was simply a big, low-urgency project competing with things on fire that week. The intervention had to fit the workflow already in place: accept the song sheet they already write and return the PowerPoint they already use.
My first prototype was a deterministic parser. It worked for my church because users defined their own slide delimiters, then fell over at the second church. Lyric formatting is home-grown and inconsistent. A language model could absorb that variation without making users learn a new syntax, while a chat interface removed the last technical step.
Assumptions tested
Reality edited the roadmap
The useful discoveries were not all confirmations. Several wrong assumptions directly reshaped the product.
Slide preparation was the bottleneck
The repetitive formatting work, rather than choosing songs, was consuming the time.
Lyrics lived in one master document
Some churches used folders of old PowerPoints as their source of truth.
Users would trust the output unseen
They wanted to review everything, which made a full slide editor essential.
Difficulty stopped people switching
Workflow migration was a bigger obstacle than missing presentation features.
The worship leader was the buyer
The user and problem were clear; who would actually pay was not validated.
This was a paid product
Billing went live, but nobody converted from the free tier.
Product decisions
Automate the expensive part. Keep humans in control.
I started with song-sheet-to-PowerPoint because every user did it every week, it cost an hour on average and up to three at the tail, and it produced an artefact they already understood. Nothing else in their week had that shape.
Cut attractive distractions
I left out sermon slides despite demand. I did not have the domain knowledge or a validated buyer, and generic text-to-slides sat directly in the path of ChatGPT and Gemini. Song slides had libraries, recurring formatting and arrangement quirks that rewarded specialisation. I also cut agentic slide design because I did not yet trust models with unsupervised aesthetic decisions.
Follow the source of truth
I assumed every church kept one master Word document. After launch, I found churches that rehearsed from folders of old PowerPoints. That feedback created a PowerPoint-folder import path and the hardest engineering problem in the project.
Make review a feature
Users land in a block-based slide editor with control over lyrics, positioning and design. I was dogfooding HymnDeck for my own church and knew that silently changing a lyric was unacceptable. The model proposes; the user can inspect and correct.
Teach by getting out of the way
The first screen uses a familiar chat interface with an example of exactly what to paste. A 30-second explainer can expand or disappear. I repeatedly ran silent demos, watched where people hesitated and rewrote the opening flow around those moments.
Song sheet, document or PowerPoint library
Structured AI tools with source constraints
Edit lyrics, layout, media and design
Download the PowerPoint the church already knows
Architecture
Serverless by constraint, not by slogan
A Turborepo monorepo pairs a Next.js frontend with a FastAPI backend in Docker on Cloud Run and Neon PostgreSQL over async SQLAlchemy. Long-running AI work leaves the request path through Cloud Tasks and runs in a private Cloud Run worker.
Explore the full technology stack
Frontend
Next.js 16, React 19, TypeScript, Turborepo, Bun workspaces, Tailwind CSS 4, Zustand, Framer Motion, Konva, React-Konva, dnd-kit, Firebase, next-themes, React Dropzone, React Markdown, Remark GFM, react-colorful and JSZip.
Backend and data
Python 3.14, FastAPI, Uvicorn, Pydantic, SQLAlchemy 2 async, asyncpg, Alembic, python-multipart, HTTP bearer authentication, Neon PostgreSQL and in-memory SQLite tests.
Infrastructure
Public and private Cloud Run services, Cloud Tasks, Cloud Storage, IAM service accounts, Secret Manager, Firebase Auth and Admin SDK, Google OAuth, Docker multi-stage builds, uv and gcloud.
AI and documents
OpenRouter through the OpenAI Python SDK, configurable vision and tool-calling models, persisted agent activity, python-pptx, OOXML, LibreOffice, Poppler, Pillow, python-docx, pypdf, ZIP/XML inspection and bundled fonts.
Three engineering problems
The parts worth writing about
The difficult work was not producing a good demo. It was putting unreliable inputs and probabilistic models inside a workflow people could depend on every week.
Constrained AI
Make a probabilistic component behave predictably
The problem was never getting a good answer from a model. It was guaranteeing the model could not do something unacceptable.
Silently altering a lyric is unacceptable in worship software, so I treated hallucination as an authorisation problem. The model never touches the database or PowerPoint file. It selects typed tools whose Pydantic arguments become OpenRouter function schemas; unknown arguments are rejected before execution.
Server-side rules require an authorised source for lyric changes, flag wording absent from uploaded material, scope tools by organisation and design, and require confirmation for higher-impact or shared mutations. Draft and committed decks are separate objects, so proposing and persisting are distinct steps and users review ambiguous matches.
I tested GLM 5, Kimi K2.5, Gemini 2.5 Flash, Gemini 3 Flash and MiniMax M3 against cost and latency. Gemini 3 Flash won the constrained formatter role; MiniMax M3 with thinking enabled won the agentic chatbot role. Tests cover tool schemas, invalid arguments, permissions, source requirements, wording preservation, provenance, confirmation, failures and observability redaction. A scored evaluation dataset remains the clearest testing gap.
Real-world files
Ingest messy PowerPoint libraries in Cloud Run
A folder of old presentations turned a text-parsing feature into a file-infrastructure problem.
Formatting varied too widely for a deterministic lyric extractor, so the system reads PowerPoint XML with a language model. Older files are converted by headless LibreOffice. Parallel conversion was unreliable in the container, so I serialised that stage and accepted latency in exchange for dependable completion.
Uploads went through three versions: asking users to make ZIP files; packaging selected folders in the browser with JSZip and no compression; then, when larger libraries hit Cloud Run’s 32 MiB request limit, transferring files directly to Cloud Storage through signed URLs while the API handled metadata only.
Durable work
Build background jobs that survive failure
AI generation is too slow and failure-prone to live inside one HTTP request, so it does not.
The API enqueues a Cloud Task that makes an authenticated request to a private Cloud Run worker. Job state persists in PostgreSQL through queued, running, processing, saving, completed, failed and cancelled states. Work survives request termination, container restarts, model failures and partial processing.
Duplicate delivery is handled, abandoned jobs are identifiable, and the frontend streams agent activity so users see progress rather than a spinner. Model calls, tool calls and wall-clock duration are all bounded.
Other people’s content
Hold little. Invent nothing.
The model only formats material supplied by the user; it never fills lyrics from training data. Uploads are not retained. The generated deck structure persists for the life of the account because deleting somebody’s slides after 30 days would be hostile. The only output is a downloaded .pptx; HymnDeck publishes or shares nothing.
Customer content is scoped to the authenticated organisation, Drive imports are restricted, URLs are allowlisted, and verbose model traces are off by default with unsafe values redacted during debugging. A takedown request would be straightforward to honour.
Production work
The work nobody demos
Two Cloud Run services are deployed separately, with the worker private and reachable only through authenticated Cloud Tasks. Ordered Alembic migrations cover the initial schema, billing, multiple designs, background jobs, agent sessions, asset provenance and legacy data removal.
Secrets arrive through Secret Manager rather than the image. Warm-up logic covers Cloud Run, Neon, Firebase and the model client so the first request of the week is not the slow one. Persisted runs, model requests, tool calls and durations provide application-level observability; proper alerting would come before scale.
Results, honestly
Useful product. Real users. No commercial traction.
One church closed; the other still uses HymnDeck every week. A workflow that took about an hour—and up to three at the worst end—now takes minutes. The product has run in production since April without manual intervention to keep it alive.
Billing was built, live and integrated, but nobody converted from the free tier. I under-tested willingness to pay and built a system whose engineering depth outran its distribution. Product risk, technical risk and distribution risk are different things; they need testing in a different order than the one I chose.
What I would do differently
Review the intent, not only the code
I built HymnDeck largely through agentic coding. The failure mode was rarely broken code; it was code that missed the intent. A chatbot bug would prompt a hyper-specific new tool instead of a better prompt using existing primitives. Architecture suggestions quietly depended on Cloud Run at min-instances: 1, discarding the serverless economics. Parsers handled the examples I supplied and nothing beyond them.
Reading a diff for product intent—not just correctness—was the highest-leverage habit I developed. Given another month, I would also make the system friendlier to newer agentic models. Worship leaders need slides that are consistent and pleasant, not bespoke; a fast agentic designer can now reasonably own more of that work.
Working alone made me quick and made me sloppy. I spent too little time on architecture up front, context-switched across product, frontend, backend, infrastructure and support, and let scope creep arrive without anyone asking why. On a team I would introduce lightweight design reviews early—especially around retries and tenant isolation—and go deeper in one area rather than shallow across all of them.
What this is evidence of
I know what shipping costs because I have owned what happened after launch.
I have done the messy end-to-end work of turning an AI product idea into functioning production software: product decisions, wrong assumptions, failure cases, deployment, users and ongoing responsibility. I am an early-career engineer who can contribute where product thinking, application engineering and AI-enabled systems meet.