<?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: Haseeb Sheikh</title>
    <description>The latest articles on DEV Community by Haseeb Sheikh (@haseeb_sheikh_0f627e74ba7).</description>
    <link>https://dev.to/haseeb_sheikh_0f627e74ba7</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%2F4069738%2F47cc3978-648d-4861-a758-9b3ae94f0113.jpg</url>
      <title>DEV Community: Haseeb Sheikh</title>
      <link>https://dev.to/haseeb_sheikh_0f627e74ba7</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/haseeb_sheikh_0f627e74ba7"/>
    <language>en</language>
    <item>
      <title>No Budget to Build Your SaaS? Here's How to Get Started Anyway</title>
      <dc:creator>Haseeb Sheikh</dc:creator>
      <pubDate>Sun, 30 Aug 2026 14:37:41 +0000</pubDate>
      <link>https://dev.to/haseeb_sheikh_0f627e74ba7/no-budget-to-build-your-saas-heres-how-to-get-started-anyway-1673</link>
      <guid>https://dev.to/haseeb_sheikh_0f627e74ba7/no-budget-to-build-your-saas-heres-how-to-get-started-anyway-1673</guid>
      <description>&lt;p&gt;No Budget to Build Your SaaS? Here's How to Get Started Anyway&lt;/p&gt;

&lt;p&gt;You have a SaaS idea.&lt;/p&gt;

&lt;p&gt;You believe it can solve a real problem.&lt;/p&gt;

&lt;p&gt;But there is one major problem:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You don't have the budget to hire a development team.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That doesn't necessarily mean you have to stop.&lt;/p&gt;

&lt;p&gt;You can start by building what you can, validating the idea, and preparing a clear opportunity for someone who may want to join you as a technical partner.&lt;/p&gt;

&lt;p&gt;The goal isn't to build an entire SaaS for free.&lt;/p&gt;

&lt;p&gt;The goal is to get far enough that the right person can look at what you've done and say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I understand what you're building. I understand the opportunity. I understand what you need from me."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That clarity can be extremely valuable when your cash budget is limited.&lt;/p&gt;




&lt;h1&gt;
  
  
  Your First Job Isn't Hiring a Developer
&lt;/h1&gt;

&lt;p&gt;One of the first mistakes I see founders make is immediately searching for a developer.&lt;/p&gt;

&lt;p&gt;They have an idea and send a message like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I have a SaaS idea. I don't have a budget. Can you build it for equity?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Put yourself in the developer's position.&lt;/p&gt;

&lt;p&gt;You know almost nothing about the opportunity.&lt;/p&gt;

&lt;p&gt;You don't know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What the product does&lt;/li&gt;
&lt;li&gt;Who needs it&lt;/li&gt;
&lt;li&gt;Whether anyone wants it&lt;/li&gt;
&lt;li&gt;What has already been built&lt;/li&gt;
&lt;li&gt;How much work is required&lt;/li&gt;
&lt;li&gt;What the founder is contributing&lt;/li&gt;
&lt;li&gt;What the equity actually means&lt;/li&gt;
&lt;li&gt;Where the company is going&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You're being asked to take a significant risk based almost entirely on trust.&lt;/p&gt;

&lt;p&gt;Instead, do some of the work first.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 1: Turn Your Idea Into a Clear Product
&lt;/h1&gt;

&lt;p&gt;Before you approach anyone, answer a few basic questions.&lt;/p&gt;

&lt;h3&gt;
  
  
  What problem are you solving?
&lt;/h3&gt;

&lt;p&gt;Don't describe the technology.&lt;/p&gt;

&lt;p&gt;Describe the pain.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Small businesses spend several hours every week manually preparing reports from multiple systems."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's easier to understand than:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I'm building an AI-powered business intelligence platform."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Who has this problem?
&lt;/h3&gt;

&lt;p&gt;Be specific.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Businesses"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Try:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Small agencies with 5–20 employees."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The more specific you are, the easier it becomes to validate your idea.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does your product do?
&lt;/h3&gt;

&lt;p&gt;Explain the solution in simple language.&lt;/p&gt;

&lt;p&gt;Someone who knows nothing about your industry should understand it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why would someone pay for it?
&lt;/h3&gt;

&lt;p&gt;You don't need a perfect pricing strategy at this stage.&lt;/p&gt;

&lt;p&gt;But you should have a hypothesis.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"$49/month for small teams."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now you have something you can test.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 2: Build a Landing Page
&lt;/h1&gt;

&lt;p&gt;You don't need the complete application to start presenting your idea.&lt;/p&gt;

&lt;p&gt;Build a simple, professional landing page.&lt;/p&gt;

