<?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: DivyanshuLohani</title>
    <description>The latest articles on DEV Community by DivyanshuLohani (@divyanshulohani).</description>
    <link>https://dev.to/divyanshulohani</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%2F763132%2F37c61118-cff2-42c3-ab2b-910cf50840a1.jpeg</url>
      <title>DEV Community: DivyanshuLohani</title>
      <link>https://dev.to/divyanshulohani</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/divyanshulohani"/>
    <language>en</language>
    <item>
      <title>My 2025</title>
      <dc:creator>DivyanshuLohani</dc:creator>
      <pubDate>Thu, 01 Jan 2026 18:04:58 +0000</pubDate>
      <link>https://dev.to/divyanshulohani/my-2025-ljj</link>
      <guid>https://dev.to/divyanshulohani/my-2025-ljj</guid>
      <description>&lt;p&gt;2024 was about proving things to myself that I could build, learn fast, and keep shipping projects. 2025, on the other hand, was about &lt;strong&gt;understanding what it actually means to build software&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This year wasn’t flashy. It didn’t revolve around frameworks or buzzwords. It was messy, practical, and occasionally uncomfortable. I shipped code to production, dealt with real failures, made architectural decisions that had consequences, and learned lessons that no tutorial could’ve taught me.&lt;/p&gt;

&lt;p&gt;These are not lessons I learned by reading, they’re lessons I learned by &lt;em&gt;doing&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stepping Outside the Screen: Real-World Opportunities
&lt;/h2&gt;

&lt;p&gt;This year I finished high school and also got into actual real world things, for the longest time, my entire relationship with tech existed behind a screen but after my boards I had very much free time to go and explore the real tech world.&lt;/p&gt;

&lt;p&gt;This year, I attended my &lt;strong&gt;first ever tech fest&lt;/strong&gt;: &lt;strong&gt;DevFest by&lt;/strong&gt; &lt;a href="https://dev.to/in/gdg-ranchi-128833360/"&gt;GDG Ranchi&lt;/a&gt; . It might sound small on paper, but for me, it was a turning point. Seeing developers, speakers, and students in the same physical space made something click that online learning never quite did.&lt;/p&gt;

&lt;p&gt;Until then, tech had felt like a solo pursuit me, my pc and an internet connection. DevFest showed me the &lt;em&gt;human side&lt;/em&gt; of software: people sharing failures, debating decisions, and talking about systems they’ve actually broken and fixed in production.&lt;/p&gt;

&lt;p&gt;What surprised me most wasn’t the scale of the event, but the realization that I belonged there. I wasn’t “too early” or “underprepared.” I understood the problems being discussed, asked better questions, and left with a stronger sense of direction.&lt;/p&gt;

&lt;p&gt;This experience taught me that learning doesn’t only happen through code it also happens through exposure. Being in the same room as people building real things pushes you to raise your own standards. It made me want to ship better software, think more responsibly, and stop treating projects as disposable experiments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production Is a Different Game Entirely
&lt;/h2&gt;

&lt;p&gt;Thanks to this habbit of writing blogs I got quite lucky I secured my first clients to work as a freelance web developer and take my skills to the real world. For a long time, I treated “shipping” as the finish line. If the app worked locally, deployed successfully, and didn’t crash immediately, I considered it done (I knew few edge cases but left it there because it was just a casual project). 2025 completely destroyed that illusion. This year I dealt with apps made for real users, real people using them, bugs that weren't just there all along but left unspotted due to manual testing and treating the app with care.&lt;/p&gt;

&lt;p&gt;Shipping code to production is not an extension of side projects, it’s a different discipline altogether. Suddenly, every decision carries weight you can't just break things and push to prod or like get a quick change. Logs matter a lot I mean a lot things can go wrong in the frontend as well as backend. Rollbacks matter. Downtime matters. Even small changes feel heavier when real users depend on them. One wrong mistake and your whole app is down for minutes cosing you a lot.&lt;/p&gt;

&lt;p&gt;In casual projects, mistakes are learning opportunities. In production, mistakes are responsibilities. A broken feature isn’t just “oops” it’s someone else’s workflow interrupted, someone's dilevery failed, data potentially affected, or trust slightly eroded.&lt;/p&gt;

&lt;p&gt;This year taught me that production software demands a mindset shift:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  You think twice before deploying.&lt;/li&gt;
&lt;li&gt;  You test flows you previously ignored.&lt;/li&gt;
&lt;li&gt;  You plan for failure instead of assuming success.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The biggest change wasn’t technical — it was psychological. I stopped asking &lt;em&gt;“Will this work?”&lt;/em&gt; and started asking &lt;em&gt;“What happens when this breaks?”&lt;/em&gt; "What more ways a user can perform this action to break things" these questions alone made me a better engineer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vulnerabilities Don’t Care About Your User Count
&lt;/h2&gt;

&lt;p&gt;One of the most uncomfortable lessons of 2025 was realizing how exposed even a small application can be. I used to believe that security becomes important &lt;em&gt;after&lt;/em&gt; you have users. That belief didn’t survive this year.&lt;/p&gt;

&lt;p&gt;Major vulnerabilities surfaced that directly affected production apps including one that took my own app down. It didn’t matter that my user base wasn’t massive. It didn’t matter that the app wasn’t popular. Once something is publicly accessible, it becomes a target.&lt;/p&gt;

&lt;p&gt;The most painful realization came at the end of the year when a critical CVE hit the ecosystem I was using. My production app was suddenly vulnerable, and the damage wasn’t theoretical it was immediate and real.&lt;/p&gt;

&lt;p&gt;That experience taught me hard rules I won’t forget:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Never run applications as root in production — &lt;em&gt;ever&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;  Assume every service will be scanned, probed, and tested.&lt;/li&gt;
&lt;li&gt;  Security is not an “enterprise problem”; it’s a baseline responsibility.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Security stopped being an abstract concept for me in 2025. It became personal. And once that happens, you never build the same way again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Projects That Shaped How I Think About Software
&lt;/h2&gt;

&lt;p&gt;2025 wasn’t about the number of projects I built, it was about how each project forced me to confront a different kind of complexity. Every serious project this year changed the way I reason about systems, trade-offs, and responsibility.&lt;/p&gt;

&lt;p&gt;One of the earliest was a &lt;strong&gt;grocery delivery app&lt;/strong&gt;, inspired by products like Blinkit. On the surface, it looked straightforward: users, products, carts, orders. In reality, it introduced me to time-sensitive workflows, state transitions, and operational pressure. Orders aren’t just data — they’re promises. Late updates, inconsistent states, or missing edge cases immediately translate into broken trust. You can read more about this project &lt;a href="https://linkedin.com/pulse/10-minute-grocery-delivery-app-challenges-tech-stack-key-lohani-mwizc?lipi=urn%3Ali%3Apage%3Ad_flagship3_profile_self_edit_featured_add_article%3B9PRJxT2mTvSzb3SAhHOQ1w%3D%3D" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Later, I went deep into &lt;strong&gt;live streaming, transcoding, and video processing&lt;/strong&gt;. This was a completely different mental model. I learned that video systems are not frontend problems —they’re infrastructure problems. Encoding formats, transcoding pipelines, latency, and resource usage all matter more than UI polish. This exploration reshaped how I think about performance and scalability. more about this &lt;a href="https://www.linkedin.com/pulse/going-live-localhost-nginx-rtmp-module-divyanshu-lohani-twrdc" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finflow&lt;/strong&gt;, my expense tracker, evolved significantly this year. What started as a web app grew into a system with major updates and a &lt;strong&gt;Flutter mobile app&lt;/strong&gt; that synced data with the web. Handling consistency across platforms taught me that shared state is one of the hardest problems in software. UI is forgiving — data corruption is not.&lt;/p&gt;

&lt;p&gt;When &lt;strong&gt;GitHub Actions were blocked&lt;/strong&gt; for me, I didn’t wait. I built a &lt;strong&gt;simple CI/CD pipeline&lt;/strong&gt; myself. It wasn’t fancy, but it worked — and more importantly, it forced me to understand deployments at a lower level. When the tooling disappeared, the learning multiplied.&lt;/p&gt;

&lt;p&gt;Payments were another major theme. I integrated &lt;strong&gt;multiple payment gateways&lt;/strong&gt;, explored different verification patterns, callback flows, retries, and failure handling. Payments taught me that correctness matters more than elegance. You don’t optimize for beauty when money is involved — you optimize for certainty.&lt;/p&gt;

&lt;p&gt;I briefly explored &lt;strong&gt;Flutter more deeply&lt;/strong&gt;, but eventually realized my long-term focus was better aligned with &lt;strong&gt;Expo and JavaScript-based ecosystems&lt;/strong&gt;. That detour wasn’t wasted — it clarified my direction.&lt;/p&gt;

&lt;p&gt;By the end of the year, I was building something bigger: a &lt;strong&gt;platform-style app inspired by Hago and WePlay&lt;/strong&gt;. Not just a game, but a system — users, sessions, state, and real-time interaction.&lt;/p&gt;

&lt;p&gt;Every project added a new layer to how I think. Not “how do I build this?” but “what breaks first, and why?”&lt;/p&gt;

&lt;h2&gt;
  
  
  ReadmeHub and Remembering Why I Build
&lt;/h2&gt;

&lt;p&gt;I vibe coded a simple app to try out vibe coding but also spread a positive message around the world a small, almost sarcastic project aimed at calling out fake participation during Hacktoberfest. It was for the developers who show up only for the T-shirt, not the contribution.&lt;/p&gt;

&lt;p&gt;The app wasn’t complex. It didn’t introduce new architectural challenges. But it served a purpose it let me build something opinionated, fast, and honest without worrying about polish or metrics.&lt;/p&gt;

&lt;p&gt;That mattered more than I expected.&lt;/p&gt;

&lt;p&gt;ReadmeHub reminded me that building can still be playful. That not every project needs to become a product. Some projects exist just to say something, explore an idea, or scratch an itch.&lt;/p&gt;

&lt;p&gt;In a year full of responsibility, production pressure, and real consequences, this small project helped me reconnect with why I started building in the first place.&lt;/p&gt;

&lt;p&gt;You can checkit out at: &lt;a href="https://readmehub.divyanshulohani.xyz/" rel="noopener noreferrer"&gt;https://readmehub.divyanshulohani.xyz/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Biggest Lesson Wasn’t Technical
&lt;/h2&gt;

&lt;p&gt;Looking back, the most important things 2025 taught me had very little to do with frameworks, languages, or tools. I didn't build too much projects I explored every aspect in my existing apps I could yeah some distractions were there that did contribute to the factor of not building new projects&lt;/p&gt;

&lt;p&gt;But I learned that building software is an act of responsibility. Your mistake can cost a lot in production a mistake is not just a mistake its a real thing you are accountable for.&lt;/p&gt;

&lt;p&gt;Once something is in production, it stops being “your code” and starts being something other people rely on. That shift changes how you think, how you test, how you deploy, and how you respond when things go wrong.&lt;/p&gt;

&lt;p&gt;I also learned that speed without intention leads to fragile systems, and that maturity in engineering often looks like choosing the simpler, safer option — even when you could build something more impressive.&lt;/p&gt;

&lt;p&gt;Most importantly, I learned that growth isn’t about how much you ship. It’s about how deeply you understand the consequences of what you ship.&lt;/p&gt;

&lt;p&gt;That mindset changed everything.&lt;/p&gt;

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

&lt;p&gt;If 2025 was about learning through experience, 2026 will be about &lt;strong&gt;sharing those experiences openly and building more software tougher ones&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Next year, I want to create more content not tutorials chasing trends, but honest write-ups about what worked, what broke, and what I’d do differently. I want to document the decisions behind the code, not just the code itself.&lt;/p&gt;

&lt;p&gt;I’m entering 2026 with clearer priorities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Build fewer things, but build them better&lt;/li&gt;
&lt;li&gt;  Treat production as sacred&lt;/li&gt;
&lt;li&gt;  Keep learning, but stay grounded&lt;/li&gt;
&lt;li&gt;  Share the journey, not just the outcomes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2025 taught me how fragile software can be. 2026 is about building it with more care and helping others do the same.&lt;/p&gt;

&lt;p&gt;I hope you spent your 2025 just as amazing and fantastic as I did and looking forward I Wish you all a Happy 2026&lt;/p&gt;

</description>
      <category>python</category>
      <category>webdev</category>
      <category>newyearchallenge</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Compromise of a Production VPS</title>
      <dc:creator>DivyanshuLohani</dc:creator>
      <pubDate>Wed, 10 Dec 2025 19:41:08 +0000</pubDate>
      <link>https://dev.to/divyanshulohani/the-compromise-of-a-production-vps-4065</link>
      <guid>https://dev.to/divyanshulohani/the-compromise-of-a-production-vps-4065</guid>
      <description>&lt;p&gt;Security incidents often arrive quietly, without alarm bells or dramatic warnings. They sneak into production environments when we least expect them, on weekends, during moments of downtime, or when routine vigilance slips just a little.&lt;/p&gt;

&lt;p&gt;This is the story of how my production VPS was compromised by attackers who deployed crypto-mining malware and persistent agents. What followed was a long learning process filled with investigation, cleanup attempts, unexpected failures, and finally, a deep dive into what true production-grade security really means.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Sunday Evening Shock
&lt;/h2&gt;

&lt;p&gt;It began innocently. On a calm Sunday evening, I opened my production app to check something. Except… it didn’t load. No error page. No timeout. Just a silent blank screen nothing except the slpash screen.&lt;/p&gt;

&lt;p&gt;I assumed the server might be having a hiccup. Maybe a deployment issue or that core just again said I will not work. Maybe some error or the hosting service down (which is happening a lot recently) but I was so wrong.&lt;/p&gt;

&lt;p&gt;My first thought today is sunday lets leave it for tomorrow but curiousity began in my mind so I got to my workstation and tried to SSH into the VPS. &lt;strong&gt;Connection timed out.&lt;/strong&gt; Tried again. Timed out again I thought hmm... maybe connection failure or may the vps have been shutdown due to overdue bill I supposed. again I was so wrong.&lt;/p&gt;

&lt;p&gt;After multiple attempts, the SSH prompt finally appeared. the machine was slow and sluggish like running ls was also taking 3 - 4 seconds that sluggishness became my first real clue&lt;/p&gt;

