<?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: Dominic li</title>
    <description>The latest articles on DEV Community by Dominic li (@tallyassistant).</description>
    <link>https://dev.to/tallyassistant</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%2F4019871%2F0e735219-46ae-4d29-88a4-75970c5edbfc.png</url>
      <title>DEV Community: Dominic li</title>
      <link>https://dev.to/tallyassistant</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tallyassistant"/>
    <language>en</language>
    <item>
      <title>How I Built an AI Bookkeeping SaaS as a Solo Developer (And What I'd Do Differently)</title>
      <dc:creator>Dominic li</dc:creator>
      <pubDate>Tue, 07 Jul 2026 15:56:59 +0000</pubDate>
      <link>https://dev.to/tallyassistant/how-i-built-an-ai-bookkeeping-saas-as-a-solo-developer-and-what-id-do-differently-je9</link>
      <guid>https://dev.to/tallyassistant/how-i-built-an-ai-bookkeeping-saas-as-a-solo-developer-and-what-id-do-differently-je9</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Got laid off from my dev job. Built an &lt;a href="https://tallyassistant.com/features/ai-invoice-generator" rel="noopener noreferrer"&gt;AI invoicing for freelancers&lt;/a&gt; tool plus full bookkeeping in 4 months. Tech stack: Next.js 16, Prisma, OpenAI, Clerk, Inngest. Launched on Product Hunt. Here's the architecture, the mistakes, and the one decision that saved me months.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Backstory
&lt;/h2&gt;

&lt;p&gt;I'm Dominic, a full-stack developer from Chengdu, China 🇨🇳. I spent years freelancing for international clients before taking a full-time programming job. Then I got laid off.&lt;/p&gt;

&lt;p&gt;Instead of job hunting immediately, I decided to solve the problem that annoyed me most during my freelance years: &lt;strong&gt;bookkeeping.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every month, I'd download CSVs from Stripe, PayPal, Wise, and my Chinese bank — each in a completely different format. I'd spend hours reformatting dates, converting European commas, matching currencies, and manually creating invoices. Then I'd awkwardly chase clients who were late on payments.&lt;/p&gt;

&lt;p&gt;I looked at existing tools. QuickBooks and Xero felt like they were built for accountants, not freelancers. They expected clean, standardized data. Real freelancer data is messy — different column headers, different date formats, different languages.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;Tally Assistant&lt;/strong&gt; — an AI bookkeeping tool that accepts whatever you throw at it and figures out the rest. The centerpiece is &lt;a href="https://tallyassistant.com/features/ai-invoice-generator" rel="noopener noreferrer"&gt;AI invoicing for freelancers&lt;/a&gt; — describe your work in one sentence and the AI builds the invoice with line items, tax, and a PayPal payment link in under 60 seconds.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Tech Stack (And Why I Chose Each Piece)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Next.js 16 (App Router)
&lt;/h3&gt;

&lt;p&gt;I picked Next.js because I wanted SSR for SEO and the App Router for clean file-based routing. The landing page, blog, features pages, comparison pages — all statically generated at build time. Zero client-side JS on the marketing pages means fast Core Web Vitals and happy Google rankings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I got right:&lt;/strong&gt; Static generation for all 60+ SEO pages. sitemap auto-generates from content files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I'd change:&lt;/strong&gt; I'd set up ISR (Incremental Static Regeneration) sooner for the blog page so I don't need a full redeploy for every new post.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prisma + PostgreSQL
&lt;/h3&gt;

&lt;p&gt;Prisma's type-safe queries are a superpower for solo devs. You change the schema, run &lt;code&gt;prisma migrate dev&lt;/code&gt;, and TypeScript tells you everywhere that broke. No silent runtime errors from missing columns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One thing that saved me:&lt;/strong&gt; Using &lt;code&gt;prisma generate&lt;/code&gt; in the &lt;code&gt;postinstall&lt;/code&gt; script so Vercel always has the latest client generated at deploy time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clerk for Auth
&lt;/h3&gt;

&lt;p&gt;I didn't want to build auth from scratch — it's boring, security-critical, and adds zero product value. Clerk gave me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sign-up/sign-in with Google, GitHub, and email&lt;/li&gt;
&lt;li&gt;Social login in 10 lines of code&lt;/li&gt;
&lt;li&gt;Webhook-based user sync to my Prisma database&lt;/li&gt;
&lt;li&gt;Zero maintenance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The webhook pattern is worth explaining because it's easy to get wrong:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Clerk webhook → creates or updates user in Prisma&lt;/span&gt;
&lt;span class="c1"&gt;// Runs on user creation, update, and deletion&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;evt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;verifyWebhook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// verify Clerk signature&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user.created&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;clerkId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email_addresses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;email_address&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  OpenAI — The Core Engine
&lt;/h3&gt;

&lt;p&gt;The AI does four things, each using a different prompting strategy:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. CSV parsing:&lt;/strong&gt; I send the first 20 rows of the CSV as raw text, and GPT-4o returns a JSON map of column types with date format detection and currency identification. The key insight: I don't ask the AI to parse every row — I ask it to &lt;em&gt;describe the schema&lt;/em&gt;, then use deterministic code to parse the rest. This is faster, cheaper, and more reliable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Receipt OCR:&lt;/strong&gt; I use GPT-4V (vision) to read payment screenshots. The prompt explicitly asks for structured JSON output with merchant, amount, currency, and date fields. Multiple transactions in one image are detected by asking the model to return an array.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. &lt;a href="https://tallyassistant.com/features/ai-invoice-generator" rel="noopener noreferrer"&gt;AI invoicing for freelancers&lt;/a&gt;:&lt;/strong&gt; Natural language → structured invoice. Type "Logo design $300, landing page $700 for Acme Corp, 6% VAT" and the AI extracts line items, matches the client, calculates tax, and generates a professional PDF with a PayPal payment link. The prompt converts free-text descriptions into structured invoice data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Finance chat assistant:&lt;/strong&gt; RAG-style — the user asks a question, I query their financial data from Prisma, then send the results + question to the model for summarization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Biggest AI lesson:&lt;/strong&gt; The quality of your prompt matters more than the model version. I spent more time iterating on prompt design than on any other part of the AI pipeline. A well-structured system prompt with explicit output format instructions beats a bigger model with a vague prompt every time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inngest for Background Jobs
&lt;/h3&gt;

&lt;p&gt;I needed two cron jobs: daily overdue invoice checking and sending reminder emails. Inngest handles both with step functions — each step is individually retryable and visible in the dashboard.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;checkOverdueInvoices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;inngest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;check-overdue-invoices&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;triggers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;TZ=America/New_York 0 8 * * *&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;overdue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;find-overdue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SENT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inv&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;overdue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`send-reminder-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;inv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// AI-generated reminder email via Resend&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Resend for Email
&lt;/h3&gt;

&lt;p&gt;Transactional email for invoices and payment reminders. Simple REST API, no SMTP config, excellent deliverability. One gotcha: you need to verify your domain in Resend before sending from &lt;code&gt;@yourdomain.com&lt;/code&gt; addresses — I learned this the hard way when my first batch of reminder emails silently failed.&lt;/p&gt;




&lt;h2&gt;
  
  
  The SEO Bet That Paid Off
&lt;/h2&gt;

&lt;p&gt;I knew ranking for "Tally Assistant" wouldn't be enough — nobody searches for a product they don't know exists. So I built &lt;strong&gt;60+ static landing pages&lt;/strong&gt; targeting specific search queries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;11 feature pages&lt;/strong&gt; targeting "&lt;a href="https://tallyassistant.com/features/ai-invoice-generator" rel="noopener noreferrer"&gt;AI invoicing for freelancers&lt;/a&gt;," "receipt scanner," "CSV import"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;9 industry pages&lt;/strong&gt; for "bookkeeping for Shopify sellers," "bookkeeping for photographers"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;6 comparison pages&lt;/strong&gt; for "QuickBooks alternative," "Wave alternative"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;15 glossary terms&lt;/strong&gt; for "what is accounts receivable," "invoice definition"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;4 in-depth guides&lt;/strong&gt; and &lt;strong&gt;6 free template pages&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every page uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JSON-LD structured data (FAQPage, Article, DefinedTerm schemas)&lt;/li&gt;
&lt;li&gt;Semantic HTML with proper heading hierarchy&lt;/li&gt;
&lt;li&gt;Internal linking between related content&lt;/li&gt;
&lt;li&gt;Meta descriptions written as search snippets, not afterthoughts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This compound SEO strategy cost me about 2 weeks of writing and is already generating organic traffic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three Mistakes I Made (So You Don't Have To)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. I built features nobody asked for
&lt;/h3&gt;

&lt;p&gt;I spent 3 weeks on a PDF receipt designer with drag-and-drop logo placement and custom color pickers. Beautiful. Useless. Nobody asked for it. When I finally talked to actual freelancers, they said: "Just make the CSV import work with my bank." I deleted the designer and spent that time on universal CSV parsing instead — which became the #1 feature users mention.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; Talk to users before building. I now post on Reddit r/freelance asking for feedback on every feature before writing code.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. I launched too quietly
&lt;/h3&gt;

&lt;p&gt;I spent 4 months building and 0 minutes thinking about distribution. My first launch was tweeting "I built a thing" to my 12 followers. Crickets.&lt;/p&gt;

&lt;p&gt;What actually worked: Product Hunt (scheduled for a Tuesday midnight PST), Reddit posts asking for feedback (not promoting), X replies to people complaining about bookkeeping (offering help, not links), and these SEO pages that keep bringing traffic regardless of whether I'm posting that day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; Distribution isn't an afterthought. Budget 50% of your time for it.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. I hardcoded USD as the default currency
&lt;/h3&gt;

&lt;p&gt;I built the entire multi-currency system but defaulted the base currency to USD in the reporting. A user in Germany asked: "Why is everything in dollars? I'm in Berlin." Embarrassing but easy fix — now the base currency is whatever the user configures in settings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; Don't hardcode assumptions about your users. I'm in China, my users are everywhere.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Direct integrations with Stripe, PayPal, and Wise APIs (currently CSV-based)&lt;/li&gt;
&lt;li&gt;iOS/Android companion app for receipt scanning&lt;/li&gt;
&lt;li&gt;Open sourcing the CSV parser (it's the part I'm proudest of)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try It or Ask Me Anything
&lt;/h2&gt;

&lt;p&gt;The product is free during launch: &lt;strong&gt;&lt;a href="https://tallyassistant.com" rel="noopener noreferrer"&gt;tallyassistant.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you're building a SaaS as a solo dev — or thinking about it — ask me anything in the comments. I'll respond to every question. Especially happy to talk about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prompt engineering for structured data extraction&lt;/li&gt;
&lt;li&gt;Prisma schema design for multi-tenant SaaS&lt;/li&gt;
&lt;li&gt;SEO strategy for new products&lt;/li&gt;
&lt;li&gt;The actual economics of a solo SaaS (costs, revenue, burn rate)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Built in Chengdu, China 🐼 · Next.js + Prisma + OpenAI + Clerk + Inngest + Resend&lt;/em&gt;&lt;/p&gt;

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