&lt;p&gt;It should explain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The problem&lt;/li&gt;
&lt;li&gt;Your solution&lt;/li&gt;
&lt;li&gt;Who it's for&lt;/li&gt;
&lt;li&gt;Main benefits&lt;/li&gt;
&lt;li&gt;How it works&lt;/li&gt;
&lt;li&gt;Early screenshots or mockups&lt;/li&gt;
&lt;li&gt;Call to action&lt;/li&gt;
&lt;li&gt;Contact or waitlist&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This can often be done using website builders or AI-powered tools without hiring a developer.&lt;/p&gt;

&lt;p&gt;Depending on the tools you choose, you may be able to get the first version online for free or for a few dollars.&lt;/p&gt;

&lt;p&gt;The objective isn't to impress investors.&lt;/p&gt;

&lt;p&gt;It's to make your idea &lt;strong&gt;real enough to evaluate&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 3: Build a Basic Prototype With AI
&lt;/h1&gt;

&lt;p&gt;This is where things have changed dramatically for founders.&lt;/p&gt;

&lt;p&gt;You no longer necessarily need to hire a development team just to create the first visual version of a product.&lt;/p&gt;

&lt;p&gt;AI-powered development tools can help you create basic screens and workflows.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Signup&lt;/li&gt;
&lt;li&gt;Login&lt;/li&gt;
&lt;li&gt;Forgot password&lt;/li&gt;
&lt;li&gt;Dashboard&lt;/li&gt;
&lt;li&gt;Forms&lt;/li&gt;
&lt;li&gt;Settings&lt;/li&gt;
&lt;li&gt;Basic user workflow&lt;/li&gt;
&lt;li&gt;Simple admin screens&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You might be able to create an early prototype for free or with a small monthly AI-tool budget.&lt;/p&gt;

&lt;p&gt;But remember:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A prototype is not a production-ready SaaS.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's okay.&lt;/p&gt;

&lt;p&gt;You're not trying to build the final company yourself.&lt;/p&gt;

&lt;p&gt;You're creating enough of the product to demonstrate:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"This is what I mean."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A clickable prototype can communicate more than ten pages of explanation.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 4: Talk to Potential Customers
&lt;/h1&gt;

&lt;p&gt;Don't spend months building before speaking to customers.&lt;/p&gt;

&lt;p&gt;Find people who match your target audience.&lt;/p&gt;

&lt;p&gt;Ask them about the problem.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How do you solve this today?&lt;/p&gt;

&lt;p&gt;What is frustrating about your current process?&lt;/p&gt;

&lt;p&gt;How often does this problem happen?&lt;/p&gt;

&lt;p&gt;How much time does it take?&lt;/p&gt;

&lt;p&gt;Have you paid for a solution before?&lt;/p&gt;

&lt;p&gt;What would make you switch?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Don't focus only on whether they like your idea.&lt;/p&gt;

&lt;p&gt;People are very good at saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"That's a great idea."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That doesn't mean they'll use it.&lt;/p&gt;

&lt;p&gt;Look for stronger signals.&lt;/p&gt;

&lt;p&gt;Someone asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"When can I try it?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;is more interesting.&lt;/p&gt;

&lt;p&gt;Someone asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How much will it cost?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;is even more interesting.&lt;/p&gt;

&lt;p&gt;Someone willing to become a paying customer is stronger still.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 5: Package Everything Into a Simple Presentation
&lt;/h1&gt;

&lt;p&gt;This is probably the most important step if you have little or no budget.&lt;/p&gt;

&lt;p&gt;Before approaching a technical partner, create a simple presentation.&lt;/p&gt;

&lt;p&gt;It can be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A PDF&lt;/li&gt;
&lt;li&gt;A Notion document&lt;/li&gt;
&lt;li&gt;Google Slides&lt;/li&gt;
&lt;li&gt;A short pitch deck&lt;/li&gt;
&lt;li&gt;A product brief&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It doesn't need to look like a Silicon Valley investor deck.&lt;/p&gt;

&lt;p&gt;It needs to be &lt;strong&gt;clear&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I would structure it like this:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Product
&lt;/h2&gt;

&lt;p&gt;What are you building?&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Problem
&lt;/h2&gt;

&lt;p&gt;What problem does it solve?&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Target customer
&lt;/h2&gt;

&lt;p&gt;Who will use it?&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Current solution
&lt;/h2&gt;

&lt;p&gt;How do people solve the problem today?&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Your solution
&lt;/h2&gt;

&lt;p&gt;How will your product improve the situation?&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Current progress
&lt;/h2&gt;

