<?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: Sidharth Sangelia</title>
    <description>The latest articles on DEV Community by Sidharth Sangelia (@sidharth_sangelia).</description>
    <link>https://dev.to/sidharth_sangelia</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3458581%2Fc3175927-f4cd-4dad-b12f-04700b4bf241.JPG</url>
      <title>DEV Community: Sidharth Sangelia</title>
      <link>https://dev.to/sidharth_sangelia</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sidharth_sangelia"/>
    <language>en</language>
    <item>
      <title>I'm Building a Unified Freight Platform at My Part-Time Job and the Architecture Is Genuinely Interesting</title>
      <dc:creator>Sidharth Sangelia</dc:creator>
      <pubDate>Fri, 22 May 2026 08:50:07 +0000</pubDate>
      <link>https://dev.to/sidharth_sangelia/im-building-a-unified-freight-platform-at-my-part-time-job-and-the-architecture-is-genuinely-1j82</link>
      <guid>https://dev.to/sidharth_sangelia/im-building-a-unified-freight-platform-at-my-part-time-job-and-the-architecture-is-genuinely-1j82</guid>
      <description>&lt;p&gt;Let me tell you about what I have been building at my part time job, because the architecture problem at the center of it is one of the more interesting things I have worked on.&lt;/p&gt;

&lt;p&gt;I am a Pricing and Tech Associate at an international freight forwarding company. Part time. And in that capacity I am essentially responsible for the entire tech layer of the business.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Context: What Has Already Shipped
&lt;/h2&gt;

&lt;p&gt;Before I get into the big thing, the smaller deliverables first.&lt;/p&gt;

&lt;p&gt;I built the founder's personal portfolio website with a full blog system backed by Sanity CMS. He can add posts and categories without touching a single line of code. That is live and actively being used.&lt;/p&gt;

&lt;p&gt;Multiple pitch and company profile decks. The XGBoost price predictor I wrote about a few weeks back, meant to speed up freight quoting which currently happens manually. And some LinkedIn content strategy and data analytics work which I will get to.&lt;/p&gt;

&lt;p&gt;But the project I am most focused on right now is different in scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Platform
&lt;/h2&gt;

&lt;p&gt;A customer facing dashboard. Clients log in, add a booking, compare rates across multiple vendors, track shipments, and manage a wallet.&lt;/p&gt;

&lt;p&gt;The CRUD layer and the auth and the wallet are all standard enough. Solvable problems with clear paths.&lt;/p&gt;

&lt;p&gt;The genuinely hard part is the vendor integration layer.&lt;/p&gt;

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

&lt;p&gt;We work with multiple vendors. Some specialise in specific trade lanes. Some are channel partners of UPS or FedEx, meaning they can offer better rates on certain routes.&lt;/p&gt;

&lt;p&gt;Each vendor has their own API. Their own request structure for rates. Their own response shape for booking confirmations. Their own webhook format for tracking updates. Their own error codes and edge cases.&lt;/p&gt;

&lt;p&gt;And I need to build one unified platform on top of all of it.&lt;/p&gt;

&lt;p&gt;From the customer side: one clean interface. They enter shipment details, see a comparison, book, track. Clean and simple.&lt;/p&gt;

&lt;p&gt;From behind the scenes: potentially five different APIs with five completely different structures being called, normalised, and returned in a consistent shape.&lt;/p&gt;

&lt;p&gt;This is not a toy project. Real bookings, real money, real shipments. The stakes are not tutorial level.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the Adapter Pattern
&lt;/h2&gt;

&lt;p&gt;I have been thinking through the architecture for a while and I keep landing in the same place.&lt;/p&gt;

&lt;p&gt;A typed master interface in TypeScript. One canonical shape for everything.&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;FreightVendorAdapter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;getRates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CanonicalRateRequest&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CanonicalRateResponse&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nf"&gt;createBooking&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CanonicalBookingRequest&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CanonicalBookingConfirmation&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nf"&gt;getTracking&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trackingNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CanonicalTrackingEvent&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each vendor gets its own adapter class that implements this interface. The adapter does two things: translates canonical input into whatever that vendor's API actually expects, and translates whatever comes back into the canonical response shape.&lt;/p&gt;

&lt;p&gt;From the platform's perspective, every vendor behaves identically. You call &lt;code&gt;getRates&lt;/code&gt; and you get back &lt;code&gt;CanonicalRateResponse[]&lt;/code&gt;. What the adapter does internally is fully encapsulated.&lt;/p&gt;

&lt;p&gt;The TypeScript interface is not optional decoration here. It is the contract that holds the whole system together. If an adapter does not satisfy the interface, it does not compile. That is a hard guarantee, not a convention.&lt;/p&gt;

&lt;p&gt;Adding a new vendor later becomes a well defined task: implement the interface, write the translation logic, plug it in. The rest of the platform does not change.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Am Thinking About the Rollout
&lt;/h2&gt;

&lt;p&gt;Not building everything at once. That is how you build nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase one: tracking.&lt;/strong&gt; Bookings are still made manually on vendor platforms for now. But if I can surface tracking through our own interface, clients see our UI every time they check their shipment. Builds trust. Makes the platform feel real before it is fully real. Also the most self contained piece, I can implement the adapter, wire up one or two vendors, and have something useful without needing the full booking system done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase two: rate comparison.&lt;/strong&gt; Internal only first. The team uses it to quote faster. Once there is real confidence in accuracy and reliability, it opens to clients. You do not show clients something you have not stress tested yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase three: booking and wallet.&lt;/strong&gt; Later.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Things I Did Not Expect to Be Doing
&lt;/h2&gt;

&lt;p&gt;I have been doing data analytics in Excel.&lt;/p&gt;

&lt;p&gt;I say this with full awareness of what that sounds like coming from a developer. But here is what actually happened.&lt;/p&gt;

&lt;p&gt;We exported the founder's LinkedIn connection data. Thousands of connections. The goal is to segment them for a targeted email campaign. Different message for a logistics professional versus a startup founder versus a procurement manager.&lt;/p&gt;

&lt;p&gt;I learned VLOOKUP on the spot. Built filtering logic to create clean segments. And now I am looking into data enrichment tools because the raw LinkedIn export is shallow and a good email campaign needs more context than just name, company, and title.&lt;/p&gt;

&lt;p&gt;I also pushed three LinkedIn posts as part of the founder's personal content strategy. LinkedIn is its own craft. The feed is brutal and if the first line does not stop the scroll nothing else lands. I am learning this by doing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Being Inside an Industry vs Building For One
&lt;/h2&gt;

&lt;p&gt;This is the part that is hardest to convey but I think matters most.&lt;/p&gt;

&lt;p&gt;There is a real difference between building something because you theorised that a problem might exist, and building something because you watched the problem happen in front of you on a spreadsheet it should not be running on.&lt;/p&gt;

&lt;p&gt;I have been going to meetings. Visiting shipment hubs. Sitting in rooms where the operational friction is not hypothetical.&lt;/p&gt;

&lt;p&gt;When you are properly calibrated to a real context the engineering instincts feel different. You are not guessing at what matters. You can see it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Things Are
&lt;/h2&gt;

&lt;p&gt;Exams from 26th May through mid June, so there is a pause coming. But the foundation is solid and the thinking is clear.&lt;/p&gt;

&lt;p&gt;By the end of June the tracking feature should be live. Rate comparison internals after that.&lt;/p&gt;

&lt;p&gt;I will write about the adapter implementation as it comes together. There are some genuinely interesting problems in the data normalisation layer, especially around how different vendors model the same concepts (a trade lane, a weight break, an accessorial charge) in completely different ways.&lt;/p&gt;

&lt;p&gt;More on that soon.&lt;/p&gt;




&lt;p&gt;If you have built something similar with multiple third party APIs and a unified abstraction layer, I would genuinely love to hear how you structured it. Drop a comment or find me on &lt;a href="https://thesidharth.com" rel="noopener noreferrer"&gt;thesidharth.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>architecture</category>
    </item>
    <item>
      <title>New Laptop, New Job, and Two Weeks of Not Touching My Invoice App</title>
      <dc:creator>Sidharth Sangelia</dc:creator>
      <pubDate>Wed, 13 May 2026 13:05:15 +0000</pubDate>
      <link>https://dev.to/sidharth_sangelia/new-laptop-new-job-and-two-weeks-of-not-touching-my-invoice-app-26l2</link>
      <guid>https://dev.to/sidharth_sangelia/new-laptop-new-job-and-two-weeks-of-not-touching-my-invoice-app-26l2</guid>
      <description>&lt;p&gt;So it has been roughly two weeks since my last post and a lot happened. Not in a "shipped three features" way. More in a "life moved fast and I was just trying to keep up" kind of way.&lt;/p&gt;

