<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Isaiah Kim</title>
    <description>The latest articles on DEV Community by Isaiah Kim (@kyisaiah47).</description>
    <link>https://dev.to/kyisaiah47</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3981339%2F7515e39c-98fe-4819-bcc5-d59ace01fc25.jpeg</url>
      <title>DEV Community: Isaiah Kim</title>
      <link>https://dev.to/kyisaiah47</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kyisaiah47"/>
    <language>en</language>
    <item>
      <title>How one person runs 73 production apps</title>
      <dc:creator>Isaiah Kim</dc:creator>
      <pubDate>Thu, 09 Jul 2026 01:02:41 +0000</pubDate>
      <link>https://dev.to/kyisaiah47/how-one-person-runs-73-production-apps-ie8</link>
      <guid>https://dev.to/kyisaiah47/how-one-person-runs-73-production-apps-ie8</guid>
      <description>&lt;p&gt;I maintain 73 production web apps by myself. Not 73 landing pages — 73 apps with auth, billing, a database, their own domains, their own UI, and real AI features behind a paywall. People assume the trick is that they're thin. It's the opposite: the only way one person keeps 73 real apps alive is to make the apps thin and the &lt;em&gt;engine&lt;/em&gt; thick. Almost everything an app does, it borrows.&lt;/p&gt;

&lt;p&gt;This is a teardown of that engine. I'm writing it because the engine is now a product — &lt;a href="https://api.kynth.studio" rel="noopener noreferrer"&gt;api.kynth.studio&lt;/a&gt;, which I call Kynth Core — and 73 apps in production is the most honest load test I can offer for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shape of the problem
&lt;/h2&gt;

&lt;p&gt;A single app is easy. The cost of software isn't the first instance, it's the Nth. Every app wants the same twelve things: sign-in, a session, a user row, a Stripe subscription, a webhook to reconcile it, analytics, SEO metadata, a design system, an AI call with a budget on it, and a way to ship without babysitting a pipeline. If each app owns its own copy of those twelve things, then N apps is 12N surfaces to patch when Stripe rotates an API version or Next ships a breaking change. That number is where solo maintenance dies.&lt;/p&gt;

&lt;p&gt;So the design rule is blunt: &lt;strong&gt;an app is allowed to own its domain logic and its screens, and nothing else.&lt;/strong&gt; Everything horizontal lives once, in a shared package, and every app imports it. When I fix auth, I fix it for 73 apps in one commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The monorepo and the shared packages
&lt;/h2&gt;

&lt;p&gt;It's a Turborepo monorepo. The apps are Next.js. The load-bearing part isn't the apps directory — it's &lt;code&gt;packages/&lt;/code&gt;. A few of them do most of the work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@kynth/analytics&lt;/code&gt;&lt;/strong&gt; — one React component, mounted in every app's root layout. It initializes PostHog, fires &lt;code&gt;$pageview&lt;/code&gt;, and — this is the part that pays for itself — self-labels every event with an &lt;code&gt;app&lt;/code&gt; super-property derived from the host. A signup on one app's subdomain and a signup on any other land in the same event stream, correctly attributed, with zero per-app wiring. It also fires the funnel events and the ads conversion pixel on the same shared success boundary. No app writes analytics code. They inherit it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@kynth/shell&lt;/code&gt; / &lt;code&gt;@kynth/layouts&lt;/code&gt;&lt;/strong&gt; — the design system as components, not copied CSS. A three-zone workspace layout, the modals, the toasts, the form primitives, the dashboard cards. Each app sets exactly one variable — &lt;code&gt;--shell-accent&lt;/code&gt;, its identity hue — and the whole chrome recolors. The house chrome is monochrome on purpose so that one variable is the only thing an app has to decide.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@kynth/seo&lt;/code&gt;&lt;/strong&gt; — metadata, JSON-LD, sitemaps, and the cross-app link graph, generated from a single catalog file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That catalog file is the spine. &lt;code&gt;catalog.ts&lt;/code&gt; is the source of truth for what apps exist: slug, display name, the accent hue, the marketing copy. The build reads it; the SEO package reads it; the launcher reads it. When I add an app, I add a catalog entry, and the rest of the platform notices.&lt;/p&gt;

&lt;h2&gt;
  
  
  One database, one deploy
&lt;/h2&gt;

