<?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: RjS</title>
    <description>The latest articles on DEV Community by RjS (@rjs).</description>
    <link>https://dev.to/rjs</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%2F1793909%2Fb9774226-6a2d-4508-847d-9853be19da30.png</url>
      <title>DEV Community: RjS</title>
      <link>https://dev.to/rjs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rjs"/>
    <language>en</language>
    <item>
      <title>System Architecture of React Playground: What Actually Happens Behind the Scenes</title>
      <dc:creator>RjS</dc:creator>
      <pubDate>Fri, 01 May 2026 13:55:40 +0000</pubDate>
      <link>https://dev.to/rjs/system-architecture-what-actually-happens-behind-the-scenes-51mk</link>
      <guid>https://dev.to/rjs/system-architecture-what-actually-happens-behind-the-scenes-51mk</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/rjs/designing-a-real-time-react-playground-with-workers-queues-and-websockets-4h8f"&gt;previous blog&lt;/a&gt;, I talked about what I built and the problems I faced.&lt;/p&gt;

&lt;p&gt;This time, I want to break down the architecture, not in a dry, textbook way, but in the same way I understood it while building it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Mental Model
&lt;/h2&gt;

&lt;p&gt;At a high level, this is what’s happening:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The frontend behaves like an IDE&lt;/li&gt;
&lt;li&gt;The backend acts like a coordinator&lt;/li&gt;
&lt;li&gt;Workers actually run your code&lt;/li&gt;
&lt;li&gt;Redis connects everything&lt;/li&gt;
&lt;li&gt;S3 stores user solutions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So let’s go step by step.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Writing Code (Frontend Sandbox)
&lt;/h2&gt;

&lt;p&gt;When you type JSX in the editor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monaco Editor captures the code&lt;/li&gt;
&lt;li&gt;Babel (standalone) transpiles JSX → JavaScript&lt;/li&gt;
&lt;li&gt;That JS is injected into an iframe&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why iframe?&lt;/p&gt;

&lt;p&gt;Because if your code crashes, I don’t want the entire app to crash with it.&lt;/p&gt;

&lt;p&gt;So the execution happens inside an isolated environment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Separate DOM&lt;/li&gt;
&lt;li&gt;Separate JS context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;React and ReactDOM are loaded via CDN, and then your component is rendered inside that iframe.&lt;/p&gt;

&lt;p&gt;This gives you instant feedback.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Submitting Code
&lt;/h2&gt;

&lt;p&gt;Now comes the important part.&lt;/p&gt;

&lt;p&gt;When you click “Submit”:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The raw JSX (not transpiled code) is sent to the backend&lt;/li&gt;
&lt;li&gt;Backend creates a &lt;code&gt;solutionId&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;That ID is returned to the frontend&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At this point, nothing has been executed yet.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Why WebSockets?
&lt;/h2&gt;

&lt;p&gt;After getting the &lt;code&gt;solutionId&lt;/code&gt;, the frontend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Opens a WebSocket connection&lt;/li&gt;
&lt;li&gt;Registers itself using that ID&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why not polling?&lt;/p&gt;

&lt;p&gt;Because polling would look like:&lt;br&gt;
“Is it done?” → repeat forever&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Backend will &lt;em&gt;push&lt;/em&gt; the result when ready&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This removes unnecessary load and makes the system real-time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: The Queue
&lt;/h2&gt;

&lt;p&gt;Here’s where the architecture becomes interesting.&lt;/p&gt;

&lt;p&gt;Instead of executing code directly in the backend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The backend pushes the job into a queue (BullMQ)&lt;/li&gt;
&lt;li&gt;The job contains: code + metadata&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because executing user code is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slow&lt;/li&gt;
&lt;li&gt;Unpredictable&lt;/li&gt;
&lt;li&gt;Potentially dangerous&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we did this inside the main server:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It would block everything&lt;/li&gt;
&lt;li&gt;One bad job could kill performance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So we separate execution.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Worker Picks It Up
&lt;/h2&gt;

&lt;p&gt;The worker continuously listens to the queue.&lt;/p&gt;

&lt;p&gt;As soon as a job arrives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Worker takes the job&lt;/li&gt;
&lt;li&gt;Starts processing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is completely asynchronous.&lt;/p&gt;

&lt;p&gt;The backend doesn’t wait.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Execution Pipeline
&lt;/h2&gt;