&lt;p&gt;Let me just get into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  I Got a MacBook Air M5
&lt;/h2&gt;

&lt;p&gt;This one still feels a bit surreal to write.&lt;/p&gt;

&lt;p&gt;I did not ask for it. My parents saw me at a point where they felt confident enough to say okay, this is a good investment. And they just got it. The MacBook Air M5.&lt;/p&gt;

&lt;p&gt;I have been building on a setup that was honestly not great for a while now. So when I opened the box I genuinely did not know how to react. It felt less like buying a laptop and more like someone placing a bet on you. That kind of thing does something to you. Makes you not want to waste it.&lt;/p&gt;

&lt;h2&gt;
  
  
  I Took a Part Time Job
&lt;/h2&gt;

&lt;p&gt;This is the bigger news.&lt;/p&gt;

&lt;p&gt;I joined an international freight forwarding company as a Pricing and Tech Associate. Part time. And before you ask, yes I know that is a strange combination of words for a job title. That is sort of the point.&lt;/p&gt;

&lt;p&gt;I want to be clear about why I chose this over an internship. An internship is where you learn. A job, even a part time one, is where you implement. There is a real difference. In an internship someone is usually holding your hand through the process. Here there are live business problems and someone actually expects me to solve them. That felt more valuable to me right now.&lt;/p&gt;

&lt;h2&gt;
  
  
  I Have Been Going to Shipment Hubs
&lt;/h2&gt;

&lt;p&gt;My boss has been taking me around to understand the industry from the ground up. I have been to the Aramex hub. I am going to the FedEx hub soon.&lt;/p&gt;

&lt;p&gt;I know this sounds completely disconnected from everything I usually write about. But hear me out.&lt;/p&gt;

&lt;p&gt;I have been learning things like port to port delivery vs door to door. How a shipment actually gets processed. The steps involved. The players. The friction points. And I think this is genuinely important because we as software engineers can only build things that actually help people when we understand the problem. Not the surface version of the problem. The real version. The one you only see when you are inside it.&lt;/p&gt;

&lt;p&gt;The best micro SaaS ideas do not come from browsing Product Hunt. They come from being inside an industry and noticing the thing that makes no sense or takes way too long or still runs on a spreadsheet when it should not.&lt;/p&gt;

&lt;p&gt;I have only been here a few weeks and I am already noticing things.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Job Title Means I Do Everything
&lt;/h2&gt;

&lt;p&gt;This is either a feature or a bug depending on how you look at it.&lt;/p&gt;

&lt;p&gt;I am building a website with a CMS. I am doing LinkedIn content strategy for the business. I am thinking through growth strategy. And I have been tasked with building a price predictor system.&lt;/p&gt;

&lt;p&gt;That last one is the most technically interesting.&lt;/p&gt;

&lt;p&gt;Right now the biggest friction point for the business is response time. A client asks for a quote. The team has to manually figure out the best price across different carriers, routes, and conditions. It takes time. And in freight forwarding, slow response time costs you deals.&lt;/p&gt;

&lt;p&gt;So my job is to build something that gives the best price as fast as possible.&lt;/p&gt;

&lt;p&gt;My first instinct was linear regression. Made sense on paper. I have some data, I want to predict a number, sounds like a regression problem. But after digging into it I quickly realized that was not going to work well here. Freight pricing has too many variables, too many non-linear relationships, too much going on for a simple linear model to capture it properly.&lt;/p&gt;

&lt;p&gt;After going down a proper rabbit hole with AI and other resources I landed on XGBoost. It handles mixed data types well, it is strong on tabular data, and it does not need a massive dataset to start giving reasonable outputs. I had never worked with ML before this job. Now I am building an ML model. That is a strange sentence to write but here we are.&lt;/p&gt;

&lt;h2&gt;
  
  
  I Have Also Been Building N8N Automations
&lt;/h2&gt;

&lt;p&gt;Part of simplifying internal processes means automating the repetitive stuff. I have been setting up workflows in n8n to handle things that were previously done manually. Nothing I can go into detail on right now but it is good practice and it is directly solving real problems for real people inside a real business. That hits differently than building a personal project.&lt;/p&gt;

&lt;h2&gt;
  
  
  What About Invoicepedia
&lt;/h2&gt;

&lt;p&gt;Honestly? I have not touched it in two weeks.&lt;/p&gt;

&lt;p&gt;And I am not going to pretend that is fine or spin it into some lesson about rest. It just happened. Life got full and the invoice app sat there.&lt;/p&gt;

&lt;p&gt;But I am not worried about it. The foundation is solid, I know what I want to build next, and I will get back to it.&lt;/p&gt;

&lt;p&gt;If anything, this job is making me think harder about what Invoicepedia should eventually become. Because now I am watching a real business deal with real operational friction every day. That is going to make the product thinking sharper when I come back to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Honest Bit
&lt;/h2&gt;

&lt;p&gt;I know the conventional advice is to go deep on one thing. Pick a lane. Build expertise in a vertical.&lt;/p&gt;

&lt;p&gt;And I get that. I genuinely do.&lt;/p&gt;

&lt;p&gt;But I am also at an age where I think variety is the better bet. Not scattered, unfocused variety. Intentional variety. I am doing case competitions. I am building software. I am inside a logistics company learning how a real industry operates. Each of those things is teaching me something the others cannot.&lt;/p&gt;

&lt;p&gt;I have been competing in case competitions for a while now and there is always this gap between the recommendations you put in a slide deck and what actually happens when you try to execute them. This job is closing that gap. There are actual stakes. Actual outcomes. A client either gets their quote fast or they do not.&lt;/p&gt;

&lt;p&gt;That is worth something even if it does not fit neatly on a resume.&lt;/p&gt;

&lt;p&gt;My boss has worked at Aramex, UPS, FedEx, Jeena, DG Group. The kind of experience that takes decades to build. I am sitting next to that and asking questions. I would be an idiot not to.&lt;/p&gt;




&lt;p&gt;If you are a freight or logistics person and you have thoughts on XGBoost for pricing models I would genuinely love to hear them. This is new territory for me.&lt;/p&gt;

&lt;p&gt;And if you want the longer, more personal version of what this past two weeks has felt like, &lt;a href="https://www.thesidharth.com/blog/part-time-job-macbook" rel="noopener noreferrer"&gt;I wrote about it on my personal blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Will keep posting. Back to building soon.&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>webdev</category>
      <category>typescript</category>
      <category>career</category>
    </item>
    <item>
      <title>Auth Flows, Onboarding Traps, and Why AI Still Needs a Human to Think</title>
      <dc:creator>Sidharth Sangelia</dc:creator>
      <pubDate>Thu, 30 Apr 2026 16:21:42 +0000</pubDate>
      <link>https://dev.to/sidharth_sangelia/auth-flows-onboarding-traps-and-why-ai-still-needs-a-human-to-think-4do0</link>
      <guid>https://dev.to/sidharth_sangelia/auth-flows-onboarding-traps-and-why-ai-still-needs-a-human-to-think-4do0</guid>
      <description>&lt;p&gt;So this week was one of those weeks where you go in thinking you'll fix one small thing and come out three days later having redesigned half the app's architecture.&lt;/p&gt;

&lt;p&gt;Worth it though. Here's what actually happened.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem I Was Ignoring
&lt;/h2&gt;

&lt;p&gt;My invoice PDF templates were broken. Not broken like crashing, broken like useless. The generated PDFs had placeholder company info, no real sender details, nothing a freelancer could actually send to a client. Just four templates sitting there looking pretty and being completely non-functional.&lt;/p&gt;

&lt;p&gt;The fix was obvious: capture the user's company details somewhere and use them in the PDF.&lt;/p&gt;

&lt;p&gt;But that opened a whole other question: where in the flow do you capture this? You can't just shove a form in front of someone before they've even seen the product. That's the fastest way to kill your conversion rate.&lt;/p&gt;