&lt;p&gt;What have you already done?&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Landing page completed&lt;br&gt;
Prototype completed&lt;br&gt;
25 customer interviews&lt;br&gt;
50 people interested&lt;br&gt;
5 beta users&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Whatever your numbers are, show them honestly.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Roadmap
&lt;/h2&gt;

&lt;p&gt;What do you want to build next?&lt;/p&gt;

&lt;p&gt;Keep the first version small.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. What you need
&lt;/h2&gt;

&lt;p&gt;Be extremely specific.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I'm looking for a technical co-founder to help turn the current prototype into a production-ready SaaS."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's much better than:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I need a developer."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  9. What you can offer
&lt;/h2&gt;

&lt;p&gt;This is where you need to be completely transparent.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 6: Consider Equity + a Small Stipend
&lt;/h1&gt;

&lt;p&gt;If you genuinely don't have enough money to pay market-rate compensation, you have to acknowledge that.&lt;/p&gt;

&lt;p&gt;But that doesn't mean the only option is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Work for free."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can explore a structure that works for both sides.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Equity + small stipend&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The stipend provides some immediate compensation.&lt;/p&gt;

&lt;p&gt;The equity represents long-term upside and ownership.&lt;/p&gt;

&lt;p&gt;The exact arrangement depends entirely on the situation.&lt;/p&gt;

&lt;p&gt;There is no magic percentage that applies to every startup.&lt;/p&gt;

&lt;p&gt;A technical co-founder joining at the idea stage is very different from an experienced engineer joining an MVP that already has hundreds of customers.&lt;/p&gt;

&lt;p&gt;The arrangement should consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How early the company is&lt;/li&gt;
&lt;li&gt;How much work has already been done&lt;/li&gt;
&lt;li&gt;Expected time commitment&lt;/li&gt;
&lt;li&gt;Technical responsibility&lt;/li&gt;
&lt;li&gt;Existing traction&lt;/li&gt;
&lt;li&gt;Founder contributions&lt;/li&gt;
&lt;li&gt;Expected future commitment&lt;/li&gt;
&lt;li&gt;Whether the person is a co-founder or contractor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And whatever equity arrangement you make should be properly documented with appropriate legal advice.&lt;/p&gt;

&lt;p&gt;The most important thing is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't hide the risk. Explain it.&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 7: Don't Sell Equity. Sell the Opportunity.
&lt;/h1&gt;

&lt;p&gt;This is a subtle but important difference.&lt;/p&gt;

&lt;p&gt;If your entire pitch is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I can't pay you, but I'll give you equity."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;you're focusing on what you don't have.&lt;/p&gt;

&lt;p&gt;Instead, explain what the person is joining.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"We're solving a problem for small property managers. We've interviewed 30 potential customers, built the first prototype, and have 12 businesses interested in testing it. I'm handling the business, customer development, and sales. I'm looking for a technical partner to own the product engineering side. I can offer a small monthly stipend plus equity."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now the conversation is completely different.&lt;/p&gt;

&lt;p&gt;The person can evaluate:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Founder + Problem + Market + Progress + Role + Compensation + Opportunity&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's a real decision.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 8: Show What You Are Bringing to the Table
&lt;/h1&gt;

&lt;p&gt;If you aren't technical, you still have a lot to contribute.&lt;/p&gt;

&lt;p&gt;Don't approach a technical partner as if their job is to build your idea while you simply wait.&lt;/p&gt;

&lt;p&gt;Your contribution might be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customer research&lt;/li&gt;
&lt;li&gt;Sales&lt;/li&gt;
&lt;li&gt;Marketing&lt;/li&gt;
&lt;li&gt;Industry knowledge&lt;/li&gt;
&lt;li&gt;Product strategy&lt;/li&gt;
&lt;li&gt;Partnerships&lt;/li&gt;
&lt;li&gt;Fundraising&lt;/li&gt;
&lt;li&gt;Customer support&lt;/li&gt;
&lt;li&gt;Operations&lt;/li&gt;
&lt;li&gt;Business development&lt;/li&gt;
&lt;li&gt;Vision&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A strong technical partner should feel that you're building the company &lt;strong&gt;together&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Not that they're being hired to turn your idea into reality while you sit on the sidelines.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 9: Understand the Difference Between an MVP and Production Software
&lt;/h1&gt;

&lt;p&gt;This is where many non-technical founders get confused.&lt;/p&gt;

&lt;p&gt;You may have an AI-generated prototype that looks fantastic.&lt;/p&gt;

&lt;p&gt;That's great.&lt;/p&gt;

&lt;p&gt;But production software has another layer of complexity.&lt;/p&gt;