&lt;p&gt;The worker does this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Takes JSX string&lt;/li&gt;
&lt;li&gt;Transpiles it again using Babel&lt;/li&gt;
&lt;li&gt;Launches Puppeteer&lt;/li&gt;
&lt;li&gt;Injects the code into a real browser environment&lt;/li&gt;
&lt;li&gt;Simulates user actions&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We don’t just check output.&lt;/p&gt;

&lt;p&gt;We simulate behaviour.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Find a button&lt;/li&gt;
&lt;li&gt;Click it&lt;/li&gt;
&lt;li&gt;Check if UI updates correctly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why frontend transpilation is not enough.&lt;/p&gt;

&lt;p&gt;We need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A DOM&lt;/li&gt;
&lt;li&gt;An event loop&lt;/li&gt;
&lt;li&gt;Real browser behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Node.js alone cannot do this.&lt;/p&gt;

&lt;p&gt;Puppeteer solves that.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: Isolation
&lt;/h2&gt;

&lt;p&gt;Running user code is risky.&lt;/p&gt;

&lt;p&gt;So we isolate at multiple levels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each execution runs in a new Puppeteer page&lt;/li&gt;
&lt;li&gt;No shared state between runs&lt;/li&gt;
&lt;li&gt;Worker is separate from the backend process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even if something goes wrong, it stays contained.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8: Returning the Result
&lt;/h2&gt;

&lt;p&gt;Once execution is done:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Worker marks job as completed (BullMQ)&lt;/li&gt;
&lt;li&gt;Backend listens to queue events&lt;/li&gt;
&lt;li&gt;Backend finds the correct socket using &lt;code&gt;solutionId&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Emits result via WebSocket&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Frontend receives it instantly and updates UI.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 9: Redis
&lt;/h2&gt;

&lt;p&gt;Redis handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Queue storage (BullMQ)&lt;/li&gt;
&lt;li&gt;Job events (completed/failed)&lt;/li&gt;
&lt;li&gt;Caching layer (for faster reads)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Earlier, I was using raw Pub/Sub.&lt;/p&gt;

&lt;p&gt;But it required:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manual channel management&lt;/li&gt;
&lt;li&gt;Manual message parsing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Switching to BullMQ simplified everything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built-in lifecycle events&lt;/li&gt;
&lt;li&gt;Retry handling&lt;/li&gt;
&lt;li&gt;Cleaner architecture&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 10: Storage with S3
&lt;/h2&gt;

&lt;p&gt;Another problem:&lt;/p&gt;

&lt;p&gt;Where do we store user code?&lt;/p&gt;

&lt;p&gt;If we store it directly in MongoDB:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It becomes heavy&lt;/li&gt;
&lt;li&gt;Not scalable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code is stored in AWS S3&lt;/li&gt;
&lt;li&gt;Only the file key is saved in DB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When needed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backend generates a signed URL&lt;/li&gt;
&lt;li&gt;Frontend uses that to fetch code&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Security (no public access)&lt;/li&gt;
&lt;li&gt;Scalability&lt;/li&gt;
&lt;li&gt;Clean separation&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 11: The Full Flow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;User writes code&lt;/li&gt;
&lt;li&gt;Code runs in iframe (preview)&lt;/li&gt;
&lt;li&gt;User submits&lt;/li&gt;
&lt;li&gt;Backend returns solutionId&lt;/li&gt;
&lt;li&gt;Frontend opens WebSocket&lt;/li&gt;
&lt;li&gt;Backend pushes job to queue&lt;/li&gt;
&lt;li&gt;Worker picks a job&lt;/li&gt;
&lt;li&gt;Code executed via Puppeteer&lt;/li&gt;
&lt;li&gt;Result emitted via queue event&lt;/li&gt;
&lt;li&gt;Backend sends result via WebSocket&lt;/li&gt;
&lt;li&gt;Frontend updates UI&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Key Design Decisions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Double Transpilation
&lt;/h3&gt;