&lt;p&gt;So I decided to redesign the auth and onboarding flow entirely.&lt;/p&gt;




&lt;h2&gt;
  
  
  The New Flow
&lt;/h2&gt;

&lt;p&gt;Old flow: sign up → land on dashboard → stare at an empty page.&lt;/p&gt;

&lt;p&gt;New flow: &lt;strong&gt;create your invoice first, auth later.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The user hits the app, clicks "Create Invoice," fills in the form, sees a live preview of their PDF, all without signing up. When they hit submit, &lt;em&gt;that's&lt;/em&gt; when we ask for auth. And immediately after auth, if they haven't gone through onboarding, we collect their company details before anything else.&lt;/p&gt;

&lt;p&gt;It sounds simple but it required rethinking quite a bit of how the middleware works.&lt;/p&gt;

&lt;p&gt;The logic being: let people see value before asking them to commit. Once they've built something they actually want to send, they're invested. That's the moment to ask for an account. Not before.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Onboarding Status Problem (and Why the AI Kept Getting It Wrong)
&lt;/h2&gt;

&lt;p&gt;This is the part that actually frustrated me the most this week, and I want to be honest about it because I think it's important.&lt;/p&gt;

&lt;p&gt;Once you have an onboarding flow, you need to check: has this user gone through it or not? Seems simple. It isn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 1:&lt;/strong&gt; Hit the database every time. Check for a row. If it's there, they're onboarded.&lt;/p&gt;

&lt;p&gt;This is the obvious answer. It's also terrible. You'd be doing an unnecessary DB read on every single protected route, every middleware call, every page load. That's a lot of wasted reads for information that barely changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 2:&lt;/strong&gt; Use Clerk's user metadata. After onboarding, write &lt;code&gt;onboarded: true&lt;/code&gt; to the user's public metadata. From then on, you read it from the JWT, no DB call needed.&lt;/p&gt;

&lt;p&gt;This is the right answer. But it's harder to implement correctly, and the JWT update isn't instant, which creates a brief window where the token still shows the old state.&lt;/p&gt;

&lt;p&gt;Here's the frustrating part: I was using AI to help me through the bugs I hit with option 2. And multiple times, when I got stuck and asked "can we try a simpler approach?", the AI would just... give me database middleware code. Every time. Just casually suggest checking the DB on every request like it wasn't a problem.&lt;/p&gt;

&lt;p&gt;It doesn't know your scale concerns. It doesn't know you're trying to keep reads low. It optimises for "working code" not "well-architected code." And if you don't push back, you end up with the wrong solution wrapped in a green checkmark.&lt;/p&gt;

&lt;p&gt;This is something I keep coming back to: &lt;strong&gt;AI is a powerful tool but it needs someone who knows what they're trying to build.&lt;/strong&gt; If you don't have the mental model of what good looks like, the AI will lead you somewhere technically correct and architecturally wrong.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bug Fixes That Were Quietly Breaking Everything
&lt;/h2&gt;

&lt;p&gt;While I was in there, I caught a few bugs that had been hiding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;user&lt;/code&gt; was &lt;code&gt;undefined&lt;/code&gt; in &lt;code&gt;ModernInvoice.tsx&lt;/code&gt; because &lt;code&gt;InvoicePDFButton&lt;/code&gt; wasn't receiving or passing &lt;code&gt;user&lt;/code&gt; down to &lt;code&gt;InvoicePDFDialog&lt;/code&gt;. Classic prop drilling miss.&lt;/li&gt;
&lt;li&gt;There was a hardcoded &lt;code&gt;"Your Company"&lt;/code&gt; string that was a leftover from early dev. It had been sitting there, overriding real data, the whole time.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;InvoiceData&lt;/code&gt; type in &lt;code&gt;InvoicePDFButton&lt;/code&gt; was missing &lt;code&gt;customerId&lt;/code&gt;, causing a mismatch with &lt;code&gt;InvoiceForPDF&lt;/code&gt;. Fixed by dropping the local type and just reusing &lt;code&gt;InvoiceForPDF&lt;/code&gt; directly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these were glamorous. But fixing them made the PDF output actually correct for the first time.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Shift in How I'm Thinking About This
&lt;/h2&gt;

&lt;p&gt;Somewhere in the middle of all this, I stopped thinking like a developer and started thinking like a product person.&lt;/p&gt;

&lt;p&gt;I've been asking myself a lot of "should this even be here?" questions. What does the user actually need to see first? What am I making them do that they shouldn't have to? What am I hiding that they need?&lt;/p&gt;

&lt;p&gt;The create invoice flow is a good example. I spent a while thinking about whether to use a stepper (step 1 → step 2 → step 3) or a single split-panel view with the form on the left and a live PDF preview on the right.&lt;/p&gt;

&lt;p&gt;Steppers feel clean but they're wrong for this. Invoice fields are deeply interdependent: change the line items and the total changes, change the currency and the format changes, change the due date and the payment terms might need to update. A stepper forces you to pretend those dependencies don't exist. A split panel lets you see everything at once and makes the relationships obvious.&lt;/p&gt;

&lt;p&gt;I'm also thinking more carefully about what to reveal and when. The template picker, for example, should be a modal that opens when you click "Create Invoice", not a separate page, not a step in a form. Each template card should have a "Preview sample" link that opens a fullscreen overlay. Once you pick one, you land on the builder with that template already applied.&lt;/p&gt;

&lt;p&gt;Small decisions. But they compound into an experience that either feels thoughtful or feels like someone just strung features together.&lt;/p&gt;




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

&lt;p&gt;The onboarding flow captures company name, address, and basic details for now. No logo yet, because that would mean setting up S3 or Uploadthing and that's a whole project in itself. It's on the list but not the priority.&lt;/p&gt;

&lt;p&gt;Next up is getting the create invoice flow to its final form. The split-panel builder with live preview, a template switcher pill in the header so you can swap templates without losing your form data, and the success animation: button morphs to a checkmark, then confetti, then redirect to the invoice detail page.&lt;/p&gt;

&lt;p&gt;Also planning to add auto-save to localStorage every 30 seconds as a safety net. Nothing worse than filling in an invoice, your browser hiccups, and you lose everything.&lt;/p&gt;




&lt;p&gt;I'll keep posting updates as things come together. If you've been following along, thank you genuinely, it helps more than you'd think. This the detailed post on my &lt;a href="https://www.thesidharth.com/blog/auth-onboarding-ai-traps" rel="noopener noreferrer"&gt;personal blog&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;And if you've hit the "AI keeps suggesting the wrong architecture" wall before, I'd love to hear how you dealt with it. Drop it in the comments.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>typescript</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>I Did Market Research on Invoice Apps, and Now I Have More Questions Than Answers</title>
      <dc:creator>Sidharth Sangelia</dc:creator>
      <pubDate>Fri, 24 Apr 2026 19:10:58 +0000</pubDate>
      <link>https://dev.to/sidharth_sangelia/i-did-market-research-on-invoice-apps-and-now-i-have-more-questions-than-answers-1d09</link>
      <guid>https://dev.to/sidharth_sangelia/i-did-market-research-on-invoice-apps-and-now-i-have-more-questions-than-answers-1d09</guid>
      <description>&lt;p&gt;So if you read my last post, you know I'm rebuilding Invoicepedia. My old broken invoice app that had both Drizzle and Prisma installed at the same time and a delete button that sometimes just... crashed everything.&lt;/p&gt;

&lt;p&gt;I decided before writing a single line of new code that I should actually find out if there is a real gap here. Not a "I think there is a gap" gap. An actual, people-are-complaining-about-this gap.&lt;/p&gt;

&lt;p&gt;So I spent a few days going down Reddit rabbit holes. r/QuickBooks, r/waveapps, r/Bookkeeping, r/smallbusiness. Reading actual user complaints. Not the landing page copy. The raw, frustrated, typing-in-caps kind of posts that people write at 11pm when their invoice software just blocked their account.&lt;/p&gt;

&lt;p&gt;Here is what I found.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Complaints Are All the Same Underneath
&lt;/h2&gt;

&lt;p&gt;QuickBooks: people are drowning in features they never use and paying $30+ a month for the privilege. The word that kept coming up was "overwhelmed." One person called it unnecessary complexity for someone who just wants to send an invoice and get paid.&lt;/p&gt;