&lt;p&gt;Someone needs to make sure things such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User accounts&lt;/li&gt;
&lt;li&gt;Data&lt;/li&gt;
&lt;li&gt;Payments&lt;/li&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;Permissions&lt;/li&gt;
&lt;li&gt;Backups&lt;/li&gt;
&lt;li&gt;Performance&lt;/li&gt;
&lt;li&gt;Reliability&lt;/li&gt;
&lt;li&gt;Error handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;are handled properly.&lt;/p&gt;

&lt;p&gt;Your first prototype doesn't need to solve every problem.&lt;/p&gt;

&lt;p&gt;But once real customers depend on your software, the standard changes.&lt;/p&gt;

&lt;p&gt;That's when experienced technical help becomes especially valuable.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 10: Don't Build Everything
&lt;/h1&gt;

&lt;p&gt;Your first SaaS version should be much smaller than your dream product.&lt;/p&gt;

&lt;p&gt;Suppose your long-term vision includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Web application&lt;/li&gt;
&lt;li&gt;Mobile apps&lt;/li&gt;
&lt;li&gt;AI assistant&lt;/li&gt;
&lt;li&gt;20 integrations&lt;/li&gt;
&lt;li&gt;Advanced analytics&lt;/li&gt;
&lt;li&gt;Automated workflows&lt;/li&gt;
&lt;li&gt;Team collaboration&lt;/li&gt;
&lt;li&gt;Enterprise permissions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don't build all of that.&lt;/p&gt;

&lt;p&gt;Ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What is the one workflow that proves this product is useful?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Build that first.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User signs up
      ↓
Creates a project
      ↓
Uses the core feature
      ↓
Gets the promised result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If that works, you have something worth improving.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 11: Use Your Limited Budget Carefully
&lt;/h1&gt;

&lt;p&gt;Having a small budget doesn't mean you should refuse to spend anything.&lt;/p&gt;

&lt;p&gt;It means you need to spend intentionally.&lt;/p&gt;

&lt;p&gt;Your first $100 might be more valuable when spent on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customer research&lt;/li&gt;
&lt;li&gt;A domain&lt;/li&gt;
&lt;li&gt;Essential software&lt;/li&gt;
&lt;li&gt;A small AI-tool subscription&lt;/li&gt;
&lt;li&gt;Early marketing experiments&lt;/li&gt;
&lt;li&gt;Professional design help&lt;/li&gt;
&lt;li&gt;Technical review&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;rather than building ten features that nobody asked for.&lt;/p&gt;

&lt;p&gt;Ask yourself:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What is the biggest uncertainty in my startup right now?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then spend money to reduce that uncertainty.&lt;/p&gt;

&lt;p&gt;If nobody wants the product, you need validation.&lt;/p&gt;

&lt;p&gt;If people want it but the prototype doesn't work reliably, you need engineering.&lt;/p&gt;

&lt;p&gt;If the product works but nobody knows about it, you need distribution.&lt;/p&gt;

&lt;p&gt;Your spending priorities should change as you learn.&lt;/p&gt;




&lt;h1&gt;
  
  
  A Simple Path for a Founder With Almost No Budget
&lt;/h1&gt;

&lt;p&gt;If I were starting from zero today, I'd think about it like this:&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 1 — Idea
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Budget: $0&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Define:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Problem&lt;/li&gt;
&lt;li&gt;Customer&lt;/li&gt;
&lt;li&gt;Solution&lt;/li&gt;
&lt;li&gt;Business model&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Stage 2 — Presentation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Budget: $0&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Product brief&lt;/li&gt;
&lt;li&gt;Pitch deck&lt;/li&gt;
&lt;li&gt;Screenshots&lt;/li&gt;
&lt;li&gt;Roadmap&lt;/li&gt;
&lt;li&gt;Team requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Stage 3 — Landing Page
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Budget: $0–$10+&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a professional website and start collecting interested users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 4 — Prototype
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Budget: $0–$10+&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use AI tools to build the first visual version.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 5 — Validation
&lt;/h3&gt;

&lt;p&gt;Talk to potential customers.&lt;/p&gt;

&lt;p&gt;Get feedback.&lt;/p&gt;

&lt;p&gt;Find beta users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 6 — Find the Technical Partner
&lt;/h3&gt;

&lt;p&gt;Present:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The product&lt;/li&gt;
&lt;li&gt;The problem&lt;/li&gt;
&lt;li&gt;Your research&lt;/li&gt;
&lt;li&gt;Your prototype&lt;/li&gt;
&lt;li&gt;Your traction&lt;/li&gt;
&lt;li&gt;Your roadmap&lt;/li&gt;
&lt;li&gt;The role&lt;/li&gt;
&lt;li&gt;The expected commitment&lt;/li&gt;
&lt;li&gt;Equity&lt;/li&gt;
&lt;li&gt;Stipend, if possible&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Stage 7 — Build Production Software
&lt;/h3&gt;