&lt;h3&gt;
  
  
  The machine wasn’t down, it was overwhelmed.
&lt;/h3&gt;

&lt;p&gt;Once I logged in, the system felt painfully sluggish. Even typing commands felt like wading through molasses.&lt;/p&gt;

&lt;p&gt;I ran:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;top 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And saw something that changed the tone of the night and runined the few hours of my weekend&lt;/p&gt;

&lt;h3&gt;
  
  
  The Bad Actor
&lt;/h3&gt;

&lt;p&gt;A process named &lt;strong&gt;&lt;code&gt;rysyslo\&lt;/code&gt;&lt;/strong&gt;, located in /usr/bin/rsyslo, was consuming roughly &lt;strong&gt;80% CPU&lt;/strong&gt; and a significant amount of RAM and almost all of the disk space&lt;/p&gt;

&lt;p&gt;I had never installed any binary by that name. Ubuntu the os, doesn’t ship such a file.&lt;/p&gt;

&lt;p&gt;A quick chat and search confirmed it. It was malicious.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My server had been breached.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  First Cleanup Attempt; Sense of a False Victory
&lt;/h2&gt;

&lt;p&gt;I deleted the suspicious binary. Removed unfamiliar services. Killed the rogue processes. The load dropped. The system behaved normally. Everything felt fine. But deep down in my mind I knew that it may not have been gone with such simple attempts (spoiler alert: I was right) but still I left it as I thought I will backup the data tomorrow&lt;/p&gt;

&lt;h2&gt;
  
  
  The Next Day Things Get Worse, Not Better
&lt;/h2&gt;

&lt;p&gt;The following morning, I again opned the app, still unavailable. Over 16 hours of downtime at this point.&lt;/p&gt;

&lt;p&gt;SSH once again behaved erratically — refusing connections for minutes at a time. I even had to ask chat gpt to get me script to constantly try to get in using ssh because it just kept on timing out.&lt;/p&gt;

&lt;p&gt;When I finally got in, the situation had escalated dramatically.&lt;/p&gt;

&lt;p&gt;Running top revealed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;xmrig&lt;/strong&gt; — a notorious crypto-mining tool&lt;/li&gt;
&lt;li&gt;  Multiple disguised binaries with random names&lt;/li&gt;
&lt;li&gt;  Fake kernel threads impersonating system processes&lt;/li&gt;
&lt;li&gt;  A mysterious program called &lt;strong&gt;monarx-agent&lt;/strong&gt; consuming memory&lt;/li&gt;
&lt;li&gt;  Node processes I had not launched&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first obvious solution that came to my mind was to kill them but I knew this was not going to do anything because they must have set backdoors and other ways to restart the service. The system was no longer merely “infected.” It was &lt;strong&gt;owned&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Attackers’ Footprint
&lt;/h3&gt;

&lt;p&gt;I still am very beginner in devops and linux systems even though I use it as my daily driver. I asked chat gpt how can I clean the system follwed the steps closely and this bought me some time to backup any server data that was there.&lt;/p&gt;

&lt;p&gt;First I did basic scans to search for malicious services and found alive.service and lived.service these are too not created by the os and ofcourse not by me hence: malicious they both pointed to a script file located in the following locations:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/tmp/runnv/alive.sh
/tmp/runnv/lived.sh 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Both services were configured to:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Restart=always
RestartSec=5s 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;These scripts continuously fetched, restored, and relaunched the miner — even seconds after deletion. This explained everything: No matter how many times I killed a process or removed a binary, the malicious services resurrected them.&lt;/p&gt;

&lt;p&gt;This wasn’t a small compromise. It was a full-scale, multi-vector persistence setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Services Begin to Break
&lt;/h2&gt;

&lt;p&gt;Over time, the machine became unstable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  SSH stalled or dropped repeatedly&lt;/li&gt;
&lt;li&gt;  System operations slowed to a crawl&lt;/li&gt;
&lt;li&gt;  Memory usage spiked unpredictably&lt;/li&gt;
&lt;li&gt;  System logs showed irregularities&lt;/li&gt;
&lt;li&gt;  A fake agent (monarx-agent) reappeared no matter how many times it was removed&lt;/li&gt;
&lt;li&gt;  The machine would barely stay functional long enough for basic commands&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There was no longer any pretense of stability it became like the VPS was setup intentionally for mining bitcoin.&lt;/p&gt;

&lt;p&gt;The attackers had established &lt;strong&gt;root-level foothold&lt;/strong&gt; (this was due to my fault but more on this later), and the operating system had been modified in ways I could no longer trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Response to the things
&lt;/h2&gt;

&lt;p&gt;At this stage it was not about that how can I fix the server but the important server data that's on there should be extracted asap and the user data should be checked to see that it did not have a breech&lt;/p&gt;

&lt;p&gt;I began systematically backing up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  PostgreSQL database dumps using pgdump&lt;/li&gt;
&lt;li&gt;  Application server files&lt;/li&gt;
&lt;li&gt;  Media directories&lt;/li&gt;
&lt;li&gt;  Configuration files&lt;/li&gt;
&lt;li&gt;  User uploads&lt;/li&gt;
&lt;li&gt;  Essential environment variables&lt;/li&gt;
&lt;li&gt;  Reverse proxy configs&lt;/li&gt;
&lt;li&gt;  Logs for analysis&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because the system was barely responsive, everything required workarounds,&lt;/p&gt;

&lt;p&gt;For anything related to files I used rsync and copied the files to my local machine but this was not the usual way I had to write scripts to check the access of the ssh and then copy the files because the ssh connection kept on dropping.&lt;/p&gt;

&lt;p&gt;Eventually, I succeed in the task &lt;strong&gt;every important asset was safely backed up&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After confirming data integrity and to my surprise the application data was untouched by the hackers they just wanted to use my machine to mine crypto, so I made the decision to shut down the compromised machine permanently and migrate to a fresh VPS instance because this was destroyed beyod repair.&lt;/p&gt;

&lt;h2&gt;
  
  
  Searching for the Entry Point
&lt;/h2&gt;

&lt;p&gt;After all the chaos I was geniunly curous that how did they get access to my server, I never had my keys leaked nor the IP so I began looking into this.&lt;/p&gt;

&lt;p&gt;I began by reviewing all forensic data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  /var/log/auth.log&lt;/li&gt;
&lt;li&gt;  SSH access attempts&lt;/li&gt;
&lt;li&gt;  Systemd unit file timestamps&lt;/li&gt;
&lt;li&gt;  Root-level process creation logs&lt;/li&gt;
&lt;li&gt;  Cron jobs&lt;/li&gt;
&lt;li&gt;  Unknown script origins&lt;/li&gt;
&lt;li&gt;  Unfamiliar executables&lt;/li&gt;
&lt;li&gt;  Running port lists&lt;/li&gt;
&lt;li&gt;  Binaries in /usr/bin, /tmp, /dev/shm, /usr/share/updater&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I discovered:&lt;/p&gt;

&lt;p&gt;I was right I did not had my keys leaked or something like that. The log only showed rejected SSH requests which I know happen because some people just leave everything to the default (I am one of those but not at this point)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Everything was good but annoyingly&lt;/strong&gt;:&lt;/p&gt;

&lt;h3&gt;
  
  
  ❗ There was no definitive entry point.
&lt;/h3&gt;

&lt;p&gt;So where did they get in?&lt;/p&gt;

&lt;p&gt;The prime suspect is the very recently disclosed &lt;a href="https://www.wiz.io/blog/critical-vulnerability-in-react-cve-2025-55182" rel="noopener noreferrer"&gt;&lt;strong&gt;React2Shell vulnerability (CVE-2025-55182)&lt;/strong&gt;&lt;/a&gt; revealed just a day before the attack. This vulnerability allows remote code execution under certain misconfigured environments.&lt;/p&gt;

&lt;p&gt;Web servers or exposed ports would have been particularly vulnerable. Another major factor:&lt;/p&gt;

&lt;p&gt;And yes I was right I checked the nginx logs for the server and there I see someone forcefully did a post request to every endpoint they could've found and there might been vulnerable endpoints which gave them access to the machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hostinger VPS firewall defaults to allow ALL inbound and outbound traffic.
&lt;/h3&gt;

&lt;p&gt;Unlike cloud platforms with strict security groups (AWS, GCP), this VPS was wide open at the network level. this was the default thing I never touched.&lt;/p&gt;

&lt;p&gt;This alone dramatically increases the attack surface.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Biggest Lesson — Never Run Applications as root
&lt;/h2&gt;

&lt;p&gt;One big mistake I had made knowingly that not to run apps with root privelleges that they can modify the system in anyway. My PostgreSQL server, which ran under a &lt;strong&gt;non-root system user&lt;/strong&gt;, remained untouched by the attacker, I know they were not looking for it but in case then it would've still remained safe No tampered files. No altered configurations. No corrupt data. No suspicious access. Even though the system was compromised, the database was safe.&lt;/p&gt;

&lt;p&gt;This incident reinforced a principle I will never ignore again:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Never run applications as root. Ever.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Give them the smallest permissions required to function and nothing more. not even modification outside the directory (unless your app has to access).&lt;/p&gt;




&lt;h2&gt;
  
  
  The Rebuild
&lt;/h2&gt;

&lt;p&gt;After recovering and moving to a fresh VPS, I rebuilt the environment with proper security measures:&lt;/p&gt;

&lt;p&gt;Firstly I ensured that I do not run the process as the root user I create users for each module like for the server for the frontend or for the api. NO compromise should be made in setting users up.&lt;/p&gt;

&lt;p&gt;I enforced strict firewall rules only allow http and https ports to be expoed to the public because my app does run postgres but as an internal tool and the database should not be accisble publically all traffic is handled via nginx so other ports should not be open.&lt;/p&gt;

&lt;p&gt;This wasn’t just “rebuilding a server.” It was rebuilding an entire philosophy about production infrastructure.&lt;/p&gt;




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

&lt;p&gt;Security incidents can be unsettling, especially when a server is actively failing under the strain of malware and critical data must be preserved in a limited window of time. Yet, despite the pressure, this experience provided a deeper and more practical understanding of DevOps and cybersecurity than any structured lesson could have offered. Real incidents force you to confront the consequences of design decisions, configuration choices, and operational habits in a way theoretical learning never does.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key lessons learned:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;A VPS exposed with open ports is an open invitation for attackers.&lt;/strong&gt; Any publicly accessible service increases the attack surface. Without strict access controls, monitoring, and proper configuration, automated scanners and bots will quickly identify and target vulnerabilities.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The presence of a crypto miner is a clear indicator of full system compromise.&lt;/strong&gt; Attackers who deploy miners rarely stop there; they often modify system files, escalate privileges, and establish hidden footholds. Once this occurs, trust in the system is lost. Rebuilding from a clean state is the only reliable response.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Attackers rely on persistence mechanisms that are deeper than a single malicious process.&lt;/strong&gt; Modern attacks may involve cron jobs, rootkits, altered binaries, SSH backdoors, and hidden user accounts. Removing one visible component does not eliminate the underlying compromise.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Least privilege is a critical layer of defense.&lt;/strong&gt; Systems designed so that applications do not run as root help ensure that, even if an intrusion occurs, the damage is limited. Proper isolation, permissions, and role separation can prevent attackers from accessing sensitive data or making irreversible changes.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Backups are invaluable during emergencies.&lt;/strong&gt; When a system is compromised, having recent, verified, and off-server backups can mean the difference between a smooth recovery and catastrophic data loss. Backup integrity and testing should be treated as core operational practices.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Default configurations are rarely secure.&lt;/strong&gt; Vendor defaults prioritize usability and compatibility, not security. Hardening a system requires deliberate configuration: limiting access, disabling unnecessary services, enforcing strong authentication, and enabling proper logging.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Incident response demands composure and methodical action.&lt;/strong&gt; In high-pressure situations, rushing often leads to mistakes. A structured approach—assess, contain, preserve evidence, recover, and improve—ensures that decisions are informed and effective rather than reactive.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ultimately, this was far more than an unplanned lesson in server hardening. It served as a reminder that security is not a checkbox or a one-time setup. It is an ongoing discipline that requires continuous awareness, proactive measures, and a mindset of vigilance.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;My production VPS was fully compromised by attackers who deployed crypto-mining malware and multiple persistence mechanisms. Initial cleanup attempts failed because the system had already been taken over at the root level. After identifying disguised services, malicious binaries, and severe instability, I focused on backing up critical data before rebuilding everything on a fresh server.&lt;/p&gt;

&lt;p&gt;The investigation showed that open ports, default firewall settings, and a likely exploitation of a newly disclosed vulnerability created the entry point. The incident reinforced essential lessons: never expose unnecessary services, never run applications as root, always enforce least privilege, and treat security as an ongoing discipline—not a one-time setup.&lt;/p&gt;

</description>
      <category>react</category>
      <category>react2shell</category>
      <category>nextjs</category>
      <category>vulnerabilities</category>
    </item>
    <item>
      <title>When Hacktoberfest Became README-fest - I did this</title>
      <dc:creator>DivyanshuLohani</dc:creator>
      <pubDate>Wed, 08 Oct 2025 10:59:58 +0000</pubDate>
      <link>https://dev.to/divyanshulohani/when-hacktoberfest-became-readme-fest-i-did-this-4kk2</link>
      <guid>https://dev.to/divyanshulohani/when-hacktoberfest-became-readme-fest-i-did-this-4kk2</guid>
      <description>&lt;h2&gt;
  
  
  The October of Open Source 🎃
&lt;/h2&gt;

&lt;p&gt;October is a magical month for developers. While most people think it as just a normal month transition from summer to winter, we developers have our own tradition, &lt;strong&gt;Hacktoberfest&lt;/strong&gt;. It’s that time of year when GitHub notifications blow up, Discord servers buzz with collaboration, and developers from every corner of the internet collectively shout,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Hey, let’s contribute to open source!”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hacktoberfest, started by &lt;strong&gt;DigitalOcean in 2014&lt;/strong&gt;, was a simple yet brilliant idea: encourage developers to contribute to open source, reward them with limited-edition swag, and spread the joy of collaboration. It lowered the barrier for beginners so suddenly, even your first small PR (Pull Request) felt like a ticket to the world of real-world software.&lt;/p&gt;