&lt;p&gt;FreshBooks: the horror stories here were something else. Accounts blocked days after payment. Payment information trapped inside the platform. One person literally wrote "I advise small businesses to steer clear." That is not a product review. That is a warning.&lt;/p&gt;

&lt;p&gt;Wave: used to be free. Slowly started charging for things. The free tier now cannot fully automate recurring invoices. One thread from August 2025 had someone saying "Wave is in serious trouble" and switching to Zoho just to get reliable bank feeds.&lt;/p&gt;

&lt;p&gt;Invoice Ninja: this one hurt a little because I actually tried it once. The UI looks like a cockpit. I am not exaggerating. I opened it, felt genuinely confused, and closed the tab. The Reddit threads confirm this. Long-term paying customers upgrading to enterprise and not getting the features they paid for. One person writing "AVOID AT ALL TIMES" in capitals.&lt;/p&gt;

&lt;p&gt;Zoho: missing basic integrations. Support tickets going a week without resolution.&lt;/p&gt;

&lt;p&gt;The summary is this. Every single major player has users who are actively looking for an exit. Not because they want more features. Because the basics are broken or too expensive or too complicated or all three at once.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Gap Is Real But Is It Big Enough
&lt;/h2&gt;

&lt;p&gt;Here is the thing that kept nagging at me after all this research.&lt;/p&gt;

&lt;p&gt;The gap is obvious. Freelancers need something dead simple. No accounting bloat. No surprise fee hikes. No cockpit UI. Just: create invoice, send invoice, track who paid, get paid.&lt;/p&gt;

&lt;p&gt;But the market also shows something a little discouraging. Users are exhausted. They cycle from QuickBooks to Wave to Zoho and back. There is no dominant winner. Which either means the gap is real and unsolved or it means the gap is a graveyard where many products have already tried and quietly died.&lt;/p&gt;

&lt;p&gt;I genuinely do not know which one it is yet.&lt;/p&gt;

&lt;p&gt;What I do know is that the complaints are consistent enough that building something focused on doing the basics really well is not a crazy idea. It might just be a very slow one.&lt;/p&gt;




&lt;h2&gt;
  
  
  So Here Is What I Decided
&lt;/h2&gt;

&lt;p&gt;I am not going to wait until I have all the answers. I have been learning that waiting for certainty is just procrastination dressed up as planning.&lt;/p&gt;

&lt;p&gt;My partner and I are going to run a hackathon-style sprint on this. Intense. Time-crunched. We will ship buggy code probably. But we will have something working end to end. PDF generation, email sending, a dashboard that actually shows you useful numbers when you open it.&lt;/p&gt;

&lt;p&gt;The goal is not to be FreshBooks. The goal is not to be QuickBooks. The goal is to be the thing a freelancer opens, uses in ten minutes, and does not feel like they need a tutorial to understand.&lt;/p&gt;

&lt;p&gt;Five real users. That is the metric. Not five hundred. Five.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bigger Question I Keep Avoiding
&lt;/h2&gt;

&lt;p&gt;I am going to be honest here because that is the only reason I write these posts.&lt;/p&gt;

&lt;p&gt;I am an economics student. Not a CS degree holder. I have been building projects for a while now and I am getting better. But the job market is rough and AI is making everyone question everything. I watched Harnoor Singh's &lt;a href="https://www.youtube.com/watch?v=oSu5DtQS5LM" rel="noopener noreferrer"&gt;video&lt;/a&gt; recently where he talks about how even someone with NCR and Microsoft on their resume is having difficulty finding a role right now.&lt;/p&gt;

&lt;p&gt;If someone with that kind of experience is struggling, what does that mean for someone like me with just projects and self-taught skills?&lt;/p&gt;

&lt;p&gt;I wrote a longer version of what I am thinking on this on my personal blog if you want to read that side of it: &lt;strong&gt;&lt;a href="https://www.thesidharth.com/blog/before-i-write-code-is-this-worth-building" rel="noopener noreferrer"&gt;link to portfolio post&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But the short version is this. I think the era of DSA grind plus projects to land a FAANG job is shifting. The wave now is building. Micro SaaS. Shipping end to end. And maybe AI has compressed this timeline faster than anyone expected.&lt;/p&gt;

&lt;p&gt;Rebuilding Invoicepedia is teaching me more about product thinking than any tutorial ever did. Not just code. Thinking about why something should exist. Who it is for. What it should not do.&lt;/p&gt;

&lt;p&gt;That feels worth something even if I cannot put it on a resume yet.&lt;/p&gt;




&lt;h2&gt;
  
  
  Questions for You
&lt;/h2&gt;

&lt;p&gt;If you are a freelancer, what invoicing tool are you using right now and what is the one thing you wish it did differently? Drop it in the comments.&lt;/p&gt;

&lt;p&gt;If you are building something similar or have done the freelancer SaaS space, I would love to know what you ran into. What was harder than expected? What surprised you?&lt;/p&gt;

&lt;p&gt;And if you are also a self-taught dev or non-CS-background person navigating this market, I genuinely want to hear how you are thinking about it. I do not think I am the only one asking these questions to myself late at night.&lt;/p&gt;

&lt;p&gt;Will keep posting updates as we sprint through this build.&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>nextjs</category>
      <category>typescript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I'm Rebuilding My Old Invoice App and This Time I Actually Know What I'm Doing</title>
      <dc:creator>Sidharth Sangelia</dc:creator>
      <pubDate>Sun, 19 Apr 2026 19:12:33 +0000</pubDate>
      <link>https://dev.to/sidharth_sangelia/im-rebuilding-my-old-invoice-app-and-this-time-i-actually-know-what-im-doing-4bg7</link>
      <guid>https://dev.to/sidharth_sangelia/im-rebuilding-my-old-invoice-app-and-this-time-i-actually-know-what-im-doing-4bg7</guid>
      <description>&lt;p&gt;So I have this old project called Invoicepedia. Built it like 6 months ago when I was just starting to learn full stack. Next.js, Clerk, Neon, basic CRUD. The usual "I just learned how to connect a database" kind of project.&lt;/p&gt;

&lt;p&gt;I opened it recently and honestly it was rough. Delete button crashed the app sometimes. You could submit an empty invoice form with no validation. The Stripe integration was half working. I had both Drizzle and Prisma installed at the same time for some reason. Classic.&lt;/p&gt;

&lt;p&gt;I almost deleted it and moved on to something new.&lt;/p&gt;

&lt;p&gt;Instead I decided to actually fix it and turn it into something real.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Not Just Start a New Project
&lt;/h2&gt;

&lt;p&gt;Every time I start something new I spend like 3-4 days just on setup. Auth, database, environment variables, middleware, all that stuff before you even write a single line of actual product code. And by the time I'm done with setup I've already lost interest or I hit some weird error and just abandon the whole thing.&lt;/p&gt;

&lt;p&gt;Invoicepedia already had all of that. It was broken but the foundation was there. Auth worked. Database was connected. Routes existed.&lt;/p&gt;

&lt;p&gt;So I thought okay, why not just fix this one instead of starting from zero again. Worst case I learn something. Best case I actually ship something.&lt;/p&gt;




&lt;h2&gt;
  
  
  First Thing I Did Was Stop Adding Features
&lt;/h2&gt;

&lt;p&gt;Old me would have immediately started adding new stuff. Charts. Dark mode. AI features. Whatever sounded cool.&lt;/p&gt;

&lt;p&gt;This time I made a list of everything that was broken and fixed that first. No new features until the existing stuff actually worked properly.&lt;/p&gt;

&lt;p&gt;Removed dead dependencies. Rewrote the schema. Added proper Zod validation to every server action. Changed hard deletes to soft deletes because you should never hard delete financial records. Fixed the middleware. Added actual error handling.&lt;/p&gt;

&lt;p&gt;It took a week and nothing visually changed. The app looked the same. But after that week I wasn't scared to touch the codebase anymore and that felt different.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'm Actually Trying to Build
&lt;/h2&gt;

&lt;p&gt;Yeah I know. Another invoice app. There's like 50 of them already.&lt;/p&gt;

&lt;p&gt;But here's the thing — most of them are either too expensive or too complicated for a normal freelancer. FreshBooks charges you $19-30 a month and limits your clients on the cheaper plans. Wave was free and then slowly started charging for things that used to be free. Invoice Ninja has so many features that the UI looks like a cockpit, a normal person opens it and immediately closes the tab.&lt;/p&gt;