&lt;p&gt;Once the idea has enough evidence behind it, bring in experienced engineering to turn the prototype into a reliable SaaS.&lt;/p&gt;




&lt;h1&gt;
  
  
  What Made Me Say Yes to a Zero-Budget Founder
&lt;/h1&gt;

&lt;p&gt;This isn't theoretical for me.&lt;/p&gt;

&lt;p&gt;Recently, a founder approached me about helping scale their SaaS.&lt;/p&gt;

&lt;p&gt;They had essentially no budget.&lt;/p&gt;

&lt;p&gt;They were still building their team and were offering equity across technical, sales, and marketing roles.&lt;/p&gt;

&lt;p&gt;But I didn't say yes because of the equity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I said yes because of the clarity.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The founder clearly explained:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What they were building&lt;/li&gt;
&lt;li&gt;What problem they were solving&lt;/li&gt;
&lt;li&gt;What they had already accomplished&lt;/li&gt;
&lt;li&gt;What they needed&lt;/li&gt;
&lt;li&gt;What they were offering&lt;/li&gt;
&lt;li&gt;Where they wanted to go&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I didn't have to guess.&lt;/p&gt;

&lt;p&gt;I didn't have to spend hours figuring out what the opportunity was.&lt;/p&gt;

&lt;p&gt;They had done the work to make the opportunity understandable.&lt;/p&gt;

&lt;p&gt;That matters.&lt;/p&gt;




&lt;h1&gt;
  
  
  Your Lack of Budget Doesn't Have to Be Your Biggest Weakness
&lt;/h1&gt;

&lt;p&gt;You probably can't compete with a funded startup on salary.&lt;/p&gt;

&lt;p&gt;That's okay.&lt;/p&gt;

&lt;p&gt;You can compete on other things.&lt;/p&gt;

&lt;p&gt;You can show:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Preparation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ownership.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clarity.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Traction.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vision.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Commitment.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A developer might reject an opportunity with a large salary if the product, founder, or problem isn't compelling.&lt;/p&gt;

&lt;p&gt;And someone might consider an equity-based opportunity if they genuinely believe:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"This is something worth building."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That doesn't mean you should expect people to work indefinitely without compensation.&lt;/p&gt;

&lt;p&gt;It means you should create an opportunity where the risks, responsibilities, and potential rewards are clearly understood by everyone involved.&lt;/p&gt;




&lt;h1&gt;
  
  
  Don't Let "No Budget" Become "No Action"
&lt;/h1&gt;

&lt;p&gt;The worst thing you can do with a small budget is nothing.&lt;/p&gt;

&lt;p&gt;You don't need to have everything figured out.&lt;/p&gt;

&lt;p&gt;Start with what you can control.&lt;/p&gt;

&lt;p&gt;Build the landing page.&lt;/p&gt;

&lt;p&gt;Create the prototype.&lt;/p&gt;

&lt;p&gt;Talk to customers.&lt;/p&gt;

&lt;p&gt;Write the product presentation.&lt;/p&gt;

&lt;p&gt;Find out whether people care.&lt;/p&gt;

&lt;p&gt;Document what you've learned.&lt;/p&gt;

&lt;p&gt;Then approach the technical people you need.&lt;/p&gt;

&lt;p&gt;Don't say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"I have an idea. Can you build it?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Show them:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Here's what I'm building. Here's the problem. Here's what I've already done. Here's what I've learned. Here's what I need from you. Here's what I can offer. And here's where I want to take this."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a completely different conversation.&lt;/p&gt;

&lt;p&gt;When you don't have a big budget, &lt;strong&gt;clarity becomes part of your compensation package.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Budget tells people what you can pay.&lt;/p&gt;

&lt;p&gt;Clarity tells people whether they can trust the opportunity.&lt;/p&gt;

&lt;p&gt;Sometimes the right technical partner isn't looking for the biggest paycheck.&lt;/p&gt;

&lt;p&gt;They're looking for something worth building.&lt;/p&gt;