&lt;p&gt;Frontend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For preview&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Backend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For validation with real DOM&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Queue-Based Execution
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Prevents blocking&lt;/li&gt;
&lt;li&gt;Enables scaling workers independently&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Puppeteer Instead of Node Execution
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Needed for DOM + interaction&lt;/li&gt;
&lt;li&gt;Ensures accurate validation&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. WebSockets Instead of Polling
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Real-time updates&lt;/li&gt;
&lt;li&gt;Lower load&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  5. S3 Instead of DB Storage
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Better scalability&lt;/li&gt;
&lt;li&gt;Cleaner architecture&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>architecture</category>
      <category>javascript</category>
      <category>react</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Designing a Real-Time React Playground with Workers, Queues, and WebSockets</title>
      <dc:creator>RjS</dc:creator>
      <pubDate>Sat, 04 Apr 2026 13:52:22 +0000</pubDate>
      <link>https://dev.to/rjs/designing-a-real-time-react-playground-with-workers-queues-and-websockets-4h8f</link>
      <guid>https://dev.to/rjs/designing-a-real-time-react-playground-with-workers-queues-and-websockets-4h8f</guid>
      <description>&lt;p&gt;I built a full-stack SaaS project, an interactive playground to learn and practice React. Think of it as &lt;strong&gt;LeetCode, but for React&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Of course, the usual full-stack features are there (authentication, etc.), but that’s not the interesting part.&lt;/p&gt;

&lt;p&gt;What &lt;em&gt;is&lt;/em&gt; interesting is the system design and the problems I ran into while building and deploying it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Makes This Project Different
&lt;/h2&gt;

&lt;p&gt;This project involves several non-trivial concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pub/Sub architecture&lt;/li&gt;
&lt;li&gt;Queues and workers&lt;/li&gt;
&lt;li&gt;WebSockets for real-time updates&lt;/li&gt;
&lt;li&gt;Babel (standalone) for transpilation&lt;/li&gt;
&lt;li&gt;Puppeteer for code execution&lt;/li&gt;
&lt;li&gt;Docker for environment consistency&lt;/li&gt;
&lt;li&gt;AWS for deployment&lt;/li&gt;
&lt;li&gt;Isolated sandbox for safe execution&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Core Flow
&lt;/h2&gt;

&lt;p&gt;Here’s how the system works:&lt;/p&gt;

&lt;p&gt;1: User submits code&lt;br&gt;
2: Backend returns a solutionId&lt;br&gt;
3: Frontend registers a WebSocket using the solutionId&lt;br&gt;
4: Worker starts execution&lt;br&gt;
5: Worker finishes execution&lt;br&gt;
6: Redis publishes the result&lt;br&gt;
7: Backend receives the result&lt;br&gt;
8: Backend emits the result via WebSocket&lt;br&gt;
9: Frontend receives the result and updates the UI&lt;/p&gt;


&lt;h2&gt;
  
  
  Why Transpile Twice?
&lt;/h2&gt;

&lt;p&gt;You might wonder:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If we already transpile on the frontend, why do it again on the backend?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After transpiling on the frontend, if we pass that transpiled code to the backend, we lose the ability to properly simulate user interactions on it.&lt;/p&gt;

&lt;p&gt;For validation, we need to simulate events (like clicks). To do that reliably:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We take the original user code&lt;/li&gt;
&lt;li&gt;Transpile it again on the backend&lt;/li&gt;
&lt;li&gt;Execute it inside Puppeteer&lt;/li&gt;
&lt;li&gt;Trigger events programmatically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures we can accurately simulate interactions and validate behaviour.&lt;/p&gt;


&lt;h2&gt;
  
  
  Sandbox Execution
&lt;/h2&gt;

&lt;p&gt;Running arbitrary user code is dangerous.&lt;/p&gt;

&lt;p&gt;To handle this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code is executed inside a &lt;strong&gt;controlled Puppeteer environment&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Each execution happens in a &lt;strong&gt;new isolated page&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;This prevents malicious scripts from affecting the system&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Deployment Challenges
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Problem 1: Puppeteer Dependencies
&lt;/h3&gt;

&lt;p&gt;Puppeteer requires Chrome and several system-level dependencies.&lt;/p&gt;

&lt;p&gt;Most backend hosting platforms don’t provide these out of the box.&lt;/p&gt;
&lt;h3&gt;
  
  
  Solution: Docker
&lt;/h3&gt;

&lt;p&gt;Docker allowed me to define a consistent environment with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chrome dependencies&lt;/li&gt;
&lt;li&gt;Node environment&lt;/li&gt;
&lt;li&gt;All required libraries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now the app runs the same everywhere.&lt;/p&gt;


&lt;h3&gt;
  
  
  Problem 2: Render Deployment Failure
&lt;/h3&gt;