&lt;p&gt;Two decisions do more for maintainability than any code:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One Supabase project for all of them.&lt;/strong&gt; Every app shares one auth pool and one Postgres. A user is a user across the whole suite; I don't run 73 databases or reconcile 73 identity systems. Row-level security and a per-app column keep each app's data its own. The operational win is that there is exactly one place to look when something is wrong with data, and exactly one migration surface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One Vercel project, not 73.&lt;/strong&gt; This is the counterintuitive one. The apps used to deploy as separate Vercel projects — dozens of dashboards, dozens of env-var sets, dozens of build configs drifting apart. Now they consolidate into a single deployment that routes by &lt;code&gt;Host&lt;/code&gt; header: the same running project serves every subdomain and decides which app to render from the hostname. I deploy once. Rolling an app back is re-pointing a subdomain, not a redeploy. The 12N surface problem collapses because there's one of most things.&lt;/p&gt;

&lt;p&gt;Shared infra follows the same "one of it" rule everywhere it can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One OAuth callback per provider&lt;/strong&gt;, app-agnostic, at a single hub URL. Apps fan out from a &lt;code&gt;state&lt;/code&gt; param carrying &lt;code&gt;{ app, uid }&lt;/code&gt; instead of each registering its own redirect URI. Adding an app doesn't touch the Google Cloud console.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One Stripe webhook endpoint&lt;/strong&gt; for the whole suite, routing by &lt;code&gt;metadata.app&lt;/code&gt;. Stripe caps endpoints per account; sharing one means the cap never binds and there's a single signature to verify.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The AI layer is a wallet, not a hundred keys
&lt;/h2&gt;

&lt;p&gt;Every app has AI features, and AI features are where a solo operator gets destroyed two ways: unbounded cost, and copy-pasted prompt plumbing that rots. So the AI doesn't live in the apps either. It lives behind a credit-wallet API — the same Kynth Core I mentioned at the top.&lt;/p&gt;

&lt;p&gt;An app doesn't hold a model key or manage a rate limit. It spends credits against a wallet through a typed endpoint. The API is a suite of capabilities — document parsing (PDF / invoice / receipt / statement / contract → schema-valid JSON), text and structured-extraction endpoints, and more — each with a fixed request/response contract and a per-call credit cost. A runner gives every endpoint the same shape: auth, wallet debit, validation, execution, structured error. Adding an endpoint is implementing one function against that runner; every app gets it for free the moment it lands.&lt;/p&gt;

&lt;p&gt;This is why the AI is Pro-only inside the apps: a metered wallet has a real marginal cost, so the paywall isn't a growth tactic, it's the actual economics showing through. The free tier is the deterministic, non-AI path; the AI is what you pay for, in the app and on the API alike.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generation, not hand-authoring
&lt;/h2&gt;

&lt;p&gt;New apps aren't hand-built from an empty folder. There's a generation pipeline: a new app starts from the shared packages and the catalog entry, with the workspace shell, auth, billing, analytics, and SEO already wired because those come from imports, not from boilerplate I retype. What I actually write per app is the domain: the data model and the handful of screens that make &lt;em&gt;this&lt;/em&gt; app different from the last one. The horizontal 90% is inherited; I author the vertical 10%.&lt;/p&gt;

&lt;p&gt;That ratio is the whole story. It's why the count can be 73 and not seven. The number isn't a flex — it's the proof I care about as a developer, because 73 apps sharing one auth, one billing surface, one analytics contract, one deploy, and one AI wallet means those shared surfaces have been exercised across 73 different failure modes. Battle-tested is a claim; 73 apps in production is the receipt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why you might want the engine
&lt;/h2&gt;

&lt;p&gt;Most of what I described you can't buy — it's a monorepo I run. But the part that's genuinely reusable, I extracted: the credit-metered capability layer is Kynth Core, and it's live at &lt;a href="https://api.kynth.studio" rel="noopener noreferrer"&gt;api.kynth.studio&lt;/a&gt; with SDKs on npm and PyPI and an MCP server. If you've ever bolted an AI feature onto an app and then spent the next month babysitting the key, the budget, and the retry logic, that's the exact tax this removes. You get a typed endpoint, a wallet with a hard ceiling, and a response you can trust the shape of.&lt;/p&gt;

&lt;p&gt;I built it because I needed it 73 times. That's the only endorsement I can give it, and it's the one I'd trust.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Isaiah Kim — one-person AI product studio.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>architecture</category>
      <category>ai</category>
      <category>saas</category>
    </item>
  </channel>
</rss>