&lt;p&gt;It wasn’t just about code; it was about community. People made friends, joined projects, fixed typos, improved docs, and sometimes even built features that millions now use. For many developers, Hacktoberfest was their &lt;em&gt;gateway drug&lt;/em&gt; into open source.&lt;/p&gt;

&lt;p&gt;But like all things on the internet, what starts wholesome… eventually mutates into chaos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Source: The Internet’s Most Beautiful Mess
&lt;/h2&gt;

&lt;p&gt;To anyone outside the dev world, “open source” sounds like a technical term. But for us, it’s much more than that. it’s &lt;em&gt;a philosophy&lt;/em&gt;. It’s about sharing knowledge, collaboration without boundaries, and believing that software gets better when everyone can help shape it.&lt;/p&gt;

&lt;p&gt;I still remember my first real open source contribution. It was at &lt;a href="https://www.linkedin.com/company/formbricks/" rel="noopener noreferrer"&gt;Formbricks&lt;/a&gt; as a part of combined initiative that was &lt;a href="http://oss.gg" rel="noopener noreferrer"&gt;oss.gg&lt;/a&gt; I stepped into the world by just making a small change adding a &lt;em&gt;Last Used&lt;/em&gt; indicator to the login page.&lt;/p&gt;

&lt;p&gt;But as we know every coin has two faces open source is no exception to that, the noble idea of coming together and helping build good software for people collided with internet culture’s favorite game: &lt;strong&gt;the numbers game (&lt;/strong&gt;&lt;em&gt;it ruins everything&lt;/em&gt;&lt;strong&gt;)&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: When “Open Source” Turned Into “Open Spam”
&lt;/h2&gt;

&lt;p&gt;Let’s be honest — the moment a reward system exists, people will find a shortcut to just abuse it and get the reward no matter the outcome.&lt;/p&gt;

&lt;p&gt;As Hacktoberfest grew, so did YouTube tutorials titled &lt;em&gt;“How to Win Hacktoberfest FAST!”&lt;/em&gt; New devs, excited to participate (and maybe get a free t-shirt), started following “guides” that basically said:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Just find any repo, fix a typo, and submit four PRs.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And thus began the era of the &lt;strong&gt;README Pull Request Apocalypse&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Repos everywhere were getting flooded with contributions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  “Fixed a small grammar mistake.”&lt;/li&gt;
&lt;li&gt;  “Added full stop at the end.”&lt;/li&gt;
&lt;li&gt;  “Updated README title from H1 to H2.”&lt;/li&gt;
&lt;li&gt;  “Added emojis for ✨aesthetic✨.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some poeple &lt;em&gt;(yes people not devs)&lt;/em&gt; just be like I will add my name to the readme for what reason god knows but they will do.&lt;/p&gt;

&lt;p&gt;Open source maintainers were drowning in meaningless PRs. Some people were even creating repos &lt;em&gt;just to spam each other with fake PRs&lt;/em&gt;. Github like introduced a feature to like limit new users from contribution which can be turned off every major framework or library has that turned on, It became a game changed from who can get do meaningful help to who could get four green squares the fastest.&lt;/p&gt;

&lt;p&gt;Now, don’t get me wrong — I don’t blame anyone. For many, it wasn’t malice; it was &lt;em&gt;desperation to belong&lt;/em&gt;. Beginners wanted to see their name in the contributors list. They wanted proof they were “part of open source.”&lt;/p&gt;

&lt;p&gt;But in chasing that feeling, Hacktoberfest started losing what made it beautiful — genuine collaboration.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Pun-In-Code Response: Enter ReadmeHub
&lt;/h2&gt;

&lt;p&gt;I could’ve written a tweet thread complaining about it. Or made a meme. But instead, I thought — &lt;em&gt;why not turn the whole situation into a joke that actually works as an app?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That’s how &lt;strong&gt;ReadmeHub&lt;/strong&gt; was born — a parody platform for “&lt;em&gt;open source contributors&lt;/em&gt;” who just can’t resist updating README files.&lt;/p&gt;

&lt;p&gt;It’s like GitHub’s unserious cousin — built purely for fun and satire. You log in, type your username, and you’re now a “contributor.”&lt;/p&gt;

&lt;p&gt;The app has a variety of repositories to contribute to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;cool-project&lt;/strong&gt; – “The coolest project you’ll ever see (probably not)”&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;todo-app-9000&lt;/strong&gt; – “Yet another todo app that will definitely change your life”&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;useless-project&lt;/strong&gt; – “A project so useless, it’s actually useful”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can contribute to them in following ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Fixing Typos&lt;/li&gt;
&lt;li&gt;  Adding Emojis (AI thinks it makes a repo look professional)&lt;/li&gt;
&lt;li&gt;  Adding Quotes randomly&lt;/li&gt;
&lt;li&gt;  Vibe edit the readme using AI ofcourse&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every “contribution” you make earns you &lt;em&gt;badges&lt;/em&gt; like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  🏆 &lt;strong&gt;Fixed 10 Typos&lt;/strong&gt; – Grammar police salute you!&lt;/li&gt;
&lt;li&gt;  🚀 &lt;strong&gt;Added 500 Emojis&lt;/strong&gt; – Houston, we have a contribution!&lt;/li&gt;
&lt;li&gt;  ☕ &lt;strong&gt;Coffee-Fueled Coder&lt;/strong&gt; – 2 a.m. commits, fueled by caffeine and chaos.&lt;/li&gt;
&lt;li&gt;  🐦 &lt;strong&gt;Early Bird&lt;/strong&gt; – You were here before it was cool.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the best part? You can generate a &lt;strong&gt;shareable image&lt;/strong&gt; — a fake contribution card to flex on your developer friends.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9b2pnfran5ryl8hcfiu4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9b2pnfran5ryl8hcfiu4.png" alt="My Profile" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because sometimes, instead of fighting absurdity… it’s more fun to join in and make it &lt;em&gt;funnier.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;👉 Try it here: &lt;a href="https://readmehub.divyanshulohani.xyz/" rel="noopener noreferrer"&gt;https://readmehub.divyanshulohani.xyz/&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Hidden Message Beneath the Joke 💭
&lt;/h3&gt;

&lt;p&gt;ReadmeHub isn’t just a meme project (okay, maybe 90% meme, 10% message). But it also says something about developer culture today.&lt;/p&gt;

&lt;p&gt;The world of programming is increasingly gamified — GitHub streaks, LeetCode badges, AI-assisted commits, contribution graphs. Sometimes, we forget that &lt;em&gt;code is supposed to solve problems&lt;/em&gt;, not just earn points.&lt;/p&gt;

&lt;p&gt;ReadmeHub pokes fun at that obsession with metrics. It’s satire for anyone who has ever stared at their GitHub profile thinking,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I need more green squares.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;or&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I need to have that perfect looking github graph"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s a reminder that &lt;em&gt;your value as a developer isn’t measured in PR counts or stars.&lt;/em&gt; It’s measured by curiosity, learning, and the problems you solve — even if they’re small.&lt;/p&gt;

&lt;p&gt;Ironically, by making a fake-contribution app, I ended up having deeper conversations with developers about &lt;em&gt;real&lt;/em&gt; open-source contribution. Sometimes humor opens doors that serious rants can’t.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Irony of It All
&lt;/h2&gt;

&lt;p&gt;The funniest (and maybe saddest) realization? A parody app about fake contributions probably got more engagement than many genuine open-source repos do in a month.&lt;/p&gt;

&lt;p&gt;But that’s also what makes developer culture fascinating — we love building weird things, just because we can.&lt;/p&gt;

&lt;p&gt;Every viral project starts with a mix of humor and curiosity. Whether it’s a useless API that returns random cat facts, or a website that tells you “how many coffees you’ve coded today,” — it’s all part of the same creative chaos that fuels open source.&lt;/p&gt;

&lt;p&gt;ReadmeHub fits right into that lineage — silly, smart, and just self-aware enough to make people smile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: What Hacktoberfest Still Means
&lt;/h2&gt;

&lt;p&gt;At its core, Hacktoberfest is still a beautiful idea. It gets thousands of people writing code, exploring projects, and learning skills they’ll carry for life maybe we will see some more open source projects.&lt;/p&gt;

&lt;p&gt;But maybe we all need a reminder: Contributions are more than commits — they’re collaboration, communication, and creativity combined. If you’re contributing to open source this October, do it for the right reasons: to learn, to share, to help.&lt;/p&gt;

&lt;p&gt;And if you’re just here for the badges, emojis, and “README updates”… don’t worry — we’ve got you covered. Visit &lt;a href="https://readmehub.divyanshulohani.xyz/" rel="noopener noreferrer"&gt;&lt;strong&gt;ReadmeHub&lt;/strong&gt;&lt;/a&gt; and go wild. After all, sometimes the best way to make a point is to &lt;strong&gt;turn it into a punchline.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now go and fix some typos while I find a repo that can run on my pc.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>hacktoberfest</category>
      <category>opensource</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Implementing Real-Time Chat with SSE vs WebSockets (and Why I Chose One)</title>
      <dc:creator>DivyanshuLohani</dc:creator>
      <pubDate>Mon, 15 Sep 2025 04:12:03 +0000</pubDate>
      <link>https://dev.to/divyanshulohani/implementing-real-time-chat-with-sse-vs-websockets-and-why-i-chose-one-2mn2</link>
      <guid>https://dev.to/divyanshulohani/implementing-real-time-chat-with-sse-vs-websockets-and-why-i-chose-one-2mn2</guid>
      <description>&lt;p&gt;Every developer building a chat app eventually stumbles into the same rabbit hole: &lt;strong&gt;Do I use WebSockets or Server-Sent Events (SSE)?&lt;/strong&gt; It sounds like a minor technical choice at first—just pick whichever tutorial looks nicer on YouTube and move on. But trust me, this one decision can quietly shape the scalability, complexity, and sanity of your project (and your future AWS bills if you pick wrong).&lt;/p&gt;

&lt;p&gt;When I started working on real-time communication for my Django app, I had to make this exact decision. The app needed chat, but chat wasn’t the &lt;em&gt;main&lt;/em&gt; feature just a nice to have. So naturally I didn't wnat to spend more time on selecting techonlogies and give it a whole new timeline.&lt;/p&gt;

&lt;p&gt;In this article, we’ll dive deep into &lt;strong&gt;what real-time communication really is&lt;/strong&gt;, explore &lt;strong&gt;how SSE and WebSockets work&lt;/strong&gt;, compare them side by side, and finally, I’ll share why I picked SSE for my project (and why it was some sort of inspired by linkedin).&lt;/p&gt;

&lt;h2&gt;
  
  
  Deep Dive into SSE
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Sever Sent Events (SSE)&lt;/strong&gt; is like an open door which the client makes to the server the open door only allows one way communication from server to the client not the other way around it sends messages its just another TCP connection which just has its end packet forgotten so it keeps on going.&lt;/p&gt;