&lt;p&gt;I'm Haseeb, building SaaS products at Seebify. The full founder-focused version of this topic is here: &lt;a href="https://www.seebify.com/blog/how-to-build-saas-mvp-with-no-budget" rel="noopener noreferrer"&gt;https://www.seebify.com/blog/how-to-build-saas-mvp-with-no-budget&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>sass</category>
      <category>entrepreneurship</category>
      <category>startup</category>
    </item>
    <item>
      <title>Scaling a Node.js + PostgreSQL SaaS Backend: Pagination, Redis Caching, and Background Jobs</title>
      <dc:creator>Haseeb Sheikh</dc:creator>
      <pubDate>Tue, 25 Aug 2026 11:46:07 +0000</pubDate>
      <link>https://dev.to/haseeb_sheikh_0f627e74ba7/scaling-a-nodejs-postgresql-saas-backend-pagination-redis-caching-and-background-jobs-3m8h</link>
      <guid>https://dev.to/haseeb_sheikh_0f627e74ba7/scaling-a-nodejs-postgresql-saas-backend-pagination-redis-caching-and-background-jobs-3m8h</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuh8o58l19jvd726sgk7q.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuh8o58l19jvd726sgk7q.jpg" alt=" " width="799" height="436"&gt;&lt;/a&gt;One backend. One PostgreSQL database. An admin panel, a web app, and two mobile clients all hitting the same API.&lt;/p&gt;

&lt;p&gt;It worked fine — until it didn't. &lt;strong&gt;p99 latency crept past 4 seconds, the job queue started backing up, and the instinct was to add more servers.&lt;/strong&gt; That instinct is almost always wrong. Here's the actual sequence that fixed it, with the code — not just the theory.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Measure before you touch anything&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Don't scale based on a feeling. Track these five things before changing infrastructure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CPU/RAM&lt;/strong&gt; on your app servers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API latency&lt;/strong&gt; — p95 and p99, not the average (a 200ms average can hide a p99 of 4s)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Query latency, active connections, lock contention&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Error rate per endpoint&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Queue depth and job processing time&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A basic dashboard covering these tells you whether the problem is application, database, or queue — before you spend money on the wrong layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Make the application do less work first&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This is the cheapest scaling win, and the one most often skipped.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Cursor-based pagination instead of large offsets&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/orders&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&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;query&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;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cursor&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`SELECT * FROM orders WHERE id &amp;gt; $1 ORDER BY id LIMIT $2`&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`SELECT * FROM orders ORDER BY id LIMIT $1`&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;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cursor&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&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="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;}&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;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;nextCursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&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;p&gt;An indexed &lt;code&gt;WHERE id &amp;gt; cursor&lt;/code&gt; avoids the cost of skipping hundreds of thousands of rows just to discard them — unlike &lt;code&gt;OFFSET 50000&lt;/code&gt;, which still scans everything before it.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Kill N+1 queries&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Fetching 100 orders, then querying the customer for each one separately, turns &lt;strong&gt;1 query into 101&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Before: 1 query + 100 more in a loop&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- After: 1 query&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dangerous part: &lt;strong&gt;N+1 is invisible with 10 records and painful with 10,000.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;PostgreSQL: indexes, EXPLAIN ANALYZE, connection pooling&lt;/strong&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_orders_user_id&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="k"&gt;ANALYZE&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; shows you sequential scans, bad row estimates, and unnecessary sorts — &lt;strong&gt;don't guess why a query is slow, look.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Five app servers each opening 20 raw connections is &lt;strong&gt;100 connections fast&lt;/strong&gt;, and Postgres has a hard limit. Pool them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Pool&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pg&lt;/span&gt;&lt;span class="dl"&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;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Pool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB_HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                     &lt;span class="c1"&gt;// per instance, not global&lt;/span&gt;
  &lt;span class="na"&gt;idleTimeoutMillis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;connectionTimeoutMillis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2000&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;p&gt;For anything beyond a handful of app instances, put &lt;strong&gt;PgBouncer&lt;/strong&gt; in front of Postgres so pooling is handled centrally instead of per-process.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Redis: cache-aside, not a database&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Cache-aside is the default pattern: &lt;strong&gt;check Redis → fall back to Postgres on a miss → write through → return.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ioredis&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getDashboardSummary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&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;cacheKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`dashboard:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userId&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cached&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;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;);&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;cached&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;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cached&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;summary&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;computeDashboardSummary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// hits PostgreSQL&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 60s TTL&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;summary&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;p&gt;The hard part isn't reading from cache, it's &lt;strong&gt;invalidation&lt;/strong&gt; — when the underlying data changes, update or evict the key, or you'll serve a stale permission set long after it changed.&lt;/p&gt;

&lt;p&gt;Redis also isn't your source of truth; use it for &lt;strong&gt;caching, rate limiting, and sessions&lt;/strong&gt;, not durable business data.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Background jobs, with real retry logic&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Your API shouldn't block on report generation, PDF exports, or bulk email. Using &lt;strong&gt;BullMQ&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Worker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bullmq&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Producer — API route&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reportQueue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reports&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="na"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;redisConnection&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/reports&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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;job&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;reportQueue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;generate-report&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="na"&gt;userId&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;user&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;202&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&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="s1"&gt;processing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;jobId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;job&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="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Worker — separate process&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reports&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="nx"&gt;job&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;job&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="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;generateAndStoreReport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&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="na"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;redisConnection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;attempts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;backoff&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exponential&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5000&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;p&gt;&lt;strong&gt;Retries alone aren't enough&lt;/strong&gt; — a worker can crash &lt;em&gt;after&lt;/em&gt; sending an email but &lt;em&gt;before&lt;/em&gt; marking the job done, and the retry will fire again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;invoices&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="nx"&gt;job&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;invoiceId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;job&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alreadySent&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;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`invoice-sent:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;invoiceId&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;alreadySent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// retried, but the email already went out&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendInvoiceEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;invoiceId&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;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`invoice-sent:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;invoiceId&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;86400&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;p&gt;Without that idempotency check, a retried job means &lt;strong&gt;a duplicate invoice email in someone's inbox.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Rate limiting, tiered by plan&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;An endpoint that normally sees 100 req/min can suddenly see 20,000 — spike, bad integration, or abuse. Enforce limits consistently across instances via Redis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rateLimit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express-rate-limit&lt;/span&gt;&lt;span class="dl"&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;RedisStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rate-limit-redis&lt;/span&gt;&lt;span class="dl"&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;limiter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;rateLimit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;store&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;RedisStore&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;sendCommand&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;windowMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&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="o"&gt;=&amp;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;user&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;enterprise&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;5000&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;user&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;paid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limiter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Gotchas that don't show up until production&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;N+1 queries are invisible at small scale.&lt;/strong&gt; They only hurt once a customer has thousands of rows, by which point they're already in production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Forgotten cache invalidation&lt;/strong&gt; serves stale data silently — a user balance or permission set that's wrong for minutes or hours with no error thrown anywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In-memory session state breaks the moment you add a second node.&lt;/strong&gt; &lt;code&gt;const sessions = {}&lt;/code&gt; living in process memory means a request routed to a different instance won't see it. Sessions, cache, and uploaded files need to live in Postgres, Redis, or object storage — not app memory — before you run more than one instance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jobs without idempotency duplicate side effects on retry&lt;/strong&gt;, not just waste compute — a resent email or a double-charged webhook is a worse bug than a slow query.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Scale infrastructure last, not first&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Once the application and database are genuinely optimized: &lt;strong&gt;vertical scaling&lt;/strong&gt; (bigger machine) is simple but has a ceiling. &lt;strong&gt;Horizontal scaling&lt;/strong&gt; (more machines behind a load balancer) needs a stateless app first — shared sessions, shared cache, shared storage — or you'll get inconsistent behavior depending on which node a request hits.&lt;/p&gt;