&lt;p&gt;Initially, I deployed the backend on &lt;strong&gt;Render&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But I kept getting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package dotenv not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Reinstalling dependencies&lt;/li&gt;
&lt;li&gt;Modifying the Dockerfile&lt;/li&gt;
&lt;li&gt;Adding installation scripts&lt;/li&gt;
&lt;/ul&gt;

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




&lt;h3&gt;
  
  
  Switching to Railway
&lt;/h3&gt;

&lt;p&gt;I moved to &lt;strong&gt;Railway&lt;/strong&gt;, and guess what? It worked immediately.&lt;/p&gt;

&lt;p&gt;But there was a catch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only &lt;strong&gt;1 month free trial&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So this wasn’t a long-term solution.&lt;/p&gt;




&lt;h2&gt;
  
  
  Moving to AWS
&lt;/h2&gt;

&lt;p&gt;This is where things got real.&lt;/p&gt;

&lt;p&gt;I had theoretical knowledge of AWS, but this was my first practical experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I did:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Launched an EC2 instance&lt;/li&gt;
&lt;li&gt;Configured a static IP&lt;/li&gt;
&lt;li&gt;Set up security groups&lt;/li&gt;
&lt;li&gt;Learned SSH (the hard way)&lt;/li&gt;
&lt;li&gt;Cloned the backend repo&lt;/li&gt;
&lt;li&gt;Configured environment variables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This process involved a few trials and errors, but it worked.&lt;/p&gt;




&lt;h2&gt;
  
  
  Problem 3: No HTTPS
&lt;/h2&gt;

&lt;p&gt;AWS provides a public DNS, but it’s &lt;strong&gt;HTTP&lt;/strong&gt;, not HTTPS. This becomes a problem because if the frontend is served over HTTPS, the browser expects all backend requests to also use HTTPS; otherwise, it blocks them due to mixed content security restrictions.&lt;/p&gt;

&lt;p&gt;To get HTTPS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I needed a domain&lt;/li&gt;
&lt;li&gt;I didn’t want to spend money on a hobby project&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Solution: Render as a Proxy
&lt;/h3&gt;

&lt;p&gt;I reused Render, but differently.&lt;/p&gt;

&lt;p&gt;Instead of hosting the backend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I created a &lt;strong&gt;proxy service&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;It forwards requests from a secure Render URL → AWS backend&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users hit a secure HTTPS endpoint (Render)&lt;/li&gt;
&lt;li&gt;Requests are forwarded to my AWS server&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Problem 4: Keeping the Backend Updated
&lt;/h2&gt;

&lt;p&gt;After deployment, another issue appeared:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When I push new code, how does AWS get it?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Initially, the answer was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SSH into the instance&lt;/li&gt;
&lt;li&gt;Pull the latest code manually&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Clearly not scalable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Solution: CI/CD
&lt;/h2&gt;

&lt;p&gt;Time to implement CI/CD (this, too, I only knew in theory before this).&lt;/p&gt;

&lt;h3&gt;
  
  
  What I did:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Wrote a simple &lt;strong&gt;YAML pipeline&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Automated pulling the latest code on deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: I still have a lot to learn about Docker and CI/CD.&lt;br&gt;
This setup is basic but functional.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Final Result
&lt;/h2&gt;

&lt;p&gt;Everything is now working end-to-end:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Secure access via proxy&lt;/li&gt;
&lt;li&gt;Scalable job processing via queues&lt;/li&gt;
&lt;li&gt;Real-time updates via WebSockets&lt;/li&gt;
&lt;li&gt;Safe execution using Puppeteer + isolation&lt;/li&gt;
&lt;li&gt;Automated deployment via CI/CD&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try It Out
&lt;/h2&gt;

&lt;p&gt;👉 &lt;a href="https://reactpg.vercel.app" rel="noopener noreferrer"&gt;https://reactpg.vercel.app&lt;/a&gt;&lt;/p&gt;




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

&lt;p&gt;I’ll be writing another blog soon covering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;System architecture in depth&lt;/li&gt;
&lt;li&gt;Detailed flow diagrams&lt;/li&gt;
&lt;li&gt;Design decisions and trade-offs&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;This project forced me to move from theory → practice in multiple areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;System design&lt;/li&gt;
&lt;li&gt;DevOps&lt;/li&gt;
&lt;li&gt;Deployment&lt;/li&gt;
&lt;li&gt;Debugging real-world issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And honestly, that’s where most of the learning happened.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>architecture</category>
      <category>aws</category>
    </item>
  </channel>
</rss>