&lt;p&gt;It’s built on &lt;strong&gt;plain old HTTP&lt;/strong&gt;, so you don’t need to break your brain setting up new protocols.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code Example: SSE in Django
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Server (Django view):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.http&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StreamingHttpResponse&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sse_chat_stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;event_stream&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="c1"&gt;# In reality, pull from Redis/pub-sub
&lt;/span&gt;                &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data: New message at &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;StreamingHttpResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;event_stream&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;text/event-stream&lt;/span&gt;&lt;span class="sh"&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;Client (JavaScript):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;eventSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EventSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/chat/stream/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;eventSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&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="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;New message:&lt;/span&gt;&lt;span class="dl"&gt;"&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it. You’ve got a real-time connection with about 20 lines of code. (Which is about 50 fewer than WebSockets usually require.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros of SSE
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Simple and easy to impliment with very less of a overhaul.&lt;/li&gt;
&lt;li&gt;  One way communication mostly realtime but this can become a down side too&lt;/li&gt;
&lt;li&gt;  Your browser autoretrys the connection in an event of failure&lt;/li&gt;
&lt;li&gt;  Almost all browsers support it (Almost is due to IE)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons of SSE
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  So one way communication is also pro but can be a con depending on your project.&lt;/li&gt;
&lt;li&gt;  Not great if your app relies on the milisecond the message is sent you have to recieve it&lt;/li&gt;
&lt;li&gt;  No binary data (text only). If you need binary, look elsewhere. but can be useful&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But here’s the kicker: for 80% of “real-time” needs—like chats that aren’t the entire focus of the product—SSE is more than enough you don't need NASA like communication that every millisecond matters.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqff28swlic12em47zdi3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqff28swlic12em47zdi3.png" alt="SSE Connection Diagram" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deep Dive into WebSockets
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;p&gt;WebSockets are like having a direct phone line between client and server. Once connected, both can talk freely, as much as they want, whenever they want, just a private, always-on conversation.&lt;/p&gt;

&lt;p&gt;It’s a separate protocol that starts with an HTTP handshake and then upgrades to a &lt;strong&gt;persistent TCP connection&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code Example: WebSockets
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Server (Node.js with&lt;/strong&gt; ws*&lt;em&gt;):&lt;/em&gt;*&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;WebSocket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ws&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;wss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;wss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;connection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Received: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;message&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="nx"&gt;ws&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="s2"&gt;`Echo: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;message&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Client:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ws://localhost:8080&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onopen&lt;/span&gt; &lt;span class="o"&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="nx"&gt;socket&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello Server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you’ve got two-way communication—chat, games, collaborative docs, you name it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros of WebSockets
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Both ways of communication can happen any time.&lt;/li&gt;
&lt;li&gt;  Latency is very low almost realtime&lt;/li&gt;
&lt;li&gt;  Perfect for chat apps if your main focus is reducing latency to the 10th of a second&lt;/li&gt;
&lt;li&gt;  Binary data handling is supported.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons of WebSockets
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Complex to scale and has lots of network limitations&lt;/li&gt;
&lt;li&gt;  Requires extra setup in case of django it can be another level of headache&lt;/li&gt;
&lt;li&gt;  Its too powerful for simple apps and comes with the same cost.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffzwxob74d4hp0phaamqp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffzwxob74d4hp0phaamqp.png" alt="Comparison b/w two" width="625" height="331"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My Real-World Implementation
&lt;/h2&gt;

&lt;p&gt;Here’s the context: I was building a Django app where chat was a &lt;strong&gt;nice-to-have feature&lt;/strong&gt;, not the core. Think of it as the beverages that come in a Burger King combo not needed but good to have. (Yes I don't like soft drinks that much)&lt;/p&gt;

&lt;p&gt;I first considered &lt;strong&gt;WebSockets&lt;/strong&gt;. They’re the gold standard for chat, after all. But as I dug deeper, I realized setting up Django Channels, handling scaling with Redis, configuring sticky sessions, and then debugging it all would be like bringing a rocket launcher to open a soda can.&lt;/p&gt;

&lt;p&gt;Then I started researching more on the topic and did some reverse engineering on apps I use such as linkedin what they use in their apps for the same I found that linkedin used SSE for the chats and it just works very well I know they have put a lot of thought in selecting this as their primary system but it gave me a way then I learned about SSE and implimented it in my app. It has allowed me to have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Real-time server → client updates.&lt;/li&gt;
&lt;li&gt;  Super easy scaling with existing HTTP infra.&lt;/li&gt;
&lt;li&gt;  Less complexity to manage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And to be honest that's all I need for my app I don't need like crazy good realtime updates its not Telegram or Discord. All I do is create a POST request for new message from clients and using simple redis queue and some pubsub magic it just works.&lt;/p&gt;

&lt;p&gt;Simple. Effective. And no therapy sessions required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minimal Chat with SSE
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Server (Django + Redis pub/sub):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.http&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StreamingHttpResponse&lt;/span&gt;

    &lt;span class="n"&gt;redis_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StrictRedis&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;room_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;event_stream&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;pubsub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pubsub&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;pubsub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chat_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;room_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pubsub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;StreamingHttpResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;event_stream&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text/event-stream&lt;/span&gt;&lt;span class="sh"&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;Client:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;eventSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EventSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/chat/stream/room123/`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;eventSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&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="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&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;parse&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;New message:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&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="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// Sending messages&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#send&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;onclick&lt;/span&gt; &lt;span class="o"&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/chat/send/&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="s2"&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="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello world&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;
  
  
  Lessons Learned &amp;amp; Best Practices
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Start simple.&lt;/strong&gt; Don’t dive into WebSockets unless you truly need bidirectional communication they just require lot more effort than they should. (If all you want is notifications, SSE is your friend.)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Scale smartly.&lt;/strong&gt; Use Redis pub/sub for message distribution. Throw in Nginx as a reverse proxy, and you’re good for a long time.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Fallbacks matter.&lt;/strong&gt; If SSE fails (say, old browsers), fall back to long-polling. Your users don’t care about protocols; they just want their message to show up.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Measure before you optimize.&lt;/strong&gt; Don’t assume you’ll have 10 million concurrent users tomorrow. Pick the tool that works now and is easy to evolve later.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;To sum it up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;SSE&lt;/strong&gt;: Lightweight, simple, perfect for one-way updates.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;WebSockets&lt;/strong&gt;: Powerful, full-duplex, perfect for heavy chat/gaming apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For my project, I chose &lt;strong&gt;SSE&lt;/strong&gt;—because it was easy, scalable enough, and fit my use case without unnecessary complexity.&lt;/p&gt;

&lt;p&gt;Real-time communication isn’t about flexing the fanciest tech—it’s about delivering the right experience without making your life miserable as a developer.&lt;/p&gt;

&lt;p&gt;So, what about you? &lt;strong&gt;Did you pick SSE or WebSockets for your project?&lt;/strong&gt; Let me know—I’d love to hear your war stories.&lt;/p&gt;

</description>
      <category>sse</category>
      <category>websockets</category>
      <category>django</category>
      <category>python</category>
    </item>
    <item>
      <title>How Shipping Code in a Rush Can Cause Uncalculated Losses</title>
      <dc:creator>DivyanshuLohani</dc:creator>
      <pubDate>Tue, 09 Sep 2025 11:02:24 +0000</pubDate>
      <link>https://dev.to/divyanshulohani/how-shipping-code-in-a-rush-can-cause-uncalculated-losses-3bg8</link>
      <guid>https://dev.to/divyanshulohani/how-shipping-code-in-a-rush-can-cause-uncalculated-losses-3bg8</guid>
      <description>&lt;p&gt;There’s a golden rule in software development—something every junior developer learns within their first month of writing code: &lt;strong&gt;“Never ship untested code to production.”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It sounds simple enough, right? Like “look both sides before crosing a road” or “don't use metal in a microwave” Yet, every now and then, giant corporations (with teams of thousands of developers, mind you) somehow forget this rule. And when they do, the results are usually hilarious for users and catastrophic for the company’s balance sheet.&lt;/p&gt;

&lt;p&gt;This past week, Reliance JioMart (yes, the Reliance-backed e-commerce giant) may have had their mishappening. Officially, JioMart hasn’t said a word about it (and they probably never will—because who likes to admit they left the door wide open?). But users discovered something that looked suspiciously like a bug, and the telegram mafia did what it always does best: exploit it, and turn it into flowing cash&lt;/p&gt;

&lt;h2&gt;
  
  
  The Incident: When Offer Turned into Infinite Money Glitches
&lt;/h2&gt;

&lt;p&gt;On the surface, JioMart’s scheme looked innocent enough. They rolled out a promotion saying: spend ₹101 or more, and you’d get ₹100 off. Great deal, right? (No, not even if this was not the incident) The kind that makes you add that extra packet of biscuits to your cart just to qualify.&lt;/p&gt;

&lt;p&gt;Except… the way they set it up apparently had a fatal flaw. The promotional link they sent wasn’t just a one-time coupon link—it was reportedly a &lt;strong&gt;coupon factory&lt;/strong&gt;. Every time you clicked it (or tweaked it just a little), boom: a brand-new coupon worth ₹100 landed in your account. Unlimited. Endless. Like Willy Wonka’s chocolate river, but instead of chocolate, it was discount codes.&lt;/p&gt;

&lt;p&gt;And because the internet is faster at finding loopholes than companies are at closing them, people quickly caught on. Telegram groups saw a great opportunity and turned it to a cash thing. They started offering hundrends even thousands of cupons for free and once they ran out(which was pretty quick) they started selling them ₹10–20 each. Think about that: a ₹100 coupon going for just ₹10 or even free if you were lucky and quick. That’s not just a discount—that’s arbitrage, baby.&lt;/p&gt;

&lt;p&gt;In effect, JioMart’s marketing campaign accidentally created a &lt;strong&gt;black market economy&lt;/strong&gt; inside Telegram. Imagine explaining that in a board meeting:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Sir, our competitors are not the issue. Our biggest competitor is now a Telegram group run by some college kids selling our coupons at 90% off.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What (Allegedly) Went Wrong
&lt;/h2&gt;

&lt;p&gt;From digging into the reports (and yes, some curious minds actually tested the link), the bug seemed almost embarrassingly simple. The link they sent out had a &lt;strong&gt;query parameter&lt;/strong&gt; which likely was a tracking id for the specific customer but was not checked.&lt;/p&gt;

&lt;p&gt;But here’s the kicker: the parameter could accept &lt;em&gt;any random value&lt;/em&gt; and still generate a fresh coupon code like a coupon creating machine. Now, as developers, we can all imagine how this might have happened. Maybe someone forgot to add a validation check. Maybe QA was skipped because deadlines were looming. Or maybe the dev team thought, &lt;em&gt;“Who’s going to notice?”&lt;/em&gt; (Answer: everyone. Everyone noticed.)&lt;/p&gt;

&lt;p&gt;It raises the question: what kind of logic was powering this system? Was it something like:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (request.hasQueryParam) {
    generateCoupon();
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Because that’s what it felt like. One missing if condition. One missing database check. One small oversight that spiraled into thousands of coupons.&lt;/p&gt;

&lt;p&gt;And the funny part? This wasn’t some deep, sophisticated hack that required genius-level skills. No, this was &lt;strong&gt;low-hanging fruit&lt;/strong&gt;. It’s the digital equivalent of leaving your shop unlocked at night with a sign saying, &lt;em&gt;“Please don’t steal.”&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Aftermath: Servers Down, Coupons Everywhere
&lt;/h2&gt;

&lt;p&gt;As word spread, chaos followed. JioMart’s cart service and related systems reportedly buckled under the load. Think about it: hundreds (maybe thousands) of people hammering the system, generating coupon after coupon, placing order after order.&lt;/p&gt;

&lt;p&gt;For customers, it was like Diwali came few weeks early. For JioMart, it must have looked like a DDoS attack—but powered not by bots, but by &lt;em&gt;real coupon hungry people.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Financially, the losses could have been massive. Even if only a fraction of the coupons were redeemed, each represented ₹100 in lost revenue. Multiply that by thousands, and you’re staring at some very uncomfortable spreadsheets.&lt;/p&gt;

&lt;p&gt;JioMart has not confirmed the incident. Which makes sense. You don’t exactly want your brand trending on Twitter under &lt;em&gt;#UnlimitedCoupons&lt;/em&gt;. Admitting it would be like saying, &lt;em&gt;“Yeah, we accidentally let people print money for a day. Oops.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But interestingly, the next day the link magically stopped working. Coincidence? Or a silent fix? I’ll let you decide. (Spoiler: it was a fix.). Still some sort of bug or intended feature like everyday you would click the same link you would still get a brand new coupon but seems like its intended working.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why This Matters (and Why It’s Funny)
&lt;/h3&gt;

&lt;p&gt;On one level, this is hilarious. Big company leaves giant hole, users exploit it, chaos ensues. It’s the kind of story that makes developers shake their heads and laugh nervously, because deep down, we all know: &lt;em&gt;this could happen to any of us if we’re not careful&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;But on another level, it’s dead serious. This wasn’t just a minor UI glitch or a typo in the app. This was a &lt;strong&gt;systemic design flaw&lt;/strong&gt;—the kind that directly hits revenue.&lt;/p&gt;

&lt;p&gt;The sarcasm writes itself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  “Why bother with rate limiting? Let everyone generate coupons like new orders”&lt;/li&gt;
&lt;li&gt;  “Who needs backend validation when you can trust query parameters, the most trustworthy part of the internet?”&lt;/li&gt;
&lt;li&gt;  “Unit testing? Nah. Let production users do the testing for us. They’ll find bugs faster anyway.”&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lessons for Developers (and Corporates Who Don’t Listen)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Validation is sacred.&lt;/strong&gt; Never trust input from the client side. Ever. It doesn’t matter if it’s a coupon code, a price, or a user ID—if your backend just accepts it blindly, you’re inviting chaos.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Abuse scenarios are real.&lt;/strong&gt; When designing a system, don’t just think, &lt;em&gt;“What’s the happy path?”&lt;/em&gt; Think, &lt;em&gt;“How would someone misuse this?”&lt;/em&gt; Because trust me, someone will.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Testing isn’t optional.&lt;/strong&gt; Yes, deadlines are tight. Yes, management wants the feature out “yesterday.” But the cost of rushing can dwarf the cost of a delay. Which would you rather explain in a meeting: &lt;em&gt;“We’re shipping a week late,”&lt;/em&gt; or &lt;em&gt;“We accidentally lost crores because our coupon system was a slot machine”?&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Transparency matters (sometimes).&lt;/strong&gt; JioMart chose silence, which is understandable. But imagine the goodwill they could have earned by acknowledging it lightly—“Hey, we goofed, it’s fixed, no free money anymore.” At least it would have made them human.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Incidents like this aren’t rare. History is full of “fun” bugs that cost companies millions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  In 2014, a bug in Shoppers Stop’s online store let people order items worth thousands for ₹1.&lt;/li&gt;
&lt;li&gt;  In 2019, a Paytm glitch allowed people to generate unlimited movie tickets using promo codes.&lt;/li&gt;
&lt;li&gt;  And who can forget the infamous “Amazon accidentally listed expensive cameras for $94” bug? (Someone, somewhere, got Christmas and Diwali rolled into one.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The common thread? &lt;strong&gt;Rushing to production without proper testing.&lt;/strong&gt; It’s the software equivalent of building a skyscraper but forgetting to check if the elevator works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;Whether or not JioMart ever admits it, this was an &lt;em&gt;interesting choke point&lt;/em&gt; in their system design—a perfect example of how one unchecked assumption can snowball into a full-blown crisis.&lt;/p&gt;

&lt;p&gt;For developers, it’s a cautionary tale. For users, it was a once-in-a-lifetime sale. And for JioMart, it’s probably a very expensive lesson written into some internal post-mortem doc with the title: &lt;em&gt;“Why We Test Promo Systems Before Sending Them to 100 Million Users.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So the next time you’re tempted to push that untested code to production because “it’s just a small feature,” remember JioMart’s infinite coupon generator. In the world of software, shortcuts aren’t just risky—they’re sometimes very, very costly.&lt;/p&gt;

&lt;p&gt;Or, to put it simply: &lt;strong&gt;you can either test your code, or your users will test it for you—and they’ll do it with a lot more creativity.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>softwareengineering</category>
      <category>jokes</category>
      <category>product</category>
    </item>
    <item>
      <title>Why MVP Mindset Saves Projects (And How I Apply It)</title>
      <dc:creator>DivyanshuLohani</dc:creator>
      <pubDate>Tue, 12 Aug 2025 11:08:06 +0000</pubDate>
      <link>https://dev.to/divyanshulohani/why-mvp-mindset-saves-projects-and-how-i-apply-it-5ei4</link>
      <guid>https://dev.to/divyanshulohani/why-mvp-mindset-saves-projects-and-how-i-apply-it-5ei4</guid>
      <description>&lt;h2&gt;
  
  
  The Project Graveyard in My Github
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F29vlzfejf960errt0kcz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F29vlzfejf960errt0kcz.png" alt="Screenshot of a GitHub profile showing multiple unfinished repositories" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have a lot of unfinished projects in my GitHub. They are digital tombstones of grand ambitions that died somewhere between "revolutionary idea" and "hmm, maybe I need user authentication AND real-time chat AND AI integration AND..." everything I can think of during the planing phase and only have kanban boards or todo lists for them never got to be real.&lt;/p&gt;

&lt;p&gt;Sound familiar?&lt;/p&gt;

&lt;p&gt;For years, I was that developer who'd start projects with a mental feature list longer than a CVS receipt. I'd spend weeks architecting the perfect system, planning for edge cases that might never happen, and building features "just in case." The result? A graveyard of half-built applications and a growing sense that I couldn't finish anything.&lt;/p&gt;

&lt;p&gt;Then I discovered the MVP mindset. Not just as a business concept, but as a personal productivity philosophy. And it changed everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  What MVP Actually Means
&lt;/h2&gt;

&lt;p&gt;Most people think MVP means "build something basic and hope for the best." That's wrong.&lt;/p&gt;

&lt;p&gt;MVP means building the smallest thing that solves a real problem and gives you real feedback. It's not about being lazy or cutting corners—it's about being ruthlessly focused on what actually matters.&lt;/p&gt;

&lt;p&gt;The skateboard-to-car analogy gets thrown around a lot, but here's what it really taught me: each iteration should be a complete, working solution. A skateboard gets you from point A to point B. So does a car. But you learn different things from each version. The way you maintain balance on a skateboard is not the same as gearing up in a car.&lt;/p&gt;

&lt;p&gt;My mistake for years was trying to build the car from day one. Spoiler alert: it doesn't work and after this realization I belive it will never work projects built from ground up should be treated like wheels without wheels skateboard or cars are just not possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Project That Actually Shipped
&lt;/h3&gt;

&lt;p&gt;Last year, I had an idea for a expesne management app specifically for me. My initial plan was insane:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  GitHub integration&lt;/li&gt;
&lt;li&gt;  Passbook integration&lt;/li&gt;
&lt;li&gt;  Accounts managemnet&lt;/li&gt;
&lt;li&gt;  Todo Apps&lt;/li&gt;
&lt;li&gt;  Custom admin dashboard&lt;/li&gt;
&lt;li&gt;  AI-powered suggestions&lt;/li&gt;
&lt;li&gt;  Native Mobile app&lt;/li&gt;
&lt;li&gt;  Dark mode (obviously)&lt;/li&gt;
&lt;li&gt;  Kanban boards&lt;/li&gt;
&lt;li&gt;  Budget system&lt;/li&gt;
&lt;li&gt;  Reports and analytics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I spent three weeks just setting up the database schema. Then I realized I was doing it again—building the app which is only needed by me or maybe few others when I launch&lt;/p&gt;

&lt;p&gt;So I stopped. Started over. Built the dumbest version possible: a single page where you could add expenses only no balance maintence nothing. That's it. No database, just localStorage. No user accounts, no fancy UI. Just a working app for tracking transactions and exporting them&lt;/p&gt;

&lt;p&gt;I shipped it in two days.&lt;/p&gt;

&lt;p&gt;And you know what? People used it. My friends started asking for the link. I got feedback I never expected. Some wanted categories. Others wanted due dates. A few asked about multiple accounts features&lt;/p&gt;

&lt;p&gt;But here's the key: I learned what people actually need, not what my delusional brain thought they needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Apply MVP Thinking Now
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The "One Problem" Rule
&lt;/h3&gt;

&lt;p&gt;Every project starts with one question: What's the ONE problem this solves? Not five problems. Not "it could also do this and that." One problem. My current project is expense tracker. The one problem it solves: I keep forgetting just stroing expense and nothing more than that.&lt;/p&gt;

&lt;p&gt;That's it. Not "social media for sharing things" or "Bank account passbook" Just "store and generate csvs of your transactions."&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The 80/20 Feature Cut
&lt;/h3&gt;

&lt;p&gt;I list every feature I can imagine, then cross out 80% of them.&lt;/p&gt;

&lt;p&gt;The remaining 20% becomes version 1. The crossed-out features? They go in a "maybe later" file. Most of the time, "later" never comes—and that's perfectly fine that feature may not be worth that time and effort maybe it will become a feature that is just there and only you use it and no one else which is obviously bad.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The Two-Week Test
&lt;/h3&gt;

&lt;p&gt;If I can't build a working version in two weeks, the scope is too big. This isn't about rushing or cutting quality. It's about being realistic. Two weeks forces me to focus on what's actually essential versus what's just nice to have and not waste time in thinking peculiar edge cases which may be only hypothetical.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. The "Embarrassing" Launch
&lt;/h3&gt;

&lt;p&gt;I launch when I'm slightly embarrassed by how basic it looks. This was hard to learn. I'm a perfectionist since I've been in my mind. But I realized that "slightly embarrassing" to me is often "perfectly adequate" to users what I seem imprefect everyone using my app is fine with like I still think the UI could be way better but it works and helps me focus on features first. And shipping something imperfect beats not shipping anything perfect.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Compound Effect of Small Wins
&lt;/h2&gt;

&lt;p&gt;Here's what I didn't expect: shipping small things consistently builds momentum faster than planning big things perfectly. When you plan things you get into a world where everything seems like ideal like the bodies in physics they do not apply in real world and maybe are too redundent for users like I told earlier some features may seem apealing to you but may not be useful for the users.&lt;/p&gt;

&lt;p&gt;Every completed project teaches you something. Every launch gives you confidence. Every piece of feedback makes the next version better and if you only plan and plan you will only plan and gain no expeirence in theory everything seems very easy like only I have to push it to vercel or netlify and the project is deployed but what if the time comes and you have to run it on a custom VPS how will you be able to do that.&lt;/p&gt;

&lt;p&gt;My unfinished projects taught me nothing except how to start things. My finished MVPs taught me how products actually work in the real world and what to expect when deploying them instead of thinking this feature would be a great thing or like make my app seem better to users as a solo developer you can not know if what are you thinking will be appealing to users hence you need to launch the bare minimum to get the things your users think what they actually need and what are just nice to have.&lt;/p&gt;

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

&lt;p&gt;The MVP mindset isn't just about building products faster (though it does that). It's about building products that people actually want what makes your core idea speak rather than a polished ui of a similar kind of app.&lt;/p&gt;

&lt;p&gt;Every feature you don't build is a feature that can't break, can't confuse users, and can't distract from your core value proposition. Constraints force creativity. Limitations inspire focus. It helps you to be more productive and a better person in hitting the deadlines better.&lt;/p&gt;

&lt;p&gt;Your first version doesn't need to be your final vision. It just needs to be the first step toward it. So what's your skateboard? What's the simplest thing you can build that solves a real problem for real people?&lt;/p&gt;

&lt;p&gt;Stop planning the car. Start building the skateboard it will then lead to better car than you expect your car to be it will not just run it will break all the records&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F77ptmisz4lqq3hxb59a3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F77ptmisz4lqq3hxb59a3.png" alt="A developer at their desk with a simple app on screen" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; MVP isn't about building cheap products—it's about learning fast and building what people actually want. Start small, ship quickly, iterate based on real feedback.&lt;/p&gt;

</description>
      <category>mvp</category>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>From Web to Mobile: Building PWAs with Next.js &amp; Bubblewrap</title>
      <dc:creator>DivyanshuLohani</dc:creator>
      <pubDate>Tue, 17 Jun 2025 14:19:01 +0000</pubDate>
      <link>https://dev.to/divyanshulohani/from-web-to-mobile-building-pwas-with-nextjs-bubblewrap-337b</link>
      <guid>https://dev.to/divyanshulohani/from-web-to-mobile-building-pwas-with-nextjs-bubblewrap-337b</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;The Realization That Hit Me Like a Truck&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;So there I was, scrolling through my phone, and I noticed something weird. Almost every "app" I was using daily wasn't really an app—it was just a website pretending to be one. Twitter, Instagram, even some banking apps. They loaded fast, worked offline, and felt native.&lt;/p&gt;

&lt;p&gt;That's when it clicked: &lt;strong&gt;Progressive Web Apps (PWAs)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I'd been building React/Next.js apps for years, but I was missing this whole world where you could take your web app and make it feel like a proper mobile app. No app store approval hell. No learning Swift or Kotlin. Just good old web tech doing mobile things.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Wait, What Actually IS a PWA?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Okay, so before I dive into the how-to, let me break down what PWAs actually are—because I definitely overthought this at first.&lt;/p&gt;

&lt;p&gt;A PWA is basically a website that acts like a mobile app. It can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install on your phone like a real app&lt;/li&gt;
&lt;li&gt;Work offline (kind of)&lt;/li&gt;
&lt;li&gt;Send push notifications&lt;/li&gt;
&lt;li&gt;Access device features like camera, GPS, etc.&lt;/li&gt;
&lt;li&gt;Feel smooth and native-ish&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The magic happens through something called a &lt;strong&gt;Service Worker&lt;/strong&gt; (handles offline stuff) and a &lt;strong&gt;Web App Manifest&lt;/strong&gt; (tells the browser how to behave like an app).&lt;/p&gt;

&lt;p&gt;And the best part? If you're already building with Next.js, you're like 70% there already.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Why I Went Down This Rabbit Hole&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;I had this side project that worked great on desktop, but mobile users kept asking "where's the app?" Every time someone visited on mobile, they'd try to find it in the app store.&lt;/p&gt;

&lt;p&gt;Building separate iOS and Android apps felt like overkill for what was essentially a web app. Enter PWAs and &lt;strong&gt;Bubblewrap&lt;/strong&gt;—Google's tool that takes your PWA and wraps it into a proper Android app you can actually publish to the Play Store.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmuafeyt4ea1x850r3kmy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmuafeyt4ea1x850r3kmy.png" alt="Screenshot of PWA vs native app comparison" width="750" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 1: Icons (Because First Impressions Matter)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You can't have a proper app without proper icons. I used to spend hours in Figma creating different icon sizes, but there's a much easier way.&lt;/p&gt;

&lt;p&gt;Head over to &lt;a href="https://pwa-icon-generator.vercel.app/" rel="noopener noreferrer"&gt;https://pwa-icon-generator.vercel.app/&lt;/a&gt; and upload your base icon. This tool generates all the sizes you need—from tiny 16x16 favicons to massive 512x512 app icons.&lt;/p&gt;

&lt;p&gt;Download the zip, extract it to your &lt;code&gt;public&lt;/code&gt; directory, and boom—icon problem solved.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 2: The Manifest (Next.js Makes This Easy)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here's where Next.js really shines. Instead of manually creating a &lt;code&gt;manifest.json&lt;/code&gt; file, you can create a &lt;code&gt;manifest.ts&lt;/code&gt; file in your &lt;code&gt;app&lt;/code&gt; directory and Next.js will automatically generate the webmanifest for you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MetadataRoute&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;next&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;MetadataRoute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Manifest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="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;My Awesome App&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;short_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;AwesomeApp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A really cool web application&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;start_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;standalone&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;background_color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#ffffff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;theme_color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#2563eb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;orientation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;portrait&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;en-US&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&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;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;icons&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/icon512_maskable.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;512x512&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/png&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="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/icon192.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;192x192&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/png&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;display: "standalone"&lt;/code&gt; is crucial—it tells the browser to hide the address bar and make your app feel like a native one.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 3: Enter Bubblewrap (The Game Changer)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This is where the magic happens. Bubblewrap is a CLI tool that takes your PWA and packages it into a proper Android app.&lt;/p&gt;

&lt;p&gt;First, install it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @bubblewrap/cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;IMPORTANT&lt;/strong&gt;: Do NOT run this in your Next.js project root. Seriously. I learned this the hard way when I accidentally pushed Android build files to production and my app crashed. Create a separate folder for this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;my-app-android
&lt;span class="nb"&gt;cd &lt;/span&gt;my-app-android
bubblewrap init &lt;span class="nt"&gt;--manifest&lt;/span&gt; https://yourapp.com/manifest.webmanifest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bubblewrap will ask you a bunch of questions—app name, package name, keystore password, etc. Just answer honestly, and it'll set up the entire Android project structure for you.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;[Image placeholder: Terminal showing bubblewrap init process]&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 4: Building Your App&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Once the setup is done, building is surprisingly simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bubblewrap build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An APK file (for direct installation)&lt;/li&gt;
&lt;li&gt;An AAB file (for Play Store upload)&lt;/li&gt;
&lt;li&gt;All the necessary Android project files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The build process takes a few minutes, but when it's done, you have a real Android app that you can install on any device.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 5: The URL Bar Problem (And How to Fix It)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here's something that tripped me up initially. Even after all this setup, your app might still show that ugly browser address bar. That's because Android needs to verify that your website actually "owns" this app.&lt;/p&gt;

&lt;p&gt;You fix this by creating a file at &lt;code&gt;public/.well-known/assetlinks.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"relation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"delegate_permission/common.handle_all_urls"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"namespace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"android_app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"package_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"com.myapp.example.twa"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"sha256_cert_fingerprints"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"A1:B2:C3:D4:E5:F6:G7:H8:I9:J0:K1:L2:M3:N4:O5:P6:Q7:R8:S9:T0:U1:V2:W3:X4:Y5:Z6"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"relation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"check_validation"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"namespace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"android_app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"package_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"com.myapp.example.twa"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get that SHA256 fingerprint, run this command in your Android project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;keytool &lt;span class="nt"&gt;-list&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-keystore&lt;/span&gt; android.keystore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enter the password you set during bubblewrap init, and you'll see output like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Certificate fingerprints:
    SHA1: 12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78
    SHA256: A1:B2:C3:D4:E5:F6:G7:H8:I9:J0:K1:L2:M3:N4:O5:P6:Q7:R8:S9:T0:U1:V2:W3:X4:Y5:Z6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy that SHA256 value into your assetlinks.json file.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The Play Store Gotcha&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you're planning to publish to the Google Play Store, there's one more step. Google re-signs your app with their own key, so you'll need to add their SHA256 fingerprint to your assetlinks.json file as well.&lt;/p&gt;

&lt;p&gt;You can find this in the Google Play Console under Setup → App Signing. Just add it as a second entry in the fingerprints array. &lt;a href="https://dev.to/hyunsoong_i/twa-apps-how-to-hide-the-url-bar-browser-bar-and-display-app-as-full-screen-4cm3"&gt;A detailed guide&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What Actually Changed for Me&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;After going through this whole process, here's what I gained:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real app experience&lt;/strong&gt;: Users can install it from the Play Store, it appears in their app drawer, and it feels native.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better engagement&lt;/strong&gt;: People are way more likely to use something that's "installed" vs bookmarked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline capability&lt;/strong&gt;: With service workers, core functionality works even without internet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One codebase&lt;/strong&gt;: I'm maintaining one Next.js app instead of separate web and mobile codebases.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The Reality Check&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Let's be honest—PWAs aren't perfect. iOS support is still weird (thanks, Apple). Some device features are limited. And you'll still need to understand Android development concepts if you want to customize things.&lt;/p&gt;

&lt;p&gt;But for most web apps that want mobile presence, this approach is &lt;em&gt;so much faster&lt;/em&gt; than building separate native apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;So yeah—&lt;strong&gt;PWAs aren't just a buzzword&lt;/strong&gt;. If you're already building with Next.js and want to tap into the mobile app ecosystem without learning entirely new technologies, this is probably your best bet.&lt;/p&gt;

&lt;p&gt;The whole process took me maybe 3-4 hours from start to finish (including the learning curve and that one time I accidentally nuked my build folder).&lt;/p&gt;

&lt;p&gt;I'm still experimenting with advanced PWA features like background sync and push notifications, but so far, this has been one of the &lt;em&gt;smoothest paths from web to mobile&lt;/em&gt; I've ever taken.&lt;/p&gt;

&lt;p&gt;If you're sitting on a Next.js app wondering how to make it "feel more appy"—&lt;strong&gt;maybe it's time to go PWA&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>nextjs</category>
      <category>pwa</category>
      <category>pwabuilder</category>
    </item>
    <item>
      <title>Building Your Online Dev Identity (Before It's Too Late)</title>
      <dc:creator>DivyanshuLohani</dc:creator>
      <pubDate>Mon, 09 Jun 2025 06:57:05 +0000</pubDate>
      <link>https://dev.to/divyanshulohani/building-your-online-dev-identity-before-its-too-late-3015</link>
      <guid>https://dev.to/divyanshulohani/building-your-online-dev-identity-before-its-too-late-3015</guid>
      <description>&lt;h2&gt;
  
  
  In 2025, Your Resume is Your Feed
&lt;/h2&gt;

&lt;p&gt;The best job offer you can get today isn't from a fancy MNC or a funded startup—it's from yourself, from the brand you've built online. Having your own identity online, shaped not by a company or a brand but by your own presence, is now a legitimate career path. It takes time, no doubt. But if you don't start today, it'll always stay "someday."&lt;/p&gt;

&lt;p&gt;The world has shifted. Traditional hiring processes are broken. HR departments are drowning in applications, and most resumes never even get seen by human eyes. Meanwhile, the most interesting opportunities are happening in DMs, through network connections, and from people who've been following your work for months. Your GitHub contributions, your blog posts, your hot takes on the latest JavaScript framework—that's your real resume now.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Regret (and How I Fixed It)
&lt;/h2&gt;

&lt;p&gt;I've been into development for over 6 years. I used to think, "Why post online? Who cares? If I'm good, people will find me." That's not how it works.&lt;/p&gt;

&lt;p&gt;For years, I was the typical "head down, code hard" developer. I believed in meritocracy—that good work speaks for itself. I'd build amazing projects, solve complex problems, and then... nothing. No one knew about it except my immediate team. I was essentially invisible in an industry where visibility increasingly matters.&lt;/p&gt;

&lt;p&gt;The wake-up call came when I saw junior developers with half my experience landing better opportunities simply because they had an online presence. They weren't necessarily better developers, but they were better at showing their work. That stung, but it was the reality check I needed.&lt;/p&gt;

&lt;p&gt;In 2023, I realized the harsh truth: No one is going to dream about you and magically send an opportunity your way. You have to tell the world, again and again, that you exist. And so, in 2024, I made a shift.&lt;/p&gt;

&lt;p&gt;I started posting updates about my dev projects, small wins, bugs I fixed, new tech I was trying—all on X (formerly Twitter). Then came blog posts: things I built, lessons learned, opinion pieces on tools, tech, or just stuff trending in the dev space. Hackathon updates, micro-tutorials—basically anything I was doing or learning.&lt;/p&gt;

&lt;p&gt;The hardest part? Getting over the imposter syndrome. Who was I to have opinions about React when Dan Abramov exists? Who cares about my struggles with Docker when there are experts everywhere? But here's the thing—beginners and intermediate developers learn better from people just a step ahead of them, not from the untouchable experts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Talking to a Wall (At First)
&lt;/h2&gt;

&lt;p&gt;nitially? It felt like shouting into a void. No followers. No engagement. Maybe a cousin would like it once in a while. But I kept writing.&lt;/p&gt;

&lt;p&gt;Those first few months were brutal for the ego. I'd spend hours crafting what I thought was a brilliant post about some obscure bug I'd fixed, only to get zero reactions. I started questioning everything—was my content bad? Was I posting at the wrong times? Was I even cut out for this?&lt;/p&gt;

&lt;p&gt;I experimented with reels and YouTube videos—but honestly, I was spending more time editing than coding. The pressure to make things “viral” took away from the joy of development itself. So I backed off from daily content and chose a sustainable pace instead. (Yes, you can still check out my &lt;a href="https://www.youtube.com/@divyanshuxwb" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; and &lt;a href="https://www.instagram.com/divyanshuxwb" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This was a crucial lesson: sustainability beats perfection. I'd rather post one genuine article a week than burn out trying to create daily "content" that felt forced. The algorithm rewards consistency, but your sanity should come first.&lt;/p&gt;

&lt;p&gt;Then, out of nowhere, one article hit. It wasn't even my magnum opus—just a breakdown of a Skribbl clone I had built, paired with a few improvements I was thinking of. It was honest, scrappy, and unpolished—but real. And it resonated. Slowly, views started to roll in. A few comments, some shares. Nothing viral—just 500–600 views. But when you're starting from zero, that's momentum. It meant someone out there was listening. It felt validating.&lt;/p&gt;

&lt;p&gt;That article taught me something important: authenticity beats polish. People connect with real struggles and genuine insights more than they connect with perfectly curated content.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Snowball Effect
&lt;/h2&gt;

&lt;p&gt;Now, even when I don’t have time for constant updates, I make sure I push 2–3 solid articles a month. These usually focus on a few key areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Problems I’m facing&lt;/strong&gt; – Whether it’s a tricky bug, a frustrating tool, or just a confusing decision I had to make, I document it. Writing about these challenges not only helps others but helps me make sense of my process too.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;How I solved something weird&lt;/strong&gt; – When something breaks and I fix it in a way that feels clever (or stupid), I write it down. These kinds of posts are always relatable to other devs and spark the best discussions.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Random insights&lt;/strong&gt; – Sometimes I notice a pattern, trend, or just have a thought that might help others. These posts are spontaneous but surprisingly well received.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This isn’t about going viral. I’m not trying to be the next dev influencer. It’s about being consistent—because consistency builds presence. The more I post, the more searchable and discoverable I become. And maybe someday, that consistency becomes the reason someone reaches out with a cool opportunity.&lt;/p&gt;

&lt;h2&gt;
  
  
  So How Do You Start?
&lt;/h2&gt;

&lt;p&gt;There is nothing I would say that is a secret sauce that I would tell you. You already know what you have to do and you're just lazy or don't feel like doing it but here are the steps anyways if you want there is nothing fancy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Pick a Platform:&lt;/strong&gt; X/Twitter is great for daily thoughts. Dev.to, Medium, Hashnode for longer articles. YouTube/Instagram if you like visual formats.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Share What You Do:&lt;/strong&gt; Doesn’t have to be mind-blowing. Ship something? Share it. Broke something? Share that too.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Engage:&lt;/strong&gt; Comment on others' posts. Be part of the community. You’ll learn &lt;em&gt;and&lt;/em&gt; get visibility.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Repeat:&lt;/strong&gt; Keep showing up, even if it feels like no one’s watching.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why It Matters
&lt;/h2&gt;

&lt;p&gt;When you have an online identity, it matters way more than you think:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  People trust you more because they see the proof of work online and are confident on you and they can be sure you will be the one to finish their job&lt;/li&gt;
&lt;li&gt;  Recruiters find &lt;em&gt;you&lt;/em&gt;, not the other way around, its same as you know your teacher but the teacher you know teaches hundreds of kids so you have to be special to be in his eyes.&lt;/li&gt;
&lt;li&gt;  Once you gain traction you will also start getting inboud requests no more hassle of going on to freelancing platforms to find work your inbox will be filled of it.&lt;/li&gt;
&lt;li&gt;  You become what you wanted to become in less time because these things put up a high impact.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;If you’re learning dev or already good at it, your work deserves an audience. Start small. Stay consistent. Don’t care about metrics at first—just build.&lt;/p&gt;

&lt;p&gt;Your online presence is like compound interest: the earlier you start, the bigger it grows.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Start building your personal dev brand today. The internet won’t find you on its own.&lt;/p&gt;

&lt;p&gt;#PersonalBranding #DevJourney #ContentCreation #DeveloperLife&lt;/p&gt;

</description>
      <category>devops</category>
      <category>web</category>
      <category>programming</category>
      <category>fullstack</category>
    </item>
    <item>
      <title>Payment Gateway Chaos: How I Converted Multiple Providers into One</title>
      <dc:creator>DivyanshuLohani</dc:creator>
      <pubDate>Sun, 25 May 2025 20:25:35 +0000</pubDate>
      <link>https://dev.to/divyanshulohani/payment-gateway-chaos-how-i-converted-multiple-providers-into-one-25bd</link>
      <guid>https://dev.to/divyanshulohani/payment-gateway-chaos-how-i-converted-multiple-providers-into-one-25bd</guid>
      <description>&lt;p&gt;Picture this: You're building an app, everything's going smooth, and then your client drops the bomb — &lt;em&gt;"Can we support PayU, Stripe, and maybe Razorpay too? You know, for flexibility."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Suddenly you're staring at three different APIs, each with their own quirks, data formats, and authentication methods. PayU wants a hash, Stripe wants sessions, and don't even get me started on their different ways of handling amounts (seriously, who decided some should use paise and others should use rupees?).&lt;/p&gt;

&lt;p&gt;I recently found myself in this exact situation while working on a project, and instead of copy-pasting code like a barbarian, I decided to build something that wouldn't make me want to throw my laptop out the window every time I needed to add a new gateway.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Payment Gateway Spaghetti
&lt;/h2&gt;

&lt;p&gt;Most developers handle multiple payment gateways like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Create separate functions for each gateway&lt;/li&gt;
&lt;li&gt;  Copy-paste similar logic everywhere&lt;/li&gt;
&lt;li&gt;  End up with a maintenance nightmare&lt;/li&gt;
&lt;li&gt;  Cry a little when requirements change&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's got to be a better way, right? &lt;strong&gt;&lt;em&gt;Right&lt;/em&gt;&lt;/strong&gt;&lt;em&gt;?&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Abstract the Chaos Away
&lt;/h2&gt;

&lt;p&gt;Here's the pattern that saved my sanity (and probably my career):&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create a Universal Payment Interface
&lt;/h3&gt;

&lt;p&gt;First, I created a common type that could handle all payment gateways without losing my mind:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;PurchaseTransactionData&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;itemId&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="nl"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;itemType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PurchaseType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;productinfo&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="nl"&gt;gateway&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PaymentGateway&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;PaymentData&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Common fields for all payment gateways&lt;/span&gt;
      &lt;span class="nl"&gt;gateway&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PaymentGateway&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;transactionId&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="nl"&gt;amount&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="nl"&gt;productinfo&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="nl"&gt;firstname&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="nl"&gt;email&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="nl"&gt;phone&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="nl"&gt;surl&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="nl"&gt;furl&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="c1"&gt;// PayU specific fields&lt;/span&gt;
      &lt;span class="nl"&gt;txnid&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="nl"&gt;hash&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="nl"&gt;key&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="c1"&gt;// Stripe specific fields&lt;/span&gt;
      &lt;span class="nl"&gt;stripePayUrl&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="nl"&gt;publishableKey&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Is it perfect? No. Does it work? Absolutely. The key insight here is that you need &lt;strong&gt;one interface to rule them all&lt;/strong&gt; — even if some fields are gateway-specific.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: The Abstract Base Class (The Real MVP)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&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;PaymentData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PurchaseTransactionData&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;@/types/payment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PaymentStatus&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;@repo/db&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentGatewayBase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="nf"&gt;createPaymentData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;transactionData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PurchaseTransactionData&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;PaymentData&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;async&lt;/span&gt; &lt;span class="nf"&gt;createTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transactionData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PurchaseTransactionData&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentTransaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transactionData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PaymentStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PENDING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transactionData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;gateway&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transactionData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gateway&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;itemId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transactionData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;itemId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;itemType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transactionData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;itemType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This abstract class does two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Forces&lt;/strong&gt; every gateway implementation to have a createPaymentData method&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Handles&lt;/strong&gt; the common transaction creation logic so you don't repeat yourself&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 3: Gateway Implementations (Where the Magic Happens)
&lt;/h3&gt;

&lt;p&gt;Now here's where it gets fun. Each gateway just extends the base class and implements their specific logic:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PayU Implementation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PayUGateway&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;PaymentGatewayBase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createPaymentData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;transactionData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PurchaseTransactionData&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;PaymentData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;merchantKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;merchantSalt&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getPayUCredentials&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;merchantKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;gateway&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PaymentGateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PAYU&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;txnid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// PAYU wants floats&lt;/span&gt;
          &lt;span class="na"&gt;productinfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transactionData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;productinfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;firstname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transactionData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John Doe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transactionData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1234567890&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;surl&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="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_SITE_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/payu-response?status=success`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;furl&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="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_SITE_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/payu-response?status=failure`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateHash&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;merchantSalt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;merchantKey&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;data&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;Stripe Implementation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StripeGateway&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;PaymentGatewayBase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createPaymentData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PaymentTransaction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;transactionData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PurchaseTransactionData&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;PaymentData&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;stripeCredentials&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;getStripeCredentials&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;stripe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Stripe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stripeCredentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;secretKey&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;session&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;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessions&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;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;payment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;customer_email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transactionData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;client_reference_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;line_items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;price_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;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;inr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;product_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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transactionData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;productinfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="na"&gt;unit_amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transactionData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Stripe wants paise&lt;/span&gt;
              &lt;span class="p"&gt;},&lt;/span&gt;
              &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="na"&gt;billing_address_collection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;required&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;success_url&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="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_SITE_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/shop/?transactionId=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;cancel_url&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="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_SITE_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/stripe-response?transactionId=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;gateway&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PaymentGateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STRIPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;stripePayUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;publishableKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY&lt;/span&gt;&lt;span class="o"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: The Factory Pattern (Because We're Not Animals)
&lt;/h3&gt;

&lt;p&gt;But wait, there's more! Manually instantiating gateway classes everywhere is still messy. Enter the Factory pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PaymentGateway&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;@repo/db&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;server-only&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PaymentGatewayBase&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;./payment-base&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PayUGateway&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;./payu&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StripeGateway&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;./stripe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentGatewayFactory&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gateway&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PaymentGateway&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;PaymentGatewayBase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gateway&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;PaymentGateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PAYU&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PayUGateway&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
          &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;PaymentGateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STRIPE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StripeGateway&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
          &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Unsupported payment gateway: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;gateway&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="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;Now your server-side code becomes beautifully simple:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gateway&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PaymentGatewayFactory&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="nx"&gt;selectedGateway&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;transaction&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;gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transactionData&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;paymentData&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;gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createPaymentData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transactionData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Frontend Magic (The Cherry on Top)
&lt;/h3&gt;

&lt;p&gt;The frontend doesn't need to know about your fancy abstractions. It just needs to handle the response and redirect users accordingly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;paymentResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;paymentResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentData&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;gateway&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PAYU&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nf"&gt;redirectToPaymentPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;paymentResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;STRIPE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
            &lt;span class="nx"&gt;paymentResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentData&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;stripePayUrl&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unsupported payment gateway&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;p&gt;Each gateway gets its own redirect logic, but the main flow stays clean and predictable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F19oa639nwecevv0mwe1t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F19oa639nwecevv0mwe1t.png" alt="Factory pattern image" width="720" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Adding New Gateways (The Real Test)
&lt;/h3&gt;

&lt;p&gt;Now adding a new payment gateway is genuinely simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Create a new class that extends PaymentGatewayBase&lt;/li&gt;
&lt;li&gt; Implement the createPaymentData method&lt;/li&gt;
&lt;li&gt; Add the case to your Factory class&lt;/li&gt;
&lt;li&gt; Add the frontend redirect logic&lt;/li&gt;
&lt;li&gt; That's it. You're done.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Want to add Razorpay? Just create RazorpayGateway extends PaymentGatewayBase, add it to the factory, and handle the frontend redirect. No existing code needs to change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Beauty of This Approach&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This pattern gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Consistency&lt;/strong&gt;: Every gateway follows the same contract&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Maintainability&lt;/strong&gt;: Common logic lives in one place&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Scalability&lt;/strong&gt;: Adding new gateways doesn't break existing code&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Testability&lt;/strong&gt;: You can mock the abstract class for unit tests&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Sanity&lt;/strong&gt;: You won't hate your life when requirements change&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Payment gateway integration doesn't have to be a nightmare. With a little bit of abstraction and a lot of TypeScript magic, you can build something that's actually maintainable.&lt;/p&gt;

&lt;p&gt;The next time someone asks you to "just add one more payment gateway," you can smile knowingly instead of updating your resume.&lt;/p&gt;

&lt;p&gt;Now if you'll excuse me, I need to go figure out why Razorpay's webhook decided to stop working again. Some things never change.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>systemdesign</category>
      <category>stripe</category>
    </item>
    <item>
      <title>My Experience with TurboRepo &amp; Monorepos: From Chaos to Sanity</title>
      <dc:creator>DivyanshuLohani</dc:creator>
      <pubDate>Sun, 20 Apr 2025 04:47:17 +0000</pubDate>
      <link>https://dev.to/divyanshulohani/my-experience-with-turborepo-monorepos-from-chaos-to-sanity-1h0f</link>
      <guid>https://dev.to/divyanshulohani/my-experience-with-turborepo-monorepos-from-chaos-to-sanity-1h0f</guid>
      <description>&lt;h2&gt;
  
  
  A Dev Setup That Started Like Everyone Else’s
&lt;/h2&gt;

&lt;p&gt;If you’ve ever worked on a full-stack project, chances are your folder structure looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/client  
/server  
/README.md (you never updated)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And at first—it’s fine. Until one day, I was debugging something and realize that I am just copy-pasting the same utility function between folders and breaking the DRY rule.&lt;/p&gt;

&lt;p&gt;Then the installs break. Then the dependency trees spiral. Then I am 10 commits deep into fixing a node_modules issue and forget what the feature even was.&lt;/p&gt;

&lt;p&gt;Welcome to my life… until I discovered &lt;strong&gt;Monorepos&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wait, What Even Is a Monorepo?
&lt;/h2&gt;

&lt;p&gt;So, I had heard the term floating around in dev Twitter and Reddit threads, but I never really &lt;em&gt;got it&lt;/em&gt;. I thought it was just one of those "enterprise-only" things Google does.&lt;/p&gt;

&lt;p&gt;but it turend out—it’s just keeping all your packages, apps, and services in &lt;strong&gt;one codebase&lt;/strong&gt;, organized and connected like a family that actually talks to each other.&lt;/p&gt;

&lt;p&gt;You can share code. You can isolate builds. You can run tests on specific packages. And yeah, &lt;a href="https://www.linkedin.com/company/google/" rel="noopener noreferrer"&gt;Google&lt;/a&gt; does it. That was enough to get me curious.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enter: TurboRepo
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia.licdn.com%2Fdms%2Fimage%2Fv2%2FD5612AQGrrYHfHPy4aw%2Farticle-inline_image-shrink_1000_1488%2FB56ZZPdhrDHQAQ-%2F0%2F1745089862776%3Fe%3D1750896000%26v%3Dbeta%26t%3DFGgYSfWvsJuvrI_g3BZ3IRQ1bPGteM-hAayTnHdkQ1c" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia.licdn.com%2Fdms%2Fimage%2Fv2%2FD5612AQGrrYHfHPy4aw%2Farticle-inline_image-shrink_1000_1488%2FB56ZZPdhrDHQAQ-%2F0%2F1745089862776%3Fe%3D1750896000%26v%3Dbeta%26t%3DFGgYSfWvsJuvrI_g3BZ3IRQ1bPGteM-hAayTnHdkQ1c" alt="Turbo Repo" width="300" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I stumbled across TurboRepo, and the first thing I thought was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Wait, why didn’t anyone tell me about this sooner?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s a &lt;strong&gt;high-performance build system for monorepos&lt;/strong&gt;—designed to make your life less painful when managing multiple apps and packages in a single repo. And the cool part? It's designed for JS/TS devs like us.&lt;/p&gt;

&lt;p&gt;No 20-step YAML configs. Just set up your pipeline, link things with workspaces, and boom—stuff starts working.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Switch: Folder Chaos to Turbo Calm
&lt;/h2&gt;

&lt;p&gt;I had an ongoing project with the usual split: one folder for the frontend (React), another for the backend (Express).&lt;/p&gt;

&lt;p&gt;Converting that into a Turbo monorepo setup only took me &lt;strong&gt;an hour or two&lt;/strong&gt;. (And that too with Express, which has like &lt;em&gt;zero&lt;/em&gt; real monorepo resources out there—someone fix this please. (Article soon :)))&lt;/p&gt;

&lt;p&gt;Now it looks more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/apps  
  /web  
  /api  
/packages  
  /ui  
  /utils  
  /config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And suddenly, my build scripts aren’t yelling at me. My eslint config is shared. And I'm not doing npm install in two places like an absolute fool.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Benefits I Felt
&lt;/h2&gt;

&lt;p&gt;Let’s be real, here’s what actually changed for me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;One place for all code&lt;/strong&gt;: No more syncing packages manually or debugging two node_modules folders.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Code sharing became easy&lt;/strong&gt;: Utils, types, and configs now live in one packages/ folder and are shared across both frontend and backend.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Less context switching&lt;/strong&gt;: I don't need to cd into three places just to start the dev server.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;My disaster folder got fixed&lt;/strong&gt;: I once broke a project so badly because I didn’t understand workspaces properly. Turbo fixed that mess, cleaned up the structure, and gave me a fresh start.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Ease to Scale:&lt;/strong&gt; Now it would be easier to add more things like admin or something like that.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Not All Rainbows
&lt;/h2&gt;

&lt;p&gt;Turbo is powerful, but it’s also a bit opinionated. Some things still need digging around, especially if you’re working outside the Next.js happy path.&lt;/p&gt;

&lt;p&gt;But once you get it running—it’s worth it.&lt;/p&gt;

&lt;p&gt;(Also, don’t forget to &lt;strong&gt;cache smartly&lt;/strong&gt;. Turbo's speed magic depends on it.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;So yeah—&lt;strong&gt;monorepos are not just for big corps&lt;/strong&gt;. If you're building multi-package apps, it makes total sense even for solo devs or small teams. And TurboRepo? Chef's kiss. &lt;strong&gt;🤌&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’m still experimenting and learning, especially with backend-heavy setups (&lt;em&gt;looking at you, Express&lt;/em&gt;). But so far, I’d say this is one of the &lt;em&gt;cleanest architectural decisions&lt;/em&gt; I’ve made in months.&lt;/p&gt;

&lt;p&gt;If you’re stuck in the client-server folder spiral—&lt;strong&gt;maybe it’s time to go mono&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>monorepo</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>How I Built My Own CI/CD Pipeline for a Small Project (Because GitHub Said “No”)</title>
      <dc:creator>DivyanshuLohani</dc:creator>
      <pubDate>Tue, 01 Apr 2025 07:12:15 +0000</pubDate>
      <link>https://dev.to/divyanshulohani/how-i-built-my-own-cicd-pipeline-for-a-small-project-because-github-said-no-2g2l</link>
      <guid>https://dev.to/divyanshulohani/how-i-built-my-own-cicd-pipeline-for-a-small-project-because-github-said-no-2g2l</guid>
      <description>&lt;h2&gt;
  
  
  Why I Ditched GitHub Actions
&lt;/h2&gt;

&lt;p&gt;So long story short no one wants to make systems of their own and why should we there are great minds who have devoted years in perfecting it. Like most developers, my first instinct for &lt;strong&gt;CI/CD&lt;/strong&gt; was to use &lt;strong&gt;GitHub Actions&lt;/strong&gt;—it’s built-in, well-documented, and mostly reliable. &lt;strong&gt;Until it wasn’t.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For some unknown reason, my GitHub Actions &lt;strong&gt;got blocked&lt;/strong&gt;. I submitted a support ticket, and after a quick look at GitHub’s response time (&lt;em&gt;which was somewhere between a month and eternity&lt;/em&gt;), I knew I had to find another solution.&lt;/p&gt;

&lt;p&gt;Waiting wasn’t an option. I needed &lt;strong&gt;continuous deployment now&lt;/strong&gt;, (&lt;em&gt;ego issues)&lt;/em&gt; not when GitHub finally decides to reply to my ticket.&lt;/p&gt;

&lt;p&gt;So, I did what any frustrated developer would do—I built my own &lt;strong&gt;custom CI/CD pipeline&lt;/strong&gt; from scratch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Setting Up My Own CI/CD Server
&lt;/h2&gt;

&lt;p&gt;Since GitHub wasn’t going to help me, I set up a &lt;strong&gt;self-hosted deployment server&lt;/strong&gt; using &lt;strong&gt;Express.js&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The goal was simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Whenever I push new code to my repository, my &lt;strong&gt;server should detect it&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;  It should &lt;strong&gt;pull the latest code&lt;/strong&gt;, restart the app, and &lt;strong&gt;send me a notification&lt;/strong&gt; if something fails.&lt;/li&gt;
&lt;li&gt;  No manual intervention. &lt;strong&gt;Just push and deploy.&lt;/strong&gt; &lt;em&gt;(and flex about pusing to production)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sounds simple, right? &lt;strong&gt;It wasn’t.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: GitHub Webhooks
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fty3daw343bzu3jevph4u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fty3daw343bzu3jevph4u.png" alt="GITHUB WEBHOOKS" width="555" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After some research, I discovered that &lt;strong&gt;GitHub provides webhooks&lt;/strong&gt;, which can notify a server whenever specific events occur (like a push to the main branch or a pull request).&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up the Webhook Listener
&lt;/h3&gt;

&lt;p&gt;GitHub provides webhooks that trigger events whenever something happens in a repository, like pushing code, creating a pull request, or merging branches. My goal was to create a webhook that listens for a push event on the main branch, automatically pulling the latest code and redeploying the app.&lt;/p&gt;

&lt;p&gt;First, I created a simple Express.js server and added an endpoint to listen for webhook events&lt;/p&gt;

&lt;h3&gt;
  
  
  Signature Verification Nightmare
&lt;/h3&gt;

&lt;p&gt;If you think setting up a webhook is as easy as “just writing an endpoint,” let me save you some pain: &lt;strong&gt;GitHub doesn’t trust you.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every webhook request from GitHub is &lt;strong&gt;signed using a secret key&lt;/strong&gt;, and your server must &lt;strong&gt;verify this signature&lt;/strong&gt; to ensure it’s a legitimate request.&lt;/p&gt;

&lt;p&gt;Sounds simple? &lt;strong&gt;Not when the signature verification keeps failing.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For the next &lt;strong&gt;several hours&lt;/strong&gt;, I tried everything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Manually comparing signatures&lt;/strong&gt; (they never matched).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Re-reading GitHub’s docs&lt;/strong&gt; (which didn’t help).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Trying different hashing methods&lt;/strong&gt; (none worked).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At one point, I even considered disabling verification entirely—&lt;strong&gt;which is a terrible idea&lt;/strong&gt; unless you enjoy waking up to your server being hijacked.&lt;/p&gt;

&lt;p&gt;Finally, after &lt;strong&gt;digging through GitHub issues&lt;/strong&gt;, I realized my mistake:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  I wasn’t using the exact &lt;strong&gt;raw body of the request&lt;/strong&gt; for signature validation.&lt;/li&gt;
&lt;li&gt;  Instead, I was verifying the JSON-parsed body, which &lt;strong&gt;modifies the data slightly&lt;/strong&gt;, causing the signature to mismatch.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A few painful adjustments later, my &lt;strong&gt;signature verification finally passed.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Automating the Deployment Process
&lt;/h3&gt;

&lt;p&gt;With the webhook working, I automated the deployment with a series of commands:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Pull the latest code&lt;/strong&gt; from the main branch of the repository&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Install dependencies&lt;/strong&gt; (if any new one was added)&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Rebuild the project&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Restart the server process&lt;/strong&gt; to apply changes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With this setup, every time I pushed code to GitHub, my server &lt;strong&gt;automatically updated itself&lt;/strong&gt;. No manual pulling, no SSH logins—just smooth, hands-free deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Email Notifications with Resend
&lt;/h2&gt;

&lt;p&gt;I wanted &lt;strong&gt;immediate feedback&lt;/strong&gt; if something went wrong, so I integrated &lt;strong&gt;Resend&lt;/strong&gt;, a powerful email API.&lt;/p&gt;

&lt;p&gt;Now, after every deployment, I get an email saying either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;✅ Deployment Successful&lt;/strong&gt; – Life is good.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;❌ Deployment Failed&lt;/strong&gt; – Something broke, and I need to fix it ASAP.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This turned out to be one of the most useful parts of the setup because it meant &lt;strong&gt;I didn’t have to check manually&lt;/strong&gt; whether deployments were working.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned (Or, What I’d Do Differently)
&lt;/h2&gt;

&lt;p&gt;Building a custom CI/CD pipeline was an exciting journey, but it also came with its fair share of &lt;em&gt;facepalm&lt;/em&gt; moments. Here’s what I took away from this experience and what I’d change next time.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. A CI/CD Pipeline Is a Game Changer
&lt;/h3&gt;

&lt;p&gt;Before this, I used to manually SSH into my server, pull updates, restart processes, and &lt;em&gt;pray&lt;/em&gt; nothing broke. With this setup, deployments happen automatically, saving time and reducing human error. But what really hit me was how much &lt;strong&gt;mental overhead&lt;/strong&gt; it removed—no more worrying about forgetting a step or accidentally deploying a broken commit.&lt;/p&gt;

&lt;p&gt;That being said, there’s still a critical issue I didn’t expect and figured out while writing this post…&lt;/p&gt;

&lt;h3&gt;
  
  
  2. No Rollback Mechanism = Potential Disaster
&lt;/h3&gt;

&lt;p&gt;As much as I love automation, it comes with a &lt;strong&gt;huge risk&lt;/strong&gt;: what happens when a deployment &lt;strong&gt;fails&lt;/strong&gt; and breaks the entire server? Right now, my setup blindly pulls the latest code and restarts the app. If there’s a fatal bug in the pushed code, the app crashes, and &lt;em&gt;boom&lt;/em&gt;—downtime.&lt;/p&gt;

&lt;p&gt;What I should have done:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Implement a rollback system&lt;/strong&gt;: If deployment fails, revert to the last working commit instead of leaving the server broken.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Use a blue-green deployment approach&lt;/strong&gt;: Deploy to a separate instance first, verify stability, and only then switch traffic over &lt;em&gt;(maybe I will not go with this because I am a solo developer)&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next on my list is building a rollback mechanism that can detect failed deployments and automatically restore the last working version—because nothing’s worse than waking up to “Hey, your site is down.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;While most people &lt;strong&gt;rely on GitHub Actions&lt;/strong&gt;, it’s not the only way to do CI/CD. Having a &lt;strong&gt;custom deployment pipeline&lt;/strong&gt; gives you &lt;strong&gt;more control, fewer limitations, and zero dependencies&lt;/strong&gt; on external services.&lt;/p&gt;

&lt;p&gt;Sure, it took some effort to set up, but now I can deploy changes &lt;strong&gt;automatically and reliably&lt;/strong&gt;—without waiting for GitHub support to wake up.&lt;/p&gt;

&lt;p&gt;And while I figure out how to build a &lt;strong&gt;custom monitoring system&lt;/strong&gt;, at least I have &lt;strong&gt;CI/CD fully automated&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>github</category>
      <category>githubactions</category>
      <category>express</category>
    </item>
    <item>
      <title>The Vibe Coder: Non-Technical Developers and Mirage of AI</title>
      <dc:creator>DivyanshuLohani</dc:creator>
      <pubDate>Sun, 30 Mar 2025 07:49:09 +0000</pubDate>
      <link>https://dev.to/divyanshulohani/the-vibe-coder-non-technical-developers-and-mirage-of-ai-5h0o</link>
      <guid>https://dev.to/divyanshulohani/the-vibe-coder-non-technical-developers-and-mirage-of-ai-5h0o</guid>
      <description>&lt;h2&gt;
  
  
  The Rise of "Vibe Coding"
&lt;/h2&gt;

&lt;p&gt;So, "vibe coding" has been making waves recently, all thanks to some tweets about this guy who built an app using AI alone. No actual coding, just pure vibes. The result? His app got completely obliterated by the community and hackers alike. Turns out, AI may be smart, but it's not &lt;em&gt;that&lt;/em&gt; smart.&lt;/p&gt;

&lt;p&gt;But what exactly is vibe coding?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia.licdn.com%2Fdms%2Fimage%2Fv2%2FD5612AQHHLzUJLcZx-g%2Farticle-inline_image-shrink_1500_2232%2FB56ZXi06dgGQAU-%2F0%2F1743267277403%3Fe%3D1749081600%26v%3Dbeta%26t%3D94eHuUoIfcYXJWTbmMiB0_H87NnFDntWyvOuYkiYOJg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia.licdn.com%2Fdms%2Fimage%2Fv2%2FD5612AQHHLzUJLcZx-g%2Farticle-inline_image-shrink_1500_2232%2FB56ZXi06dgGQAU-%2F0%2F1743267277403%3Fe%3D1749081600%26v%3Dbeta%26t%3D94eHuUoIfcYXJWTbmMiB0_H87NnFDntWyvOuYkiYOJg" alt="Vibe coding" width="602" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Vibe Coding?
&lt;/h2&gt;

&lt;p&gt;Vibe coding is a development approach where instead of, you know, &lt;em&gt;coding&lt;/em&gt;, you just prompt an AI model like ChatGPT or Gemini to generate everything for you. The AI writes the logic, structures the app, and spits out whatever you need.&lt;/p&gt;

&lt;p&gt;Sounds futuristic, right? Just describe what you want, and boom—a full app ready to go. No need to learn syntax, algorithms, or debugging. Just &lt;em&gt;vibes&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Except… reality isn’t so kind.&lt;/p&gt;

&lt;p&gt;If you’ve spent any time on YouTube, you’ve probably seen those flashy AI coding videos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;“I built a full-stack app in 5 minutes using ChatGPT!”&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;“I made $10,000 with an AI-generated SaaS app!”&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;“No coding skills? No problem! AI will build your app for you!”&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It all sounds too good to be true—and most of the time, it is.&lt;/p&gt;

&lt;p&gt;Take, for example, the infamous case of a YouTuber who built an AI-generated startup. He used ChatGPT to code an entire project, launched it, and bragged about how easy it was. The catch? Within days, &lt;strong&gt;hackers tore the app apart&lt;/strong&gt; due to security flaws, and the entire thing crumbled under actual user traffic.&lt;/p&gt;

&lt;p&gt;Another creator tried making a full website using only AI-generated code. The result? A buggy mess with unoptimized performance, broken features, and missing authentication. He spent &lt;strong&gt;hours debugging&lt;/strong&gt; and ultimately had to rewrite most of the project manually.&lt;/p&gt;

&lt;p&gt;The bottom line: &lt;strong&gt;AI can generate code, but it doesn’t replace real development skills.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Harsh Reality of Vibe Coding
&lt;/h2&gt;

&lt;p&gt;A friend of mine—let’s call him &lt;em&gt;AI Enthusiast #47&lt;/em&gt;—was hyped about AI-generated development. He told me, &lt;em&gt;“Bro, you don’t even need to learn to code anymore! Just prompt the AI, and boom—crazy cool websites you're cooked bro”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So, naturally, I challenged him. &lt;em&gt;“Alright, build something.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Excitedly, he opened his favorite AI model and started prompting it to build a web app. After an hour of back-and-forth, he had something that looked decent—until he ran it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Errors everywhere.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Broken UI components.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;APIs not connecting properly.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;And worst of all? He had no idea how to fix it. (&lt;/strong&gt;&lt;strong&gt;&lt;em&gt;He's not a dev&lt;/em&gt;&lt;/strong&gt;&lt;strong&gt;)&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the end of the day, he spent &lt;strong&gt;more time prompting the errors to fix them&lt;/strong&gt; than if he had just written the app himself from scratch after learning to code. The AI-generated code was messy, inconsistent, and lacked documentation. After hours of frustration, he rage quit and admitted, &lt;em&gt;“Yeah, maybe I should learn the basics first.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is the problem with vibe coding—it sells a dream, but the reality is debugging a nightmare you don’t even understand.&lt;/p&gt;

&lt;p&gt;While LLMs (Large Language Models) can generate pretty solid code snippets, building a whole app? That’s another story.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Context Window Problem
&lt;/h3&gt;

&lt;p&gt;AI models have a limited memory span. They might remember what you asked them five prompts ago, but try building a complex app with multiple components, and they’ll forget critical details halfway through. They even forget what they were doing and just staright go to write something else. This means you’ll constantly be re-explaining your own app &lt;em&gt;to the AI&lt;/em&gt;, which is ironic because wasn’t the whole point to avoid writing code yourself?&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The 70% Bad Code Problem
&lt;/h3&gt;

&lt;p&gt;Most AI models are trained on publicly available code. And guess what? At least 70% of that code isn’t great—either outdated or present in repositories that were created to disrupt AI &lt;em&gt;(Okay last one was a joke),&lt;/em&gt; inefficient, or just plain wrong. If you generate an app entirely with AI, you’re likely inheriting a ton of vulnerabilities (&lt;em&gt;not talking about you Next JS&lt;/em&gt;), spaghetti code, and security risks and much more worse things such as law suits from the serverless architecture hosting companies for abusing their service.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Debugging is a Nightmare
&lt;/h3&gt;

&lt;p&gt;Let’s say you manage to generate a fully functional app (somehow &lt;em&gt;Not possible still&lt;/em&gt;). What happens when something breaks? What if you need more features and AI is unable to generate? Debugging AI-generated code is a horror story. You didn't write the logic so guessing the code is best way to go about it and since you wrote your code while vibing so you have no documentation even the model can't help you at this stage. Errors are just foreign text to you written in a different language.&lt;/p&gt;

&lt;p&gt;At this point, you’re left with two choices: spend hours untangling the AI’s mess and give our your few brain cells left or just rewrite the app from scratch (&lt;em&gt;this will be faster actually&lt;/em&gt;). And if you’re picking the second option, why even bother with vibe coding in the first place just to waste your time?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia.licdn.com%2Fdms%2Fimage%2Fv2%2FD5612AQGLZrxPgjo93Q%2Farticle-inline_image-shrink_1000_1488%2FB56ZXi56yJHoAQ-%2F0%2F1743268602676%3Fe%3D1749081600%26v%3Dbeta%26t%3DayqIZxdcl1eikjd3JYhrbwHaeSUdFXHPtDuPrl1NglM" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia.licdn.com%2Fdms%2Fimage%2Fv2%2FD5612AQGLZrxPgjo93Q%2Farticle-inline_image-shrink_1000_1488%2FB56ZXi56yJHoAQ-%2F0%2F1743268602676%3Fe%3D1749081600%26v%3Dbeta%26t%3DayqIZxdcl1eikjd3JYhrbwHaeSUdFXHPtDuPrl1NglM" alt="Problems with vibe coding" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My Thoughts on Vibe Coding
&lt;/h2&gt;

&lt;p&gt;Every few months, there’s a new AI breakthrough that makes headlines and sends the internet into a frenzy. Remember when &lt;strong&gt;Devin, the "first AI software engineer,"&lt;/strong&gt; was announced? People started saying, &lt;em&gt;"This is it! Developers are doomed!"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And then… nothing happened.&lt;/p&gt;

&lt;p&gt;Devin turned out to be just another AI coding assistant with a fancy label. It wasn’t writing full applications from scratch, wasn’t making high-level architectural decisions, and certainly wasn’t replacing skilled developers. The hype died down just as fast as it started.&lt;/p&gt;

&lt;p&gt;Vibe coding is, at best, a gimmick. A flashy marketing term that AI companies love to push, promising a future where anyone can build apps without learning to code. But software development isn’t just about generating code—it’s about understanding &lt;strong&gt;why&lt;/strong&gt; and &lt;strong&gt;how&lt;/strong&gt; things work why your refrigerator can display a &lt;em&gt;Friends&lt;/em&gt; episode.&lt;/p&gt;

&lt;p&gt;That said, I don’t think we should shame people who are just experimenting with AI. Exploring new tools is fine curiosity is good, but if someone’s entire plan for becoming a developer is to let AI do the work, they're not meant to be in software or any field in that matter. Learning the basics first is crucial.&lt;/p&gt;

&lt;p&gt;Oh, and if you’re interested in AI’s role in development, check out my previous article on AI and programming and beginners. &lt;a href="https://www.linkedin.com/pulse/when-use-ai-developers-perspective-divyanshu-lohani-1xaec" rel="noopener noreferrer"&gt;&lt;em&gt;here&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia.licdn.com%2Fdms%2Fimage%2Fv2%2FD5612AQEmgyuyEKs4xQ%2Farticle-inline_image-shrink_1000_1488%2FB56ZXi8ydoGQAU-%2F0%2F1743269357094%3Fe%3D1749081600%26v%3Dbeta%26t%3DBwTvequM_8PUltvFhyvsjdFsgtSxyOSDrDwUWah8FA4" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia.licdn.com%2Fdms%2Fimage%2Fv2%2FD5612AQEmgyuyEKs4xQ%2Farticle-inline_image-shrink_1000_1488%2FB56ZXi8ydoGQAU-%2F0%2F1743269357094%3Fe%3D1749081600%26v%3Dbeta%26t%3DBwTvequM_8PUltvFhyvsjdFsgtSxyOSDrDwUWah8FA4" alt="My thoughts on vibe coding" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Trends like vibe coding will come and go, but software development isn’t getting automated away anytime soon. AI is a tool—a powerful one—but it can’t replace actual developers. Instead, it’s best used to &lt;strong&gt;enhance&lt;/strong&gt; productivity, not as a replacement for fundamental knowledge.&lt;/p&gt;

&lt;p&gt;So, the next time you see someone talking about building a full app using &lt;em&gt;just&lt;/em&gt; AI, remember: they might be shipping products, but they’re also shipping a &lt;strong&gt;lot&lt;/strong&gt; of hidden problems with them.&lt;/p&gt;

&lt;p&gt;While I sit back and watch AI-generated apps crash and burn, I’ll stick to writing code that actually works and is mine.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>vite</category>
      <category>coding</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