&lt;p&gt;The people I want to use this are freelancers. Developers, designers, writers. People who just did some work, need to send an invoice, and want to get paid without learning accounting software.&lt;/p&gt;

&lt;p&gt;So the whole idea is just — do the basic things really well. Don't try to be QuickBooks. Don't try to be FreshBooks. Just be the thing a freelancer needs to send professional invoices and track who's paid them and who hasn't.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'm Shipping First
&lt;/h2&gt;

&lt;p&gt;I made a list of like 10 features I want to build eventually. AI invoice generation, automated payment reminders, client portal, recurring invoices, expense tracking. The full thing.&lt;/p&gt;

&lt;p&gt;Then I looked at that list and realised if I wait until all of that is done I will never ship anything.&lt;/p&gt;

&lt;p&gt;So the first version is going to have three things:&lt;/p&gt;

&lt;p&gt;PDF generation — because an invoice app that can't produce a PDF is just a form.&lt;/p&gt;

&lt;p&gt;Email sending — so you can actually send the invoice to your client from inside the app instead of downloading it and attaching it to Gmail manually.&lt;/p&gt;

&lt;p&gt;A proper dashboard — right now it's just a table. I'm adding three metric cards at the top so when you open the app you see immediately how much you got paid this month, how much is still outstanding, and how many invoices are overdue. That's actually useful information.&lt;/p&gt;

&lt;p&gt;That's it. Ship those three. Get some users. See what they actually want next.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Goal is 5 Users
&lt;/h2&gt;

&lt;p&gt;Not 500. Not 5000. Five real people who use it and tell me what's wrong with it.&lt;/p&gt;

&lt;p&gt;I'll post updates as I build. What I'm adding, what's breaking, what people say when they actually try it. I don't have it all figured out, I'm figuring it out as I go.&lt;/p&gt;

&lt;p&gt;If you're a freelancer who's annoyed at paying too much for invoicing software, keep an eye on this. If you're a dev learning full stack, hopefully this is useful to see what an actual messy in-progress project looks like compared to the clean tutorial versions.&lt;/p&gt;




&lt;p&gt;Stack is Next.js 15, Prisma, NeonDB, Clerk, Tailwind, shadcn/ui.&lt;/p&gt;

&lt;p&gt;Happy to answer questions in the comments if anything here was interesting or you want to know more about specific decisions I made.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>typescript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>HTTP Status Codes: A Developer's Real-World Guide🚦</title>
      <dc:creator>Sidharth Sangelia</dc:creator>
      <pubDate>Wed, 03 Sep 2025 16:46:18 +0000</pubDate>
      <link>https://dev.to/sidharth_sangelia/http-status-codes-a-developers-real-world-guide-3gd4</link>
      <guid>https://dev.to/sidharth_sangelia/http-status-codes-a-developers-real-world-guide-3gd4</guid>
      <description>&lt;p&gt;Hey fellow developers! 👋&lt;/p&gt;

&lt;p&gt;Last week, I spent three hours debugging what I thought was a complex authentication issue, only to discover it was a simple 422 error that I completely misunderstood. That frustrating evening made me realize how many of us know the "famous" status codes (hello, 404!) but struggle with the nuanced ones that could save us hours of debugging.&lt;/p&gt;

&lt;p&gt;So here's my attempt to break down HTTP status codes in a way that actually sticks. No dry documentation here – just real scenarios we face every day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wait, Why Should I Care About Status Codes?
&lt;/h2&gt;

&lt;p&gt;Before we dive in, let me tell you why this matters. Last month, our API was returning 200 OK for failed operations (yeah, I know...), and our frontend team was going crazy trying to figure out why their error handling wasn't working. The fix? Proper status codes. It's not just about following standards – it's about making your life and your team's life easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Categories That Actually Matter
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2xx - The "Everything's Fine" Family ✅
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;200 OK - The Reliable Friend&lt;/strong&gt;This is your bread and butter. GET request for user data? 200. Successful login? 200. But here's the thing – don't use 200 for everything just because it works.&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="c1"&gt;// Good use of 200&lt;/span&gt;
&lt;span class="nx"&gt;GET&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;
&lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="nx"&gt;OK&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;

&lt;span class="c1"&gt;// Bad use of 200 &lt;/span&gt;
&lt;span class="nx"&gt;POST&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nf"&gt;users &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;creating&lt;/span&gt; &lt;span class="nx"&gt;a&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;Response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="nx"&gt;OK&lt;/span&gt; &lt;span class="c1"&gt;// Should be 201!  &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;201 Created - The Overachiever&lt;/strong&gt;I used to ignore this one until I realized how much cleaner my API responses became. Use 201 when you've successfully created something new. Your frontend developers will thank you because they can differentiate between "I got existing data" and "I just created something new."&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="c1"&gt;// Perfect 201 usage&lt;/span&gt;
&lt;span class="nx"&gt;POST&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&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="s2"&gt;My awesome post&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="s2"&gt;content&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="s2"&gt;Some content here&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nl"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt; &lt;span class="nx"&gt;Created&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;456&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&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="s2"&gt;My awesome post&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="s2"&gt;created_at&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="s2"&gt;2024-01-15T10:30:00Z&lt;/span&gt;&lt;span class="dl"&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;204 No Content - The Strong Silent Type&lt;/strong&gt;This one's beautiful for DELETE operations and updates where you don't need to send data back. Clean, efficient, and tells the client "job done, nothing more to say."&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="c1"&gt;// Elegant 204 usage&lt;/span&gt;
&lt;span class="nx"&gt;DELETE&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;
&lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;204&lt;/span&gt; &lt;span class="nx"&gt;No&lt;/span&gt; &lt;span class="nx"&gt;Content&lt;/span&gt;
&lt;span class="c1"&gt;// No response body needed - the status says it all &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3xx - The "Look Elsewhere" Family 🔄
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;301 vs 302 - The Redirect Siblings&lt;/strong&gt;These two confused me for years. Here's how I remember them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;301&lt;/strong&gt;: "This moved permanently, update your bookmarks" (like when you change your domain)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;302&lt;/strong&gt;: "This moved temporarily, keep using the old URL" (like maintenance pages)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Real talk: Most of the time you'll use 301 for SEO-friendly redirects and 302 for temporary stuff.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;304 Not Modified - The Bandwidth Saver&lt;/strong&gt;This little gem enables caching magic. When implemented correctly, it can dramatically speed up your app. The client asks "has this changed since last time?" and you respond "nope, use your cached version."&lt;/p&gt;

&lt;h3&gt;
  
  
  4xx - The "You Messed Up" Family ❌
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;400 Bad Request - The Vague Complainer&lt;/strong&gt;This is the generic "something's wrong with your request" response. But here's a pro tip: always include helpful error messages in the response body.&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="c1"&gt;// Bad 400 response&lt;/span&gt;
&lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="nx"&gt;Bad&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid request&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;// Good 400 response&lt;/span&gt;
&lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="nx"&gt;Bad&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&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="s2"&gt;Validation failed&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="s2"&gt;details&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&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="s2"&gt;Email format is invalid&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="s2"&gt;password&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="s2"&gt;Password must be at least 8 characters&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;401 vs 403 - The Permission Police&lt;/strong&gt;This distinction trips up so many developers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;401 Unauthorized&lt;/strong&gt;: "Who are you? Please log in."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;403 Forbidden&lt;/strong&gt;: "I know who you are, but you can't do this."&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of 401 as a bouncer asking for ID, and 403 as the same bouncer saying "sorry, members only."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;404 Not Found - The Celebrity&lt;/strong&gt;Everyone knows this one, but here's something interesting: sometimes returning 404 is a security feature. Instead of returning 403 for a private resource (which tells attackers it exists), return 404 to keep things secret.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;422 Unprocessable Entity - The Perfectionist&lt;/strong&gt;This one saved my sanity. Use 422 when the request is well-formed but semantically incorrect. Like when someone submits a perfectly formatted JSON with an email that passes regex validation but the domain doesn't exist.&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="c1"&gt;// 422 vs 400 example&lt;/span&gt;
&lt;span class="nx"&gt;POST&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&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="s2"&gt;user@fakemaindomain.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Valid format, fake domain&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="c1"&gt;// Semantically wrong&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nl"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;422&lt;/span&gt; &lt;span class="nx"&gt;Unprocessable&lt;/span&gt; &lt;span class="nx"&gt;Entity&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&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="s2"&gt;Invalid user data&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="s2"&gt;details&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&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="s2"&gt;Domain does not exist&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="s2"&gt;age&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="s2"&gt;Age cannot be negative&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5xx - The "I Messed Up" Family 💥
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;500 Internal Server Error - The Nightmare&lt;/strong&gt;The universal "something went wrong on our end." Never let your users see these in production without proper error handling and logging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;502 Bad Gateway - The Middleman Problem&lt;/strong&gt;This usually means your server is trying to talk to another server (database, external API, microservice) and that conversation failed. Super common with microservices architecture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;503 Service Unavailable - The Planned Outage&lt;/strong&gt;Perfect for maintenance windows. Include a Retry-After header to tell clients when to try again.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Debugging Stories You'll Relate To
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Case of the Mysterious 502
&lt;/h3&gt;

