{"slug": "experiments-in-software-factories-1-production-app-in-40-minutes", "title": "Experiments in Software Factories #1: Production app in 40 minutes", "summary": "A developer built and shipped pastmonday.com, a complete SaaS app with auth, a weekly calendar, Stripe tiered billing, referral credits, file uploads, and a calendar-aware AI chat, in about 40 minutes using a \"software factory\" of four reusable Claude Code skills that turn a one-page PROJECT.md brief into a staged product build. The app runs on a single backend vendor, Neon, using its GA Postgres, Auth, and Data API plus private-preview Object Storage, Functions, and AI Gateway, with Stripe handling payments and the developer's own cluster serving containers. The skills are public on GitHub, and each stage stops at an approval gate and verifies current package versions rather than relying on training data.", "body_md": "[pastmonday.com](https://pastmonday.com) is live. It has auth, a weekly calendar, tiered billing with Stripe, referral credits, file uploads, and an AI chat that knows your calendar. A small but complete SaaS, the kind of thing that used to be a three-month project.\n\nTwo things about it are unusual, and they're the reason for this post.\n\nFirst: I didn't hand-write it. A **software factory** did.. four reusable Claude Code skills that turn a one-page brief into a shipped product, stage by stage. I talked about this on [YouTube](https://www.youtube.com/watch?v=l9kY3hoj9AQ) recently, and the skills themselves are [public on GitHub](https://github.com/kubeden/kubeden/tree/main/ai/skills/claude-code/web-development).\n\nSecond: look at the vendor list. Postgres is Neon. Auth is Neon. The data API is Neon. Object storage is Neon. The LLM calls go through Neon. Stripe handles money and my own cluster serves the containers, and that's the entire list. **The backend is one vendor**, and until a few weeks ago that sentence was impossible to say, because half of these services didn't exist.\n\nNeon [announced](https://neon.com/blog/were-building-backends) they're building the full backend: Postgres, Auth, and the Data API are GA, and Object Storage, Functions, and an AI Gateway just entered private preview. I got early access, pointed the factory at all of it, and this post is the build report.\n\n**Note:** this post is a transcription of [my video on software factories](https://www.youtube.com/watch?v=l9kY3hoj9AQ), passed through AI and edited for reading. If you'd rather watch than read, here it is:\n\n[▶ Watch: Software Factories are SCARY but possible (and easy to do)](https://www.youtube.com/watch?v=l9kY3hoj9AQ)\n\n`PROJECT.md` brief, shipped The core idea is one separation: **the skill is the HOW, the brief is the WHAT.**\n\nEverything project-specific lives in a single `PROJECT.md` in the repo: name, one-liner, the plans table, the palette and fonts, the entities, which services are enabled. Everything reusable lives in the skills. When I want a different product, I write a different brief. The factory doesn't change.\n\nThere are four stages, each a separate skill, each reading that same brief:\n\n`set-project-structure`) builds the skeleton: Next.js with the directory contracts, the Drizzle schema, env keys with comments on where to get each value, design tokens, and placeholder clients for every enabled service. It deliberately builds `set-landing-page`) is the taste stage. It studies the brief's reference sites, writes real copy before layout, and works against an explicit banned-patterns list: no gradient hero, no \"Supercharge your workflow\", no fake testimonials.`set-app-backend`) is the correctness stage. Money and limits. Every quota, credit, token budget, and dollar is enforced on the server, atomically, idempotently. More on this below, because this is where Neon's new services plug in.`set-dashboard`) builds the app people live in: the calendar, the modals, the streaming AI chat, consuming only the contracts stage 3 exposed.\nEach stage stops at an approval gate before building, verifies current package versions instead of trusting its training data, and must leave the repo shippable when it ends. No half-built sections, no \"we'll polish later\".\n\nClick through the stages here.. this is the actual pipeline that built pastmonday:\n\n▶ **The software factory pipeline (interactive)**, an interactive widget, runs on [the original post](https://denislavgavrilov.com/software-factory-neon-entire-backend).\n\nI've written the first three stages up in detail before, if you want the deep dives: [the concept](https://denislavgavrilov.com/how-to-build-any-web-application), [the brief](https://denislavgavrilov.com/how-to-build-any-web-application-fff), and [the landing page](https://denislavgavrilov.com/how-to-build-any-web-application-2bb).\n\nWhat matters for this post: a factory like this is only as good as the surfaces it builds on. Stage 3 is where the factory has to wire auth, storage, data access, and LLM calls, and that's usually where you accumulate vendors. One for identity, one for buckets, one for model routing, plus the database. Four dashboards, four bills, four sets of credentials to leak.\n\nThis time, all four seams landed on the same platform.\n\nHere's what pastmonday actually runs on, and the state of each piece:\n\n| Service | Role in pastmonday | Status | \n|---|---|---|\n| **Postgres** | entries, profiles, billing, referrals, asset metadata | GA | \n| **Neon Auth** | signup, login, sessions, the identity table | GA | \n| **Data API** | PostgREST-style HTTP reads | GA | \n| **Object Storage** | user file uploads, S3-compatible | private preview | \n| **AI Gateway** | all LLM calls, metered per request | private preview | \n\nA few receipts from the codebase, because \"it works\" is cheap to say.\n\n**Auth** ships its own schema. Identity lives in `neon_auth.user`, and the factory's rule is: never mirror it. App tables hold foreign keys straight into it, so there's no sync job, no webhook that copies users around, no second source of truth:\n\n``` js\n// Neon Auth's identity schema, referenced, never mirrored\nexport const neonAuth = pgSchema(\"neon_auth\");\nexport const authUser = neonAuth.table(\"user\", { id: text(\"id\").primaryKey() });\n\n// profiles FK straight into it\nuserId: text(\"user_id\").notNull()\n  .references(() => authUser.id, { onDelete: \"cascade\" }),\n```\n\n**Storage** speaks S3, so there's no proprietary SDK to learn. The client is the plain AWS SDK, and it auto-reads the endpoint from `AWS_ENDPOINT_URL_S3`. Uploads are presigned PUTs with keys scoped per user, and since a presigned PUT can't enforce a size limit, the confirm route does a `HeadObject` and deletes anything oversized. The interesting part: [Neon's storage branches with your database](https://neon.com/docs/changelog). Every branch gets its own isolated storage state. Your files and your rows stop drifting apart across environments.\n\n**AI Gateway** is one API for models from Anthropic, OpenAI, Google and others, with per-request logging and rate limits, running on the same gateway infrastructure Databricks says handles [125 trillion tokens a month](https://neon.com/blog/were-building-backends). For the app it means one token in the environment instead of a drawer full of provider keys, and the model is a config value:\n\n``` js\nconst provider = createOpenAICompatible({\n  name: \"neon-ai-gateway\",\n  baseURL: `${NEON_AI_GATEWAY_BASE_URL}/ai-gateway/mlflow/v1`,\n  apiKey: process.env.NEON_AI_GATEWAY_TOKEN,\n});\nexport const chatModel = provider(process.env.NEON_AI_GATEWAY_MODEL);\n```\n\nPoke around the real architecture here, every box is clickable:\n\n▶ **pastmonday's backend, all Neon (interactive)**, an interactive widget, runs on [the original post](https://denislavgavrilov.com/software-factory-neon-entire-backend).\n\nIn [the last Neon post](https://denislavgavrilov.com/neon-api-first-agentic-coding) I made one argument: Neon is API-first, so an agent can reach a primitive the moment it exists, before anyone builds it a button. I proved it by building a feature Neon hadn't shipped yet.\n\nThis build pushed that idea further than I expected, twice.\n\nFirst, the AI Gateway is so new that its official AI SDK provider package **wasn't even installable yet** when the factory ran stage 3. A human would have stopped there. The agent didn't: it probed the gateway, found the OpenAI-compatible endpoint at `/ai-gateway/mlflow/v1`, verified it live with a real request, and wired the app through the AI SDK's generic OpenAI-compatible provider instead. The code carries a comment saying \"swap to the official provider once it installs\". That's what API-first buys you: the endpoint existed, so the missing package didn't matter.\n\nSecond, the convergence I didn't see coming: a week after I published my factory skills, Neon shipped [agent skills of their own](https://neon.com/docs/changelog).. instruction files that teach coding agents how to integrate Functions, Storage, and the AI Gateway, installable with one command:\n\n```\nnpx skills add neondatabase/agent-skills\n```\n\nRead that again from where I'm standing. I write skills that teach an agent how to *build products*. Neon now writes skills that teach the same agent how to *consume their platform*. The factory and the platform are meeting in the middle, and neither side needed the other's permission. This is what it looks like when a vendor treats agents as the primary developer.\n\nA factory that ships beautiful broken apps is a liability, so stage 3 is paranoid by design. pastmonday charges money and sells limits: Free gets 1 entry and a 5,000-token AI budget, Pro gets 10 entries and 20,000 tokens a cycle, Max is unlimited. Limits only mean something if they can't be raced.\n\nOne constraint made this sharper than usual: the factory wired Drizzle through Neon's HTTP driver, which doesn't do interactive transactions. No `BEGIN`, hold the lock, decide, `COMMIT`. So every money-critical operation had to become a **single atomic statement**. This is the actual entry creation, quota check and insert in one round trip:\n\n```\nWITH consumed AS (\n  UPDATE billing\n     SET used_this_cycle = CASE WHEN used_this_cycle < monthly_quota\n                                THEN used_this_cycle + 1 ELSE used_this_cycle END,\n         extra_credits   = CASE WHEN used_this_cycle >= monthly_quota AND extra_credits > 0\n                                THEN extra_credits - 1 ELSE extra_credits END\n   WHERE profile_id = ${profileId}\n     AND (used_this_cycle < monthly_quota OR extra_credits > 0)\n  RETURNING id\n)\nINSERT INTO calendar_entries (user_id, title, ...)\nSELECT ${userId}, ${title}, ...\nWHERE EXISTS (SELECT 1 FROM consumed)\n```\n\nQuota first, then credits, else nothing gets inserted and the caller gets a `402`. Two concurrent requests can't both take the last unit, not because the code is careful, but because the statement can't be split.\n\nThe same paranoia runs through the rest: the AI budget is checked server-side *before* every gateway call and metered from real usage on finish, Stripe webhooks dedupe on event id in a `processed_stripe_events` ledger so a replayed event can't grant credits twice, and quota-exhausted returns a typed `402` the UI turns into an upgrade prompt instead of an error toast.\n\nNone of this is Neon-specific, but it's the standard the factory holds, and the new services slotted into it without a fight. That's the actual test of a backend platform: not the demo, the enforcement paths.\n\nMy favorite Neon primitive has always been branching. I've built [preview environments](https://denislavgavrilov.com/agentic-workflows-branched-environments) where every pull request gets its own database branch, created and destroyed by the agent through the API.\n\nThe new services extend that idea past the database. Storage state branches with your data today, and Neon manages Functions through the same CLI and API as everything else. The direction is obvious and they're saying it out loud: a branch should fork the *backend*, not just the schema.\n\nFor a software factory, that's the missing piece. Today my factory pipeline validates each stage against the production-shaped environment it builds. The version I want is: every factory run, every agent session, every experiment gets a full isolated copy of the backend.. rows, files, functions.. for the lifetime of a task, then vanishes. Database branching already proved this workflow for schema changes. Extending it to the whole backend turns \"spin up a staging environment\" from an afternoon of Terraform into a flag on a CLI call.\n\nThat's the same story as the spending-caps feature from my last post, which is still marked \"coming soon\" by the way, and I'm still holding them to it: **the primitives ship first, through the API, and the people building on top get to run ahead of the roadmap.**\n\nThe pitch for this stack isn't \"Neon does everything\". It's narrower and more honest: a full working SaaS.. identity, data, files, AI, money.. now fits in one brief, four skills, one backend vendor, and one Stripe account. pastmonday went from empty repo to production on my cluster in about 40 minutes, and most of that time I was making taste decisions at the approval gates, not doing integration work.\n\nIf you want to go deeper: the [video](https://www.youtube.com/watch?v=l9kY3hoj9AQ) covers the factory concept, the [skills are on GitHub](https://github.com/kubeden/kubeden/tree/main/ai/skills/claude-code/web-development) to steal, the [series](https://denislavgavrilov.com/how-to-build-any-web-application) walks each stage, and [pastmonday.com](https://pastmonday.com) is the living proof.. go create an entry, that's a race-safe atomic quota consume you're clicking.\n\nAnd if you're at Neon: ship the branch-forks-everything future. I have a factory waiting for it.", "url": "https://wpnews.pro/news/experiments-in-software-factories-1-production-app-in-40-minutes", "canonical_source": "https://dev.to/kubeden/experiments-in-software-factories-1-production-app-in-40-minutes-1dp", "published_at": "2026-10-21 07:00:04+00:00", "updated_at": "2026-10-21 07:09:57.949751+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "ai-products", "ai-infrastructure"], "entities": ["Neon", "Claude Code", "Stripe", "pastmonday.com", "GitHub", "Next.js", "Drizzle"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/experiments-in-software-factories-1-production-app-in-40-minutes", "markdown": "https://wpnews.pro/news/experiments-in-software-factories-1-production-app-in-40-minutes.md", "text": "https://wpnews.pro/news/experiments-in-software-factories-1-production-app-in-40-minutes.txt", "jsonld": "https://wpnews.pro/news/experiments-in-software-factories-1-production-app-in-40-minutes.jsonld"}}