&lt;p&gt;Skip &lt;strong&gt;Kubernetes and microservices&lt;/strong&gt; until there's a concrete reason — a workload that needs to scale independently, or a team boundary large enough to justify separate deploy lifecycles.&lt;/p&gt;

&lt;p&gt;Neither one makes a slow query fast or an unindexed table efficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Takeaway&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Measure → Optimize → Test → Scale&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Most of the wins above cost nothing but engineering time — &lt;strong&gt;pagination, an index, a cache with a sane TTL, moving slow work off the request path.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Infrastructure is the &lt;strong&gt;last lever to pull, not the first.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Haseeb — I build production SaaS backends (Node.js, PostgreSQL, Redis, Stripe) at &lt;a href="https://www.seebify.com" rel="noopener noreferrer"&gt;Seebify&lt;/a&gt;. Full write-up with the business-side reasoning and a complete stage-by-stage scaling roadmap &lt;a href="https://www.seebify.com/blog/how-to-scale-saas-practical-guide" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>postgressql</category>
      <category>backend</category>
      <category>redis</category>
    </item>
    <item>
      <title>How I Built a $0/Month Ecommerce MVP Without Shopify</title>
      <dc:creator>Haseeb Sheikh</dc:creator>
      <pubDate>Sun, 09 Aug 2026 11:15:10 +0000</pubDate>
      <link>https://dev.to/haseeb_sheikh_0f627e74ba7/how-i-built-a-0month-ecommerce-mvp-without-shopify-7hh</link>
      <guid>https://dev.to/haseeb_sheikh_0f627e74ba7/how-i-built-a-0month-ecommerce-mvp-without-shopify-7hh</guid>
      <description>&lt;p&gt;A founder came to me with a simple requirement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;100+ products&lt;/li&gt;
&lt;li&gt;Customers need to browse products&lt;/li&gt;
&lt;li&gt;Customers need to place orders through WhatsApp&lt;/li&gt;
&lt;li&gt;No online payments&lt;/li&gt;
&lt;li&gt;No monthly Shopify subscription&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The interesting part?&lt;/p&gt;