&lt;p&gt;Last month, our checkout process started throwing 502s randomly. Turns out, our payment service was timing out, but our API gateway was returning 502 instead of 504. The fix? Proper timeout configuration and using 504 (Gateway Timeout) for actual timeout scenarios.&lt;/p&gt;

&lt;h3&gt;
  
  
  The 200 That Lied
&lt;/h3&gt;

&lt;p&gt;We had a "successful" user creation endpoint that always returned 200 OK, even when the email already existed. The frontend team was showing success messages for failed registrations. The lesson? Don't be afraid to use 409 (Conflict) for duplicate resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Authentication Maze
&lt;/h3&gt;

&lt;p&gt;Our mobile app was stuck in redirect loops because we were using 302 for OAuth redirects. Mobile apps don't handle redirects the same way browsers do. Switch to proper API responses with tokens instead of relying on redirects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Status Codes in the Real World
&lt;/h2&gt;

&lt;h3&gt;
  
  
  REST API Best Practices
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// User Management API&lt;/span&gt;
&lt;span class="nx"&gt;GET&lt;/span&gt;    &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;     &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;POST&lt;/span&gt;   &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;     &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="nx"&gt;created&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;GET&lt;/span&gt;    &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="nx"&gt;found&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="nx"&gt;found&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;PUT&lt;/span&gt;    &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="nx"&gt;found&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;DELETE&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="mi"&gt;204&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;deleted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="nx"&gt;found&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Error Handling Patterns
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Frontend error handling&lt;/span&gt;
&lt;span class="nf"&gt;fetch&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/users&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;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;body&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;userData&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;showSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User created successfully!&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;else&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;422&lt;/span&gt;&lt;span class="p"&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;response&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;showValidationErrors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;details&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;else&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;409&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;showError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User already exists&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="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;showError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Something went wrong. Please try again.&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Tools That'll Save Your Sanity
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Browser DevTools
&lt;/h3&gt;

&lt;p&gt;Your best friend for debugging. The Network tab shows status codes for every request. Pro tip: right-click and "Copy as cURL" to test APIs directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Postman/Insomnia
&lt;/h3&gt;

&lt;p&gt;Perfect for API testing. Set up collections with different scenarios and expected status codes.&lt;/p&gt;

&lt;h3&gt;
  
  
  curl Commands
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check just the status code&lt;/span&gt;
curl &lt;span class="nt"&gt;-I&lt;/span&gt; https://api.example.com/users

&lt;span class="c"&gt;# Follow redirects&lt;/span&gt;
curl &lt;span class="nt"&gt;-L&lt;/span&gt; https://example.com/old-url

&lt;span class="c"&gt;# Show response headers and status&lt;/span&gt;
curl &lt;span class="nt"&gt;-v&lt;/span&gt; https://api.example.com/users 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Quirky Ones Worth Knowing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;418 I'm a Teapot&lt;/strong&gt; ☕Started as an April Fools' joke but is now used by some APIs to detect automated requests. Some developers use it for rate limiting with a sense of humor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;451 Unavailable for Legal Reasons&lt;/strong&gt;Named after the book "Fahrenheit 451," used when content is blocked due to legal restrictions. More relevant than ever in our current internet landscape.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Pitfalls I've Seen (And Made)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The "Everything is 200" Syndrome
&lt;/h3&gt;

&lt;p&gt;Don't do this. If something failed, don't return 200 with an error message in the body. Your HTTP client libraries are built to handle different status codes – use them!&lt;/p&gt;

&lt;h3&gt;
  
  
  Ignoring Response Bodies for Errors
&lt;/h3&gt;

&lt;p&gt;A 400 status code without explanation is useless. Always include helpful error messages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mixing Authentication and Authorization
&lt;/h3&gt;

&lt;p&gt;401 for authentication failures, 403 for authorization failures. Keep them separate in your head and your code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Not Monitoring Status Codes
&lt;/h3&gt;

&lt;p&gt;Set up alerts for unusual patterns in your status codes. A sudden spike in 5xx errors might indicate a problem before users start complaining.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Your Status Codes
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Best example for API testing&lt;/span&gt;
&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User API&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return 201 when creating user&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&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;request&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="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/users&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="nf"&gt;send&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&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="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return 422 for invalid email&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&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;request&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="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/users&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="nf"&gt;send&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;invalid-email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;422&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Status codes aren't just numbers – they're a communication protocol between your server and clients. Getting them right makes debugging easier, improves user experience, and makes your APIs more predictable.&lt;/p&gt;

&lt;p&gt;The next time you're building an API endpoint, take a moment to think: "What's the most appropriate status code for this scenario?" Your future self (and your teammates) will thank you.&lt;/p&gt;

&lt;p&gt;What's your most encountered status code? Mine's probably 422 – I'm apparently really good at sending semantically incorrect requests to my own APIs! 😅&lt;/p&gt;

&lt;p&gt;Drop a comment below with your status code war stories. We've all been there, and sharing these experiences helps us all become better developers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Useful Resources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml" rel="noopener noreferrer"&gt;HTTP Status Codes Registry&lt;/a&gt; - The official source&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://httpstatusdogs.com/" rel="noopener noreferrer"&gt;HTTP Status Dogs&lt;/a&gt; - Because learning should be fun&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status" rel="noopener noreferrer"&gt;MDN HTTP Status Codes&lt;/a&gt; - Comprehensive documentation&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>api</category>
      <category>backend</category>
    </item>
    <item>
      <title>From Web to Mobile: Completing My React Native Journey with Maximilian Schwarzmüller</title>
      <dc:creator>Sidharth Sangelia</dc:creator>
      <pubDate>Sat, 30 Aug 2025 12:04:59 +0000</pubDate>
      <link>https://dev.to/sidharth_sangelia/from-web-to-mobile-completing-my-react-native-journey-with-maximilian-schwarzmuller-4l</link>
      <guid>https://dev.to/sidharth_sangelia/from-web-to-mobile-completing-my-react-native-journey-with-maximilian-schwarzmuller-4l</guid>
      <description>&lt;p&gt;After weeks of dedication and hands-on coding, I’ve successfully completed Maximilian Schwarzmüller’s comprehensive React Native course on Udemy. As someone with a solid background in web development, this transition to mobile app development has been both challenging and incredibly rewarding.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Little Backstory
&lt;/h2&gt;

&lt;p&gt;I’ve always wanted to learn app development. Back in Class 12, I ambitiously tried setting up Android Studio, running the simulator, and building a simple app. What followed was an entire month of battling with environment variables, Java configurations, and errors I barely understood.&lt;/p&gt;

&lt;p&gt;By the end of that month, I honestly questioned whether Android development was really for me. The complexity of the setup alone felt like a huge barrier to entry.&lt;/p&gt;

&lt;p&gt;Looking back now, I can say this: &lt;strong&gt;React Native is a fantastic entry point into the mobile ecosystem.&lt;/strong&gt; It lowers the barrier, lets you leverage existing React skills, and still leaves room to grow into full native development later as the need arises.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Course Experience
&lt;/h2&gt;

&lt;p&gt;Maximilian’s teaching approach struck the perfect balance between practical implementation and conceptual understanding. Rather than overwhelming students with theory, he focused on building real applications while explaining the “why” behind each decision. This hands-on methodology made complex mobile development concepts much more digestible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Learning Milestones
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Understanding the Component Ecosystem
&lt;/h3&gt;

&lt;p&gt;One of the first major shifts was adapting to React Native’s component structure. Gone are the familiar HTML elements like div, p, and a. Instead, I learned to work with React Native’s specific components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;View instead of div&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Text for all text content&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;TouchableOpacity for interactive elements&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Image for media display&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This wasn’t just a syntax change — it represented a fundamental shift in how mobile interfaces are constructed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Navigation Patterns
&lt;/h3&gt;

&lt;p&gt;Web development relies heavily on URLs and browser history, but mobile apps follow different patterns. The course covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Stack Navigator&lt;/strong&gt;: For hierarchical navigation where users drill down into content&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tab Navigator&lt;/strong&gt;: For parallel sections of an app&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Drawer Navigator&lt;/strong&gt;: For side menu functionality&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each navigation pattern serves specific user experience goals that don’t directly translate from web development.&lt;/p&gt;

&lt;h3&gt;
  
  
  Native Device Integration
&lt;/h3&gt;

&lt;p&gt;Perhaps the most exciting aspect of React Native is its ability to access native device features. The course introduced me to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Camera functionality&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Location services&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Device storage&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Push notifications (overview)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even in their simplest form, these integrations showed me the true power of mobile development — creating apps that live closer to users’ daily lives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reinforced React.js Skills
&lt;/h2&gt;

&lt;p&gt;An unexpected benefit was how React Native deepened my React.js knowledge. Concepts like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Component lifecycle management&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;State management patterns&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Props and data flow&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hooks usage&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;… all became clearer when applied in a mobile context. It felt like practicing the same music scales on a different instrument — the repetition in a new setting gave me fresh perspective.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges and Realizations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Learning Curve
&lt;/h3&gt;

&lt;p&gt;While my React background helped, mobile development brought unique challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Understanding platform-specific behaviors (iOS vs Android)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Handling different screen sizes and orientations&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Adapting to mobile-first interaction patterns&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Debugging on physical devices vs simulators&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Beyond the Basics
&lt;/h3&gt;

&lt;p&gt;The course laid a strong foundation, but I know this is just the start. Areas I plan to explore further include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Native module configuration&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Advanced state management in mobile contexts&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Performance optimization for mobile devices&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Looking Ahead
&lt;/h2&gt;

&lt;p&gt;This course gave me the confidence to start building mobile apps, while also showing me how much more there is to learn.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Immediate Next Steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Deep dive into push notifications&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Exploring advanced native modules&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Building more complex apps independently&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Understanding app deployment and store submissions&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Long-term Goals:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Mastering mobile-specific UX patterns&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Platform-specific optimizations&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cross-platform best practices&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mobile analytics and performance monitoring&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Advice for Fellow Web Developers
&lt;/h2&gt;

&lt;p&gt;If you’re considering moving from web to mobile development:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Embrace the differences&lt;/strong&gt; — don’t force web patterns into mobile contexts&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Start with the fundamentals&lt;/strong&gt; — get comfortable with React Native’s components&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test on real devices&lt;/strong&gt; — simulators are great, but real-world usage is different&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Be patient with native features&lt;/strong&gt; — setup and testing can be tricky, but rewarding&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Completing this React Native course feels like opening a door into an entirely new dimension of development. Web will always be my foundation, but mobile offers unique opportunities to create apps that integrate seamlessly into people’s daily routines.&lt;/p&gt;

&lt;p&gt;The journey from web to mobile isn’t just about syntax or frameworks — it’s about understanding how people interact with technology in a more personal way.&lt;/p&gt;

&lt;p&gt;Huge thanks to Maximilian Schwarzmüller for putting together such a structured and practical course. For anyone on the fence about learning React Native, his teaching makes the transition much less intimidating.&lt;/p&gt;

&lt;p&gt;This is just the beginning of my mobile dev journey — and I can’t wait to see where it leads. 🚀 I will be sharing more of my learnings on &lt;a href="https://notebook.thesidharth.com" rel="noopener noreferrer"&gt;notebook.thesidharth.com&lt;/a&gt; soon.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What about you? Have you ever tried transitioning between development platforms? Share your story — I’d love to hear it.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>react</category>
      <category>learning</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Building My First AI-Powered App: From Whisper to Vercel Limits</title>
      <dc:creator>Sidharth Sangelia</dc:creator>
      <pubDate>Thu, 28 Aug 2025 19:17:50 +0000</pubDate>
      <link>https://dev.to/sidharth_sangelia/building-my-first-ai-powered-app-from-whisper-to-vercel-limits-1cf</link>
      <guid>https://dev.to/sidharth_sangelia/building-my-first-ai-powered-app-from-whisper-to-vercel-limits-1cf</guid>
      <description>&lt;p&gt;I thought building AI apps would be about training models and complex machine learning stuff, but as I dug deeper, I found out - why reinvent the wheel when you can solve 70-80% of tasks with existing models?&lt;/p&gt;

&lt;p&gt;Turns out, the real challenge wasn't the AI part. It was everything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Idea: What I Wanted to Build
&lt;/h2&gt;

&lt;p&gt;I'd been consuming tons of great video content on YouTube (both long and short form) and Instagram, and kept thinking: "This would make such a good blog post for better readability and SEO reach."&lt;/p&gt;

&lt;p&gt;So I came up with what seemed like a simple solution - a NextJS 15 web app where users could:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Upload a video&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Get AI-generated transcript using OpenAI's Whisper-1&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Transform it into SEO-optimized content using ChatGPT/Gemini&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Download a ready-to-publish blog post&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;How hard could it be, right? OpenAI already has Whisper for transcription, other LLM models are readily available through APIs. Just connect the dots and boom - instant article creation.&lt;/p&gt;

&lt;p&gt;Plus, the blogging and content creation industry is booming. I thought if I could build this, maybe I could create some recurring revenue like the folks on Twitter always talk about.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠️ The Tech Stack (What I Thought I Needed)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Frontend &amp;amp; Backend:&lt;/strong&gt; Next.js seemed like the obvious choice - I could handle both frontend and backend with API routes and server actions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI Processing:&lt;/strong&gt; OpenAI's Whisper-1 for transcription, GPT-4/Gemini for content optimization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hosting:&lt;/strong&gt; Vercel, because... well, it's the easiest deployment ever.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;File handling:&lt;/strong&gt; This became a whole saga (more on this below).&lt;/p&gt;

&lt;p&gt;Here's what I thought the flow would look like:&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="c1"&gt;// What I thought the flow would look like&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;processVideo&lt;/span&gt; &lt;span class="o"&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;file&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;transcription&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;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transcriptions&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;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;whisper-1&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;blogPost&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;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completions&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;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gpt-4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Turn this into an SEO blog: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;transcription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&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="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;blogPost&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;Spoiler alert: It wasn't that easy.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚡ First Reality Check: The 25MB File Upload Nightmare
&lt;/h2&gt;