&lt;p&gt;He didn't need a traditional ecommerce platform.&lt;/p&gt;

&lt;p&gt;He needed a simple way to display products and receive orders.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;The original idea could easily turn into a large ecommerce application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User accounts&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Shopping cart&lt;/li&gt;
&lt;li&gt;Checkout&lt;/li&gt;
&lt;li&gt;Payment gateway&lt;/li&gt;
&lt;li&gt;Order management&lt;/li&gt;
&lt;li&gt;Email notifications&lt;/li&gt;
&lt;li&gt;Product management&lt;/li&gt;
&lt;li&gt;Inventory&lt;/li&gt;
&lt;li&gt;Hosting infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But most of these weren't actually required.&lt;/p&gt;

&lt;p&gt;The business only needed:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Products → Cart → WhatsApp order&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So we started there.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Built
&lt;/h2&gt;

&lt;p&gt;The customer-facing side has three main steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Browse products&lt;/li&gt;
&lt;li&gt;Add products to the cart&lt;/li&gt;
&lt;li&gt;Send the order directly to WhatsApp&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The admin panel handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding products&lt;/li&gt;
&lt;li&gt;Editing products&lt;/li&gt;
&lt;li&gt;Uploading product images&lt;/li&gt;
&lt;li&gt;Setting prices&lt;/li&gt;
&lt;li&gt;Managing inventory&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is no unnecessary checkout flow or payment system.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;

&lt;p&gt;For this MVP, we intentionally kept the architecture simple.&lt;/p&gt;

&lt;h3&gt;
  
  
  Frontend
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Next.js&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The customer website and admin interface are built with Next.js.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hosting
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Vercel&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The application can be deployed on Vercel's free tier, which removes the need for a traditional server for this MVP.&lt;/p&gt;

&lt;h3&gt;
  
  
  Database
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Supabase&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The product data is stored in Supabase using its free tier.&lt;/p&gt;

&lt;p&gt;The goal was to avoid paying for infrastructure before the business had validated the idea.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why We Didn't Build Payments
&lt;/h2&gt;

&lt;p&gt;The founder didn't need online payments.&lt;/p&gt;

&lt;p&gt;Customers were going to place their orders through WhatsApp.&lt;/p&gt;

&lt;p&gt;Adding Stripe or another payment provider would have introduced additional complexity without solving an actual business problem.&lt;/p&gt;

&lt;p&gt;So we didn't build it.&lt;/p&gt;

&lt;p&gt;The same principle applied to authentication and email notifications.&lt;/p&gt;

&lt;p&gt;If a feature isn't needed to validate the business, it can wait.&lt;/p&gt;

&lt;h2&gt;
  
  
  The $0/Month MVP
&lt;/h2&gt;

&lt;p&gt;With this architecture, the initial infrastructure cost can be &lt;strong&gt;$0/month&lt;/strong&gt; using the free tiers of Vercel and Supabase.&lt;/p&gt;

&lt;p&gt;That doesn't mean every ecommerce business can run indefinitely for free.&lt;/p&gt;

&lt;p&gt;As traffic, storage, database usage, or business requirements grow, paid infrastructure may become necessary.&lt;/p&gt;

&lt;p&gt;The goal here was different:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Validate the business before paying for infrastructure.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd Add Later
&lt;/h2&gt;

&lt;p&gt;If the business starts getting real traction, we can introduce features based on actual needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Online payments&lt;/li&gt;
&lt;li&gt;Customer accounts&lt;/li&gt;
&lt;li&gt;Order tracking&lt;/li&gt;
&lt;li&gt;Automated notifications&lt;/li&gt;
&lt;li&gt;Advanced inventory management&lt;/li&gt;
&lt;li&gt;Analytics&lt;/li&gt;
&lt;li&gt;More powerful admin features&lt;/li&gt;
&lt;li&gt;Better order management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important part is that these features can be added later.&lt;/p&gt;

&lt;p&gt;We don't need to build everything on day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bigger Lesson
&lt;/h2&gt;

&lt;p&gt;Good engineering isn't about building the biggest system possible.&lt;/p&gt;

&lt;p&gt;It's about understanding the actual problem and choosing the simplest architecture that solves it.&lt;/p&gt;

&lt;p&gt;In this case, the business didn't need Shopify.&lt;/p&gt;

&lt;p&gt;It needed a product catalog and a way to turn a customer's cart into a WhatsApp order.&lt;/p&gt;

&lt;p&gt;So that's what we built.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start with what the business actually needs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then add complexity when the business earns the right to need it.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>database</category>
      <category>sass</category>
    </item>
  </channel>
</rss>