&lt;p&gt;My initial plan was to use UploadThing (Theo's creation) since it had a generous free tier. But then I discovered Whisper-1 has a 25MB file limit per upload.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Most decent-quality videos are way larger than 25MB.&lt;/p&gt;

&lt;p&gt;In development, I could easily test with small videos under 25MB, but for a production app that people would actually pay for? This was a major roadblock.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution attempt #1:&lt;/strong&gt; Extract audio from video (audio files are much smaller than video files).&lt;/p&gt;

&lt;p&gt;I found ImageKit.io, which could handle uploads, compression, and audio extraction. Perfect! I ditched UploadThing and dove into ImageKit's documentation.&lt;/p&gt;

&lt;p&gt;Spent hours implementing the audio extraction feature, writing code, testing locally. Everything looked good. Then came the moment of truth - testing the actual audio file extraction.&lt;/p&gt;

&lt;p&gt;Nothing worked.&lt;/p&gt;

&lt;p&gt;I added console.logs everywhere, thinking it was a code issue. After hours of debugging, I realized the problem: I'd exhausted ImageKit's free plan just from testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The kicker:&lt;/strong&gt; ImageKit's paid plan costs $80/month and provides way less processing power than I'd need for a robust app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution attempt #2:&lt;/strong&gt; Use FFmpeg on my own server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New problem:&lt;/strong&gt; I'm a student without money for servers.&lt;/p&gt;

&lt;p&gt;So I decided to postpone the audio extraction feature and just put a strict 25MB limit for now. "I'll figure this out later," I told myself.&lt;/p&gt;

&lt;h2&gt;
  
  
  💻 The API Integration That Worked... Until It Didn't
&lt;/h2&gt;

&lt;p&gt;I implemented all the API integrations and functions. Everything worked beautifully on localhost. The transcription was accurate, the content optimization was decent, and I was feeling pretty good about myself.&lt;/p&gt;

&lt;p&gt;Time to deploy and show my friends!&lt;/p&gt;

&lt;p&gt;I deployed to Vercel, shared the link with excitement, and... nothing worked.&lt;/p&gt;

&lt;p&gt;The app would start processing, show a loading state, and then just timeout with a 504 error.&lt;/p&gt;

&lt;p&gt;After a lot of head-scratching and debugging, I found the culprit: &lt;strong&gt;Vercel's function timeout limits&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;My processing pipeline was taking 60+ seconds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;File upload: ~10 seconds&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Audio processing: ~20 seconds&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Whisper transcription: ~30 seconds&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;GPT optimization: ~15 seconds&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Google search time:&lt;/strong&gt; "vercel function timeout limit"&lt;/p&gt;

&lt;p&gt;The harsh reality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hobby plan:&lt;/strong&gt; 10 seconds&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pro plan:&lt;/strong&gt; 60 seconds&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enterprise:&lt;/strong&gt; 15 minutes&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Quick math:&lt;/strong&gt; My processing takes 60+ seconds. Even the Pro plan wouldn't save me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bigger realization:&lt;/strong&gt; This isn't just a Vercel problem. AWS Lambda has 15-minute limits, Netlify has similar constraints. Serverless functions aren't meant for long-running tasks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❌ What I Built:  User Upload → Vercel 
   Function → Process (60s) → Timeout.

✅ What I Actually Needed:  User Upload → Queue 
   Job → Background Worker → Notify User   

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🤔 Understanding the Real Problem
&lt;/h2&gt;

&lt;p&gt;Here's what I learned about serverless architecture the hard way:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Serverless functions are great for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Fast responses (under 30 seconds)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Auto-scaling&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Quick API endpoints&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Simple data processing&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Serverless functions are terrible for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Long-running AI processing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;File manipulation tasks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Complex workflows&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Anything that takes time&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The fundamental issue:&lt;/strong&gt; I was trying to fit a long-running AI workflow into a request-response architecture. That's like trying to fit a truck through a car door.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔧 Solution Discovery: Enter Background Jobs
&lt;/h2&gt;

&lt;p&gt;Back to Google: "background jobs nodejs", "async processing for AI apps"&lt;/p&gt;

&lt;p&gt;I discovered several options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Inngest&lt;/strong&gt; - Developer-friendly, good free tier&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;BullMQ&lt;/strong&gt; - Redis-based, more complex setup&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AWS SQS&lt;/strong&gt; - Powerful but overkill for my needs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Redis Queue&lt;/strong&gt; - DIY approach&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I chose &lt;strong&gt;Inngest&lt;/strong&gt; because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;It handles the infrastructure for me&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Great developer experience&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Built-in retries and error handling&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Free tier was sufficient for testing&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's the new architecture:&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;inngest&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./inngest/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// API endpoint just queues the job (fast!)&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/process-video&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;jobId&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;inngest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;video/process&lt;/span&gt;&lt;span class="dl"&gt;"&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;videoUrl&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;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;videoUrl&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;body&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="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;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Processing started! We'll notify you when it's ready.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;jobId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;checkStatusAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`/api/status/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;jobId&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="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Actual processing happens in background (no time limits!)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;processVideo&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;process-video&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;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;video/process&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;event&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;// Now I can take all the time I need&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transcription&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;processWithWhisper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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;videoUrl&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;blog&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;optimizeWithGPT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transcription&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Notify user when done&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;notifyUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;blog&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;p&gt;&lt;strong&gt;The difference:&lt;/strong&gt; Instead of making users wait for 60+ seconds (and timing out), I immediately return a "we're processing it" response and handle the heavy lifting in the background.&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 What I Actually Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Architecture Matters More Than I Thought
&lt;/h3&gt;

&lt;p&gt;You can't just throw AI processing into a standard web app architecture and expect it to work. AI apps have fundamentally different requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Long processing times&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unpredictable resource usage&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Need for progress tracking&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Error handling for expensive operations&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Research Infrastructure Constraints First
&lt;/h3&gt;

&lt;p&gt;I should have researched deployment limitations &lt;strong&gt;before&lt;/strong&gt; writing a single line of code. Now I know to ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;What are the timeout limits?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How much memory/CPU can I use?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What happens if processing fails halfway through?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How do I handle user notifications?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Hardest Part Isn't the AI
&lt;/h3&gt;

&lt;p&gt;I thought the challenging parts would be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Getting good transcriptions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Optimizing content for SEO&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fine-tuning prompts&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Actually challenging parts:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;File upload and processing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Background job orchestration&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;User experience for async operations&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Error handling and retries&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Infrastructure costs and scaling&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Local Development Can Be Misleading
&lt;/h3&gt;

&lt;p&gt;Everything worked perfectly on my MacBook Pro. But production environments have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Stricter resource limits&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Network latency&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Timeout constraints&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Different error conditions&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; Test in production-like environments early and often.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔮 What I'm Building Next
&lt;/h2&gt;

&lt;p&gt;I'm currently rebuilding the entire app with background jobs as a first-class citizen:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New tech stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Frontend:&lt;/strong&gt; Still Next.js, but with real-time progress indicators&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Background jobs:&lt;/strong&gt; Inngest for orchestration&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Database:&lt;/strong&gt; Adding Supabase for job status tracking&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;File storage:&lt;/strong&gt; Moving to Cloudinary for better video handling&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Notifications:&lt;/strong&gt; WebSocket connections for real-time updates&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Timeline reality check:&lt;/strong&gt; What I thought would be a 2-week project is now a 3-month learning journey. And honestly? I'm more excited about it now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Other technologies I discovered:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Langchain &amp;amp; Langraph:&lt;/strong&gt; For more complex AI workflows&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Redis:&lt;/strong&gt; For caching and session management&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;WebSocket/Server-sent events:&lt;/strong&gt; For real-time progress updates&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Queue monitoring tools:&lt;/strong&gt; For debugging background jobs&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  💭 Advice for Other Developers
&lt;/h2&gt;

&lt;p&gt;If you're building AI-powered apps, here's what I wish someone had told me:&lt;/p&gt;

&lt;h3&gt;
  
  
  Plan for Async from Day One
&lt;/h3&gt;

&lt;p&gt;Don't build a synchronous AI app and try to make it async later. Design your user experience around the fact that AI processing takes time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Show progress indicators&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Send email/push notifications when jobs complete&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Let users check status later&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Handle failures gracefully&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Research Your Platform's Limits
&lt;/h3&gt;

&lt;p&gt;Before you write any code, understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Function timeout limits&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Memory constraints&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;File size restrictions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pricing for overages&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Start with Background Jobs
&lt;/h3&gt;

&lt;p&gt;Even if your AI processing is currently fast, it will get slower as you add features. Background jobs give you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Better user experience&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Easier scaling&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Retry mechanisms&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Progress tracking&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Infrastructure Is Harder Than AI
&lt;/h3&gt;

&lt;p&gt;Getting good results from OpenAI's APIs is pretty straightforward. Getting those results reliably delivered to users in production? That's the real challenge.&lt;/p&gt;

&lt;h3&gt;
  
  
  It's Totally Normal
&lt;/h3&gt;

&lt;p&gt;If you're struggling with infrastructure for AI apps, you're not alone. Every AI developer goes through this learning curve. The AI part is often the easy part.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What about you?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Have you hit similar serverless limitations while building AI apps? What solutions did you find?&lt;/p&gt;

&lt;p&gt;Are you currently building something with AI? What infrastructure challenges are you facing?&lt;/p&gt;

&lt;p&gt;Drop a comment below - I'd love to hear about your experiences and maybe we can help each other avoid these pitfalls!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Currently rebuilding this app the right way. Follow my journey as I document everything I learn about building production-ready AI applications.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>openai</category>
      <category>vercel</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
