<?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: Boby Santoso</title>
    <description>The latest articles on DEV Community by Boby Santoso (@bobysf12).</description>
    <link>https://dev.to/bobysf12</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%2F3022453%2Fb0c8a759-5b6d-46ea-9186-3a65c2c1047f.jpeg</url>
      <title>DEV Community: Boby Santoso</title>
      <link>https://dev.to/bobysf12</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bobysf12"/>
    <language>en</language>
    <item>
      <title>How I Built a Multi-Tenant Static Site Engine with Next.js, Nginx, and Coolify</title>
      <dc:creator>Boby Santoso</dc:creator>
      <pubDate>Sat, 13 Dec 2025 12:00:00 +0000</pubDate>
      <link>https://dev.to/bobysf12/how-i-built-a-multi-tenant-static-site-engine-with-nextjs-nginx-and-coolify-10mm</link>
      <guid>https://dev.to/bobysf12/how-i-built-a-multi-tenant-static-site-engine-with-nextjs-nginx-and-coolify-10mm</guid>
      <description>&lt;p&gt;Over the past few months my Sister started this &lt;a href="https://instagram.com/titipdongbeni" rel="noopener noreferrer"&gt;Embroidery&lt;/a&gt; business, and as a good brother I was eager to help her. I really wanted to help her get more visibility online. Naturally, my first thought was: let's make her a simple landing page.&lt;/p&gt;

&lt;p&gt;But, here's the thing:&lt;/p&gt;

&lt;p&gt;As a Software Engineer who uses coding agents a lot and understands how powerful &lt;strong&gt;automation&lt;/strong&gt; can be, I had zero interest in building landing pages using drag and drop tools. Not with Wix, Wordpress, not with Lovable, not even with V0.&lt;br&gt;
I already pay for Claude Code, and I want the most optimized, scalable, and developer friendly solution I can build.&lt;/p&gt;

&lt;p&gt;That’s what led me to this idea:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;a &lt;strong&gt;Multi-Tenant Static Site Engine&lt;/strong&gt; with &lt;code&gt;Next.js&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But Boby, Next.js? Static? What are you talking about?&lt;/p&gt;

&lt;p&gt;Let me explain.&lt;/p&gt;
&lt;h2&gt;
  
  
  High-Level Overview
&lt;/h2&gt;

&lt;p&gt;Before diving into the technical details, let me show you what the system looks like from the top.&lt;/p&gt;

&lt;p&gt;Most landing pages today are built manually or with drag-and-drop tools.&lt;br&gt;
I wanted something different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automated&lt;/li&gt;
&lt;li&gt;Config Driven&lt;/li&gt;
&lt;li&gt;Super Fast&lt;/li&gt;
&lt;li&gt;SEO Optimized&lt;/li&gt;
&lt;li&gt;Super Cheap to Host&lt;/li&gt;
&lt;li&gt;And able to host &lt;strong&gt;hundreds&lt;/strong&gt; of sites without breaking a sweat&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;This is the “bird's-eye view” of the entire system:&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%2Ffx9oy6ql35whebf9xxwg.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%2Ffx9oy6ql35whebf9xxwg.png" alt="Multi Tenant Landing Page Architecture" width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A single engine built with &lt;code&gt;Next.js&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Config Driven&lt;/strong&gt; system for each tenant&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Component Factory&lt;/strong&gt; that assembles pages&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Static Export&lt;/strong&gt; that generates pure HTML&lt;/li&gt;
&lt;li&gt;Automated output (sitemap, robots, analytics, Nginx configs)&lt;/li&gt;
&lt;li&gt;Deployment handled by Coolify, assigning subdomains or custom domains&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;One engine -&amp;gt; unlimited landing pages.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Build Pipeline
&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%2F2tqtln6y2tk8bzghpztb.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%2F2tqtln6y2tk8bzghpztb.png" alt="Multi Tenant Landing Page Build Pipeline" width="695" height="3171"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Business Config
&lt;/h3&gt;

&lt;p&gt;Each tenant has a JSON config with branding, colors, services, copy, etc.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Component Factory&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The engine picks from a library of reusable section templates. For example, you can build commonly used sections 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;hero
|- default
|- split

features
|- grid
|- list

footer
|- default
|- multi-columns

gallery
|- default
|- scrolling
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Static Export&lt;/strong&gt; (&lt;code&gt;Next.js&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Next.js&lt;/code&gt; compiles everything into static HTML.&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;nextConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;export&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;unoptimized&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;trailingSlash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;
  
  
  Auto-Generated Assets
&lt;/h3&gt;

&lt;p&gt;The system produces sitemap.xml, robots.txt, analytics, OpenGraph metadata, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nginx Configuration
&lt;/h3&gt;

&lt;p&gt;The generator builds the routing + subdomain config for each tenant.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy via Coolify
&lt;/h3&gt;

&lt;p&gt;Coolify picks up the output -&amp;gt; reloads Nginx -&amp;gt; site goes live.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means in Practice
&lt;/h2&gt;

&lt;p&gt;Instead of building landing pages one by one, I can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;onboard a new business&lt;/li&gt;
&lt;li&gt;fill out a config&lt;/li&gt;
&lt;li&gt;pick the sections&lt;/li&gt;
&lt;li&gt;run the generator&lt;/li&gt;
&lt;li&gt;instantly deploy a fully functional, SEO-ready landing page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s fast, cheap, scalable, and extremely flexible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Static &amp;gt; SSR for This Kind of Project
&lt;/h2&gt;

&lt;p&gt;For a &lt;strong&gt;Multi-Tenant&lt;/strong&gt; system where I want to host tens or hundreds of small business sites, &lt;code&gt;SSR&lt;/code&gt; can be overkill:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every request needs a Node.js process to run the app&lt;/li&gt;
&lt;li&gt;You usually have middleware, auth, data fetching, layout logic on each hit&lt;/li&gt;
&lt;li&gt;You need to think about concurrency, memory, cold starts, scaling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With static export:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each page is already rendered to HTML at build time&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Nginx&lt;/code&gt; (or any simple web server) just reads files from disk and sends them&lt;/li&gt;
&lt;li&gt;There's no React/&lt;code&gt;Next.js&lt;/code&gt; code running on the server per request&lt;/li&gt;
&lt;li&gt;No per-request rendering = less CPU, less RAM, less that can break&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For my use case (small businesses, mostly “read-only” marketing pages), static wins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First byte is faster (no render step)&lt;/li&gt;
&lt;li&gt;Less complexity to scale&lt;/li&gt;
&lt;li&gt;Way easier to reason about performance&lt;/li&gt;
&lt;li&gt;Much cheaper infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If I ever need “dynamic” behavior, I can still add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Small API routes, I have one already&lt;/li&gt;
&lt;li&gt;Client-side interactivity&lt;/li&gt;
&lt;li&gt;Webhooks or background workers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the core of the site remains static.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Can Host Hundreds of Pages on a “Cheap” Server
&lt;/h2&gt;

&lt;p&gt;So how much is "cheap"?&lt;/p&gt;

&lt;p&gt;A basic cloud server (like a small DigitalOcean/Vultr/Linode instance) typically costs around:&lt;/p&gt;

&lt;p&gt;USD &lt;strong&gt;&lt;em&gt;$4–7 / month&lt;/em&gt;&lt;/strong&gt; for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 vCPU&lt;/li&gt;
&lt;li&gt;0.5-1 GB RAM&lt;/li&gt;
&lt;li&gt;~10-25 GB SSD&lt;/li&gt;
&lt;li&gt;500 GB - 1 TB outbound bandwidth per month&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That kind of machine is not great for heavy SSR apps with databases and background jobs.&lt;br&gt;
But for pure static hosting with Nginx, it's actually kind of overpowered.&lt;/p&gt;

&lt;p&gt;Rough math:&lt;/p&gt;

&lt;p&gt;Let’s say one page (HTML + CSS + a bit of JS) is ~300 KB&lt;br&gt;
With 500 GB outbound bandwidth, you can theoretically serve:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;500,000,000 KB / 300 KB ≈ &lt;strong&gt;&lt;em&gt;1.6 million page views per month&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With 1 TB bandwidth, that’s ≈ 3.3 million page views per month&lt;/p&gt;

&lt;p&gt;Real traffic will be lower, and you'll have images etc. But even if you divide those numbers by 10, you still get hundreds of thousands of page views comfortably.&lt;/p&gt;

&lt;p&gt;For a multi-tenant scenario:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;100 tenants × 1,000 page views/month each = 100,000 views/month&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s nothing for a static Nginx box with 500 GB+ bandwidth&lt;/p&gt;

&lt;p&gt;And because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There's no SSR&lt;/li&gt;
&lt;li&gt;No per-tenant backend&lt;/li&gt;
&lt;li&gt;No database queries per page view&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your main bottleneck becomes bandwidth, not CPU or RAM. And cheap servers already give you a lot of bandwidth for this use case.&lt;/p&gt;

&lt;p&gt;So when I say "cheap", I literally mean:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One small cloud instance in the $4–7/month range&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If I later outgrow that, I can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a CDN in front&lt;/li&gt;
&lt;li&gt;Scale horizontally (more small boxes)&lt;/li&gt;
&lt;li&gt;Or move to a slightly bigger instance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the core benefit stays the same:&lt;br&gt;
&lt;strong&gt;&lt;em&gt;Static + Multi-Tenant&lt;/em&gt;&lt;/strong&gt; = a lot of sites on very little infrastructure.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Frontend to Fullstack - Mastering the Architectural Mindset</title>
      <dc:creator>Boby Santoso</dc:creator>
      <pubDate>Sat, 31 May 2025 14:39:27 +0000</pubDate>
      <link>https://dev.to/bobysf12/frontend-to-fullstack-mastering-the-architectural-mindset-n0i</link>
      <guid>https://dev.to/bobysf12/frontend-to-fullstack-mastering-the-architectural-mindset-n0i</guid>
      <description>&lt;p&gt;I spent 6 years working as a frontend developer.  &lt;/p&gt;

&lt;p&gt;Today, I find myself responsible for building and maintaining fullstack systems. Frontend, backend, deployment, scaling, all of it. Not because I mastered everything, but because I learned how to &lt;strong&gt;think architecturally&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Becoming fullstack isn't about learning backend code. It's about changing how you think.&lt;/p&gt;

&lt;p&gt;Most frontend devs already know how to solve problems. Architecture is just &lt;strong&gt;solving bigger problems&lt;/strong&gt; but more &lt;strong&gt;intentionally&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;If you've ever designed a clean component tree, you’re already halfway there.&lt;/p&gt;

&lt;p&gt;Architecture starts with the &lt;strong&gt;problem&lt;/strong&gt;, travels through &lt;strong&gt;trade-offs&lt;/strong&gt;, and only then lands in code. &lt;/p&gt;

&lt;p&gt;In this post, I'll share what I've learned about developing that mindset, especially as someone coming from a frontend background stepping into fullstack development.&lt;/p&gt;

&lt;h3&gt;
  
  
  Start with the Problem, not the Tech
&lt;/h3&gt;

&lt;p&gt;Before writing any code, ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who the user is&lt;/li&gt;
&lt;li&gt;What pain they have&lt;/li&gt;
&lt;li&gt;What "success" looks like&lt;/li&gt;
&lt;li&gt;What constraints exist (budget, time, team)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If there's a PRD, read it, analyze it, ask questions, break it down.&lt;br&gt;
This gives you clarity and keeps your decisions aligned with real needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Identify Requirements
&lt;/h3&gt;

&lt;p&gt;Write out what your system should do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Functional requirements&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"User uploads a file and the user gains XP"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Non-functional requirements&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Upload speed must be under 2 seconds for 80% of users."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This will help you make technical decisions, like storage, caching, load handling, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Visualize the Flow
&lt;/h3&gt;

&lt;p&gt;Draw the data or interaction flow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sequence diagram&lt;/strong&gt;: user -&amp;gt; frontend -&amp;gt; API -&amp;gt; service -&amp;gt; database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Component diagram&lt;/strong&gt;: boxes for auth, cache, external services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Entity diagram&lt;/strong&gt;: tables or document structures with relationships&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This step is very important and most likely will always be used.&lt;br&gt;
There's no need to over engineer this though. The idea is to visualize the flow and interaction between services.&lt;br&gt;
As time goes by, you'll most likely going to spend more time here than writing the code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Separation of Concerns
&lt;/h3&gt;

&lt;p&gt;Architecture thrives on &lt;strong&gt;separation of concerns&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;As a frontend dev, you already apply this: components handle UI, hooks handle logic, and API calls are abstracted away.&lt;/p&gt;

&lt;p&gt;Backend works the same way. Routes handle HTTP, services contain business logic, and database functions take care of persistence.&lt;/p&gt;

&lt;p&gt;This kind of layering keeps your code clean, testable, and easier to grow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Balancing Trade-Offs
&lt;/h3&gt;

&lt;p&gt;Every architectural choice has trade-offs. Whether you realize it or not, you're constantly adjusting performance, complexity, cost, and team velocity.&lt;/p&gt;

&lt;p&gt;Here are some of the common decisions you'll face:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Monolith or microservices?&lt;/strong&gt;&lt;br&gt;
A single codebase is easier to ship, but services give you flexibility and scalability. But there will be cost of complexity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SQL or NoSQL?&lt;/strong&gt;&lt;br&gt;
Relational databases like PostgreSQL are strict and structured. NoSQL like MongoDB is more flexible, but harder to validate and model at scale.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Self-hosted or platform-as-a-service?&lt;/strong&gt;&lt;br&gt;
Platforms like Vercel help you move fast. Self-hosting gives you more control, but also more DevOps to manage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simple logic or scalable abstractions?&lt;/strong&gt;&lt;br&gt;
Hardcoding something might be faster now. But abstracting it into a reusable service or module might save you pain later.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;. . .&lt;/p&gt;

&lt;p&gt;The key is: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't chase "the best." Choose what's right for your &lt;strong&gt;current problem&lt;/strong&gt; and &lt;strong&gt;constraints&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Another example, say you're building a small internal dashboard. You could set up a full microservice architecture with Docker, Kubernetes, and separate auth services. Sounds impressive! But why? Is it necessary?&lt;/p&gt;

&lt;p&gt;Probably not.&lt;/p&gt;

&lt;p&gt;A simple monolith with a single backend repo and an SQLite as the database &lt;strong&gt;might&lt;/strong&gt; serve you better. It's faster to build, easier to deploy, and simpler to debug.&lt;/p&gt;

&lt;p&gt;Later, if the app grows or becomes critical, you can refactor and scale. But making the right trade off now saves you time and complexity without sacrificing quality.&lt;/p&gt;

&lt;p&gt;Architectural thinking is not about chasing perfect. It's about being deliberate.&lt;/p&gt;

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

&lt;p&gt;If you're &lt;strong&gt;transitioning from frontend to fullstack&lt;/strong&gt;, or if you want to level up as Frontend Engineer, remember this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The most important skill isn't backend, frontend, or any code syntax. It's &lt;strong&gt;architectural thinking.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This mindset is what makes the difference between a regular and &lt;strong&gt;a great engineer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI can help you write code&lt;/strong&gt;, explain concepts, and fill gaps in your knowledge. But without the right mindset, you'll still &lt;strong&gt;struggle&lt;/strong&gt; to make decisions, design scalable features, or solve real problems.&lt;/p&gt;

&lt;p&gt;You don't need to become an expert overnight. Just start asking deeper questions, structuring your solutions with care, and solving problems with intent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The code will follow&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>architecture</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Crafting a Real-Time Virtual Desktop for Gamified Onboarding</title>
      <dc:creator>Boby Santoso</dc:creator>
      <pubDate>Tue, 20 May 2025 14:53:01 +0000</pubDate>
      <link>https://dev.to/bobysf12/crafting-a-real-time-virtual-desktop-for-gamified-onboarding-mjk</link>
      <guid>https://dev.to/bobysf12/crafting-a-real-time-virtual-desktop-for-gamified-onboarding-mjk</guid>
      <description>&lt;p&gt;4 years ago we started RiddleStory, now &lt;a href="https://bybobyy.com/z/Lqcf7xy" rel="noopener noreferrer"&gt;Enboq&lt;/a&gt;. The idea was simple: &lt;em&gt;Create an Online Escape Room that remote teams could play together&lt;/em&gt;. What was a simple idea has evolved into a &lt;strong&gt;Gamified Onboarding Platform&lt;/strong&gt;. Helping companies turn dull training into immersive, story-driven experiences.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it started
&lt;/h2&gt;

&lt;p&gt;We started off with an idea for an Online Escape Room. This was during Covid and everyone were forced to stay inside and do stuff remotely. &lt;br&gt;
The vision was to simulate a digital escape room where players could explore, uncover clues, and solve challenges. All inside a custom built desktop environment. &lt;/p&gt;

&lt;h2&gt;
  
  
  What makes a Great Escape Room
&lt;/h2&gt;

&lt;p&gt;An escape room needs two things to work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A believable Environment&lt;/li&gt;
&lt;li&gt;A compelling Story&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Building the Environment
&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fspnsblax3218r2umgfdg.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%2Fspnsblax3218r2umgfdg.png" alt="Enboq Game Environment" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Environment&lt;/em&gt; of the escape room that we built was to give experience of a mix of &lt;strong&gt;Mac&lt;/strong&gt; and &lt;strong&gt;Windows&lt;/strong&gt; desktop. It has most of Mac essentials, like the Dock, Filebrowser, Email, Notes, and Chat.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dock&lt;/strong&gt; to hold all &lt;em&gt;applications&lt;/em&gt;, such as Filebrowser, Email, Notes, and Chat.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filebrowser&lt;/strong&gt; let users open files, draw on top of images, read pdfs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email&lt;/strong&gt; delivers in-game messages and updates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notes&lt;/strong&gt; to write and jot down clues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chat&lt;/strong&gt; to chat and connect with a live &lt;em&gt;moderator&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our goal was simple: make it feel as real as possible, not just another web page.&lt;/p&gt;

&lt;p&gt;. . .&lt;/p&gt;

&lt;p&gt;PS: We also built the same Environment in &lt;a href="https://bybobyy.com/z/k0sslO7" rel="noopener noreferrer"&gt;Enboq's blog&lt;/a&gt;. Check it out!&lt;/p&gt;

&lt;h3&gt;
  
  
  Crafting the Story
&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6p07e8j0kwcxnvzi763i.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%2F6p07e8j0kwcxnvzi763i.png" alt="Enboq Game Environment" width="800" height="508"&gt;&lt;/a&gt;&lt;br&gt;
Story is the core of an Escape Room, its the engine. We represent it as a &lt;em&gt;flow&lt;/em&gt; that drives each player's journey:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The game kicks off with a chat message…&lt;/li&gt;
&lt;li&gt;Then an email arrives with new context…&lt;/li&gt;
&lt;li&gt;Players hunt for clues, in-app or IRL…&lt;/li&gt;
&lt;li&gt;They submit answers via chat…&lt;/li&gt;
&lt;li&gt;Our bot (or moderator) judges correctness…&lt;/li&gt;
&lt;li&gt;Onward to the next puzzle (or branching ending!)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Over time, we evolved from a straight path to branching narratives with multiple endings. Kinda like a mini RPG where your answer may affect the ending&lt;/p&gt;

&lt;h2&gt;
  
  
  Under the Hood
&lt;/h2&gt;

&lt;p&gt;Building a fully interactive, real-time virtual desktop inside a browser isn't exactly standard web dev work. Here's how we tackled it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Frontend (React)
&lt;/h3&gt;

&lt;p&gt;The desktop environment was built in React from the ground up. We wrote our own window manager to handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Opening and closing &lt;em&gt;apps&lt;/em&gt; like Filebrowser, Email, Notes, Chat&lt;/li&gt;
&lt;li&gt;Custom window manager for dragging, resizing, stacking&lt;/li&gt;
&lt;li&gt;Shared state via Context + hooks, synced in real time&lt;/li&gt;
&lt;li&gt;Dynamic theming for white-label clients&lt;/li&gt;
&lt;li&gt;PDF rendering (react-pdf) and canvas overlays for drawing&lt;/li&gt;
&lt;li&gt;Mini-games: crosswords, word searches, quizzes… you name it&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Backend (Node.js, Express, Redis, MongoDB)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;REST APIs for sessions, progress tracking, story management&lt;/li&gt;
&lt;li&gt;WebSocket layer (&lt;strong&gt;socket.io&lt;/strong&gt;) for instant updates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BullMQ&lt;/strong&gt; for in-game event scheduling and queue&lt;/li&gt;
&lt;li&gt;A story engine that triggers events (emails, chats) based on your choices&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Real-Time Magic
&lt;/h3&gt;

&lt;p&gt;The WebSocket layer was the core of the game room:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Incoming messages triggered new story events (emails, chat replies)&lt;/li&gt;
&lt;li&gt;Moderator responses were pushed live into the chat app, powered by LLM&lt;/li&gt;
&lt;li&gt;Live updates on the progress of the escape room&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Moderation System
&lt;/h3&gt;

&lt;p&gt;Moderators played a big role in early versions of the game. However, due to limited personel, we built our own &lt;strong&gt;Chatbot&lt;/strong&gt; powered by nlp.js. We trained the Chatbot to  do basic responses like recognizing player's intent in the chat, whether to submit an answer, ask for a hint, or just regular chat with other team members.&lt;br&gt;
Eventually when ChatGPT came out, we also powered the Chatbot using &lt;strong&gt;ChatGPT&lt;/strong&gt; to be able to guide the user through the game.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Evolution of Our Story Engine
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Linear flow: One path, one ending.&lt;/li&gt;
&lt;li&gt;Branching choices: Your answers shape the narrative.&lt;/li&gt;
&lt;li&gt;Multiple endings: Like an RPG, every decision matters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This provides lots of possibilities for the Story creator to provide &lt;strong&gt;fun and engaging&lt;/strong&gt; story. Especially, in our case, for Onboarding.&lt;/p&gt;




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

&lt;p&gt;We're always pushing &lt;a href="https://bybobyy.com/z/Lqcf7xy" rel="noopener noreferrer"&gt;Enboq&lt;/a&gt; forward. Our big focus now is utilizing LLMs into every step of the story building process. Soon, you'll be able to generate entire Story Flows on the fly, customized to your company's unique profile (check out how we're summarizing org data &lt;a href="https://bybobyy.com/blog/building-company-profile-summarizer" rel="noopener noreferrer"&gt;here&lt;/a&gt;). &lt;br&gt;
That means every puzzle, email, and branching path adapts not just to your team's choices, but to your brand, your culture, and your goals. Imagine escape rooms that feel handcrafted for your business..&lt;/p&gt;

</description>
    </item>
    <item>
      <title>I Just Wanted to Code. Now I'm a CTO</title>
      <dc:creator>Boby Santoso</dc:creator>
      <pubDate>Fri, 09 May 2025 14:00:00 +0000</pubDate>
      <link>https://dev.to/bobysf12/i-just-wanted-to-code-now-im-a-cto-25jj</link>
      <guid>https://dev.to/bobysf12/i-just-wanted-to-code-now-im-a-cto-25jj</guid>
      <description>&lt;p&gt;I &lt;strong&gt;never planned to be a CTO&lt;/strong&gt;. I just wanted to build cool things and solve real world problems.&lt;/p&gt;

&lt;p&gt;Today, I lead a small team, manage our infrastructure, make technical decisions, and still write a lot of code every day.&lt;/p&gt;

&lt;p&gt;My younger self would have never imagined this path.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Started
&lt;/h2&gt;

&lt;p&gt;My first programming job came during my second year of college. I joined as a &lt;strong&gt;junior programmer&lt;/strong&gt; building a school management system using &lt;strong&gt;ASP.NET Web Forms&lt;/strong&gt;. The codebase followed a clean structure with &lt;strong&gt;multi-layer architecture&lt;/strong&gt;, &lt;strong&gt;MVC&lt;/strong&gt;, and &lt;strong&gt;solid naming conventions&lt;/strong&gt;. It was my first exposure to how &lt;strong&gt;real software was built&lt;/strong&gt;, and I loved it.&lt;/p&gt;

&lt;p&gt;But I was curious about more. Web Forms felt outdated, and I kept wondering how modern web apps were built. That curiosity led me to discover &lt;strong&gt;AngularJS&lt;/strong&gt; and &lt;strong&gt;React&lt;/strong&gt;. It completely changed how I saw frontend development. I could finally manage UI behavior with &lt;strong&gt;JavaScript&lt;/strong&gt;, and it felt powerful.&lt;/p&gt;

&lt;p&gt;After I graduated, I became a &lt;strong&gt;full-time frontend developer&lt;/strong&gt; working with &lt;strong&gt;React and TypeScript&lt;/strong&gt;. Thanks to my &lt;strong&gt;C# background&lt;/strong&gt;, TypeScript felt natural. At that time, React was on fire in the job market, and I was excited to dive in. &lt;strong&gt;I dove head first&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Becoming a Leader
&lt;/h2&gt;

&lt;p&gt;In my first few years as a frontend developer, I &lt;strong&gt;stayed in my lane&lt;/strong&gt;. I worked on UI components, dashboards, animations, anything visual. &lt;/p&gt;

&lt;p&gt;Then I was given a chance to &lt;strong&gt;lead the web development team&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I still wrote code, but now I was &lt;strong&gt;reviewing others work&lt;/strong&gt;, helping &lt;strong&gt;set priorities&lt;/strong&gt;, and thought more about the &lt;strong&gt;big picture&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That shift taught me that &lt;strong&gt;leadership isn't a promotion&lt;/strong&gt;. It's a new kind of &lt;strong&gt;responsibility&lt;/strong&gt;. I started paying attention to communication, decision making, and how work flowed through the team. Soon I cared more about systems than single features. &lt;/p&gt;

&lt;p&gt;That mindset prepared me for everything that came next.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Side Projects to Full Ownership
&lt;/h2&gt;

&lt;p&gt;The real turning point came &lt;strong&gt;outside my 9‑to‑5 job&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A client wanted to build an online escape room, called &lt;a href="https://bybobyy.com/z/Lqcf7xy" rel="noopener noreferrer"&gt;&lt;strong&gt;RiddleStory&lt;/strong&gt;&lt;/a&gt;. I joined to help ship the first version. Then more feature requests coming, and I ended up &lt;strong&gt;shaping the architecture&lt;/strong&gt;, discussing &lt;strong&gt;infrastructure&lt;/strong&gt;, and even started &lt;strong&gt;interviews&lt;/strong&gt; for new hires. &lt;/p&gt;

&lt;p&gt;They eventually asked me to &lt;strong&gt;join full time&lt;/strong&gt; and lead the technical vision. That is when I &lt;strong&gt;officially became the CTO&lt;/strong&gt;. It felt less like a promotion and more like a &lt;strong&gt;natural extension&lt;/strong&gt; of all those late nights spent solving problems end to end.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Being a CTO Means to Me
&lt;/h2&gt;

&lt;p&gt;People often picture a CTO &lt;strong&gt;announcing funding rounds&lt;/strong&gt;, &lt;strong&gt;speaking at conferences&lt;/strong&gt;, or &lt;strong&gt;managing several engineering teams&lt;/strong&gt;. All those cool stuffs.&lt;/p&gt;

&lt;p&gt;At large startups, a CTO focuses on aligning tech with &lt;strong&gt;business goals&lt;/strong&gt;, managing &lt;strong&gt;directors&lt;/strong&gt;, and talking to &lt;strong&gt;investors&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At big corporations, a CTO deals with &lt;strong&gt;governance&lt;/strong&gt;, &lt;strong&gt;procurement&lt;/strong&gt;, &lt;strong&gt;security audits&lt;/strong&gt;, and &lt;strong&gt;compliance&lt;/strong&gt; roadmaps.&lt;/p&gt;

&lt;p&gt;For me, being a CTO is &lt;strong&gt;closer to the code&lt;/strong&gt;. I set up CI and CD pipelines, review complex pull requests, spin up new services, and answer "What should we build next?" all in the same day.&lt;/p&gt;

&lt;p&gt;It is &lt;strong&gt;wearing many hats&lt;/strong&gt; and knowing when to put one down. It is not about &lt;strong&gt;being the smartest person&lt;/strong&gt; in the room, it is about creating &lt;strong&gt;space for the right problems&lt;/strong&gt; to be solved quickly and clearly. It is a lot of &lt;strong&gt;context switching&lt;/strong&gt;, listening, and choosing what matters over what merely looks impressive.&lt;/p&gt;

&lt;p&gt;It is a fun, frustrating, mind-boggling, and &lt;strong&gt;never-ending journey of thought&lt;/strong&gt;. And I genuinely &lt;strong&gt;enjoy it&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;I did not become a CTO by &lt;strong&gt;climbing a ladder&lt;/strong&gt;. I became one by chasing curiosity, filling gaps, and &lt;strong&gt;saying yes to tasks that scared me&lt;/strong&gt; a little.&lt;/p&gt;

&lt;p&gt;It was not a straight path, and it was never planned. Looking back, patterns stand out:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Solve real problems.&lt;/li&gt;
&lt;li&gt;Communicate with clarity.&lt;/li&gt;
&lt;li&gt;Care about the outcome, not just the code.&lt;/li&gt;
&lt;li&gt;Chase that &lt;strong&gt;"How to get to the next level"&lt;/strong&gt; thing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you are a developer wondering what is next, do not wait for permission. &lt;strong&gt;Own problems outside your scope&lt;/strong&gt;, help your team move faster, and think like a builder rather than just a contributor.&lt;/p&gt;

&lt;p&gt;Who knows, leadership might sneak up on you, too.&lt;/p&gt;

</description>
      <category>leadership</category>
      <category>softwaredevelopment</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>LLM Agent Architecture for Scalable Company Summaries</title>
      <dc:creator>Boby Santoso</dc:creator>
      <pubDate>Thu, 24 Apr 2025 14:06:40 +0000</pubDate>
      <link>https://dev.to/bobysf12/llm-agent-architecture-for-scalable-company-summaries-31m2</link>
      <guid>https://dev.to/bobysf12/llm-agent-architecture-for-scalable-company-summaries-31m2</guid>
      <description>&lt;p&gt;At &lt;a href="https://bybobyy.com/z/Lqcf7xy" rel="noopener noreferrer"&gt;&lt;strong&gt;RiddleStory&lt;/strong&gt;&lt;/a&gt;, we're building an online onboarding platform that helps new hires get up to speed faster and feel more connected from day one. But to create meaningful &lt;strong&gt;onboarding experiences&lt;/strong&gt;, we need to deeply understand the companies we're working with. We need to know their values, culture, tone, and mission.&lt;/p&gt;

&lt;p&gt;That's why we built a &lt;strong&gt;Company Profile Summarizer&lt;/strong&gt; powered by LLMs. Here's how the idea evolved and what we learned along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Idea
&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%2Fd70ctdo57064vjdaiha3.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%2Fd70ctdo57064vjdaiha3.png" alt="The Idea" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Given a company's website URL, we wanted to scrape and extract the most important elements that define the company's identity.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Approach
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Naive Approach: Simple LLM summarization
&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwuljwfvsx0jx0v855bq9.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%2Fwuljwfvsx0jx0v855bq9.png" alt="Naive" width="800" height="648"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our first idea was &lt;strong&gt;straightforward&lt;/strong&gt;: scrape all the URLs we could find on a company's website (using recursive crawling), combine the content, and pass everything to GPT-4o in a single prompt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It kind of worked&lt;/strong&gt;. But not really. We ran into 2 issues:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Token limits&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Many sites have dozens of pages, and GPT quickly hits the limit.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Quality &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Even when it stayed within the limit, the summarization and other outputs were too shallow. With so much context, the LLM struggled to produce high quality results.&lt;/p&gt;

&lt;h3&gt;
  
  
  Page by Page Approach
&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylu499by0a5oh7qg3l6a.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%2Fylu499by0a5oh7qg3l6a.png" alt="Page by Page" width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our second approach produces &lt;strong&gt;better output&lt;/strong&gt;. Each scraped page was summarized, so we have multiple page summaries. We then combined those summaries to generate the final output, including mission, vision, values, and more.&lt;/p&gt;

&lt;p&gt;However, there are still some issues:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Token limits&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Again, even with smaller summaries page by page, generating the final summary may also reach token limit.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Quality&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;LLM tends to &lt;em&gt;hallucinates&lt;/em&gt; when given too much context. Thus the output may not be too accurate.&lt;/p&gt;

&lt;p&gt;This approach works, and we thought it's production ready. But we knew there had to be a smarter way.&lt;/p&gt;

&lt;h3&gt;
  
  
  RAG Powered Agent
&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flqa42rsso6mbalgm1qjm.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%2Flqa42rsso6mbalgm1qjm.png" alt="RAG" width="800" height="987"&gt;&lt;/a&gt;&lt;br&gt;
To solve the token limit and quality problem, we moved towards a Retrieval Augmented Generation (RAG) approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Scrape &amp;amp; Chunk the Website&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We crawled the company's website and broke down the content into smaller chunks, in this case per 1 page. Each chunk was stored with metadata like the source URL and its page context.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Embed the Content&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each chunk was turned into a vector using OpenAI embeddings and stored in a vector database (MongoDB in our case). This allows us to later search semantically, not just by keyword.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ask Only What's Needed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When we need to summarize, say, the company's mission or values, we run a vector search to retrieve only the most relevant chunks. These chunks are passed into a specialized GPT prompt that focuses on extracting just that one piece of information.&lt;/p&gt;

&lt;p&gt;This approach &lt;strong&gt;dramatically improved the output&lt;/strong&gt;. It was more &lt;strong&gt;focused&lt;/strong&gt;, &lt;strong&gt;accurate&lt;/strong&gt;, and &lt;strong&gt;easier to scale&lt;/strong&gt;. The LLM had less to &lt;em&gt;think about&lt;/em&gt;. And the output was more &lt;strong&gt;accurate&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;. . . . . &lt;/p&gt;

&lt;p&gt;But again, even though this approach is far more efficient and accurate than before, there are some &lt;strong&gt;issues&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Inaccurate scraped web pages&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The scraped web pages were not accurate. Given the website url, we recursively scrape the web pages. Yes we can get all dozens if not hundreds of web pages from the url, but it isn't worth the cost to scrape hundreds of web pages blindly while we may not need all those web pages. So we decided to cap the scraped urls up to 50 pages.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Static search query for searching Vector DB&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The vector search used fixed queries. For example, we'd always use the same phrase like &lt;em&gt;"Company Values"&lt;/em&gt;, regardless of the context.&lt;/p&gt;

&lt;h3&gt;
  
  
  Smart LLM Agent Approach
&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5g8ypkntnnucirzjxcob.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%2F5g8ypkntnnucirzjxcob.png" alt="Autonomous Agent" width="800" height="860"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is where things get really &lt;strong&gt;interesting&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of manually controlling the flow or relying on static queries, we decided to &lt;strong&gt;build an autonomous agent&lt;/strong&gt; using the ReAct pattern (combining reasoning and action). The idea: give the agent a clear goal, equip it with the right tools, and let it figure out the rest.&lt;/p&gt;

&lt;p&gt;This is a &lt;strong&gt;smarter approach&lt;/strong&gt;. Make an agent, give it a ReAct prompt, give it some tools. Then simply ask what you want it to do. It will do all the research for you, and generate the outcome for you.&lt;/p&gt;

&lt;p&gt;Here's how it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ask the Agent with an Objective&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example: &lt;em&gt;"Summarize this company's mission, vision, values, etc"&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Let the Agent Plan&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The agent decides what it needs to find first. Maybe it needs to scrape specific pages, maybe it needs to search the vector DB, maybe both.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tool Driven&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It uses available tools like Web Scraper, Vector Search, and Web Search.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Reasoning Loop&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The agent uses a ReAct loop:&lt;br&gt;&lt;br&gt;
&lt;code&gt;Thought → Action → Observation → Repeat&lt;/code&gt;&lt;br&gt;&lt;br&gt;
until it's confident in the output.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Final Output&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once done, the agent produces structured summaries for mission, vision, values, culture, etc just like a &lt;strong&gt;human researcher&lt;/strong&gt; would.&lt;/p&gt;

&lt;p&gt;This approach gave us &lt;strong&gt;maximum flexibility and minimal manual tweaking&lt;/strong&gt;. It also unlocked the potential for scaling. We can now give it different objectives for different use cases, and it will adapt accordingly.&lt;/p&gt;




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

&lt;p&gt;There are so many ways to utilize LLM, from Naive approach to a more Smart and Advanced Agentic flow.&lt;/p&gt;

&lt;p&gt;Each iteration taught us something:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;naive approach&lt;/strong&gt; was quick build, but the &lt;strong&gt;output was not good&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;page-by-page approach&lt;/strong&gt; improves structure but still &lt;strong&gt;struggles with scale&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;RAG based flow&lt;/strong&gt; brings &lt;strong&gt;precision, relevance, and performance&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;autonomous agent&lt;/strong&gt; was more flexible. It can dynamically &lt;strong&gt;deciding &lt;em&gt;what&lt;/em&gt; to look for and &lt;em&gt;how&lt;/em&gt; to get it&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the end, we learned that using LLMs well isn't just about the model.&lt;br&gt;&lt;br&gt;
It's about how you guide it. What you feed it, what tools it has, and what goal it's trying to reach.&lt;/p&gt;




&lt;p&gt;Smarter people than me had done research and written papers about this. Check it out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cdn.openai.com/business-guides-and-resources/a-practical-guide-to-building-agents.pdf" rel="noopener noreferrer"&gt;OpenAI Agent guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anthropic.com/engineering/building-effective-agents" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/aishwaryanr/awesome-generative-ai-guide/blob/main/research_updates/rag_research_table.md" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>openai</category>
      <category>rag</category>
      <category>programming</category>
      <category>ai</category>
    </item>
    <item>
      <title>Optimizing My Dev Workflow in 2025</title>
      <dc:creator>Boby Santoso</dc:creator>
      <pubDate>Thu, 17 Apr 2025 02:19:46 +0000</pubDate>
      <link>https://dev.to/bobysf12/optimizing-my-dev-workflow-in-2025-g4g</link>
      <guid>https://dev.to/bobysf12/optimizing-my-dev-workflow-in-2025-g4g</guid>
      <description>&lt;p&gt;Every Software Engineer has a development setup. Some keep it clean and minimal. Others turn their machine into a digital spaceship and a terminal that looks like it's from &lt;em&gt;The Matrix&lt;/em&gt;. Either way, &lt;strong&gt;your development setup is personal and built around your role, your habits, and your tools&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is how mine evolved. What started as a &lt;strong&gt;solid full-stack setup slowly turned into a mess&lt;/strong&gt;. Instead of buying new hardware, I decided to fix the way I work.  &lt;/p&gt;

&lt;h2&gt;
  
  
  My Daily Workflow
&lt;/h2&gt;

&lt;p&gt;Most days look like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing code in &lt;strong&gt;VSCode&lt;/strong&gt;. I usually have to &lt;strong&gt;juggle between 3 to 5 projects&lt;/strong&gt; daily&lt;/li&gt;
&lt;li&gt;Running local services with &lt;strong&gt;Docker&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Managing multiple &lt;strong&gt;Node.js&lt;/strong&gt;, &lt;strong&gt;React&lt;/strong&gt;, PHP apps. Frontends and Backends&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSH&lt;/strong&gt;, lots of it&lt;/li&gt;
&lt;li&gt;Using &lt;strong&gt;Studio 3T&lt;/strong&gt; or &lt;strong&gt;DBeaver&lt;/strong&gt; for databases&lt;/li&gt;
&lt;li&gt;Keeping a bunch of browser tabs open&lt;/li&gt;
&lt;li&gt;Communicating via &lt;strong&gt;Discord&lt;/strong&gt; and &lt;strong&gt;Teams&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Listening to &lt;strong&gt;Spotify&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Taking notes in &lt;strong&gt;Obsidian&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Drawing flows in &lt;strong&gt;Excalidraw&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It all works… until one day, my MacBook decided it had seen enough.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Memory Overload
&lt;/h3&gt;

&lt;p&gt;I'm using a base model MacBook Pro M2 with &lt;strong&gt;8GB of RAM&lt;/strong&gt;. It's a very capable machine, but my workflow was pushing it to the edge.&lt;/p&gt;

&lt;p&gt;RAM hovered around 90%, with swap memory kicking in constantly. Everything started to lag. App switching, typing, even simple tasks like scrolling through files.&lt;/p&gt;

&lt;p&gt;A few seconds of delay here and there might seem minor, but when you're in the flow, it breaks focus fast. I couldn't listen to music while coding. Even opening the browser could spike things. It got frustrating enough that I had to rethink how I worked.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Changed
&lt;/h2&gt;

&lt;h3&gt;
  
  
  From VSCode to NeoVim
&lt;/h3&gt;

&lt;p&gt;I've used VSCode for years. It's great, no doubt. But when you have to juggle up to 5 projects daily, you'll find that it's going to block you away. &lt;br&gt;
Mine feels heavy, especially with multiple windows and extensions. This is why I decided to find an alternative, until I started using &lt;a href="https://neovim.io/" rel="noopener noreferrer"&gt;&lt;strong&gt;NeoVim&lt;/strong&gt;&lt;/a&gt; instead.&lt;/p&gt;

&lt;p&gt;It's fast. With built-in LSP support, I still get syntax highlighting, autocompletion, and linting. The &lt;strong&gt;Vim keybindings&lt;/strong&gt; are taking some time to learn, but the speed and responsiveness make it &lt;strong&gt;worth it&lt;/strong&gt;. Or you can say that I never quit NeoVim because I don't know how to quit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Replaced Docker Desktop with Colima
&lt;/h3&gt;

&lt;p&gt;I replaced Docker Desktop with &lt;a href="https://github.com/abiosoft/colima" rel="noopener noreferrer"&gt;Colima&lt;/a&gt;, a &lt;strong&gt;lightweight alternative&lt;/strong&gt; that integrates with Docker CLI. It's easy to toggle on and off, uses less RAM, and doesn't drag down the system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Offloaded Docker to an Old Laptop
&lt;/h3&gt;

&lt;p&gt;I had 2 old laptops sitting around, both 10 years old. I turned both into a home server. Installed Ubuntu Server, set up Docker, and now I run all my containers remotely. With &lt;a href="https://tailscale.com/" rel="noopener noreferrer"&gt;Tailscale&lt;/a&gt;, I can securely connect to it like it's on the same network. This way, my MacBook doesn't have to run MongoDB, Redis, or RabbitMQ anymore. That alone freed up a lot of memory. I can even run other services like HomeAssistant, MailHog, Immich, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simplified My Terminal Workflow with Tmux
&lt;/h3&gt;

&lt;p&gt;Instead of opening a bunch of terminal tabs or windows, I switched to &lt;a href="https://github.com/tmux/tmux/wiki" rel="noopener noreferrer"&gt;&lt;strong&gt;tmux&lt;/strong&gt;&lt;/a&gt;. It lets me manage multiple sessions in one window, split panes, and run different services side by side. It’s lightweight, keyboard-driven, and fits perfectly with NeoVim.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ghostty
&lt;/h3&gt;

&lt;p&gt;Initially, I used the Mac's built-in Terminal, but encountered issues with font loading. This led me to explore alternative terminal options. I initially chose &lt;a href="https://sw.kovidgoyal.net/kitty/" rel="noopener noreferrer"&gt;&lt;strong&gt;Kitty&lt;/strong&gt;&lt;/a&gt;, but after the official release of &lt;a href="https://ghostty.org/" rel="noopener noreferrer"&gt;&lt;strong&gt;Ghostty&lt;/strong&gt;&lt;/a&gt;, I made the switch. It has been running flawlessly for me.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aerospace
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/nikitabobko/AeroSpace" rel="noopener noreferrer"&gt;Aerospace&lt;/a&gt; is a tiling window manager like i3 on Linux environment. This way I can manage my application windows and put them into different keys. I can navigate through my app windows very quickly with a shortcut:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terminal: &lt;code&gt;option + 1&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Browser: &lt;code&gt;option + c&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Obsidian note: &lt;code&gt;option + o&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Spotify: &lt;code&gt;option + s&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Discord: &lt;code&gt;option + d&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Teams: &lt;code&gt;option + t&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And many more..&lt;/p&gt;

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

&lt;p&gt;With this setup, I have to adapt with a more &lt;strong&gt;keyboard-driven&lt;/strong&gt; dev workflow. I have to get used to use tmux for switching back and forth between projects. Then I have to get used to NeoVim with its keybindings. I live in the Terminal now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coding is much more fun&lt;/strong&gt;, and that's what matters to me!&lt;/p&gt;

&lt;p&gt;You can checkout my &lt;a href="https://bybobyy.com/z/QNcocEQ" rel="noopener noreferrer"&gt;dotfiles&lt;/a&gt;, still work in progress, I used it daily on my Mac and Ubuntu servers.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>javascript</category>
      <category>frontend</category>
      <category>devtools</category>
    </item>
    <item>
      <title>Done &gt; Perfect: How I Built My Personal Website and Actually Shipped It</title>
      <dc:creator>Boby Santoso</dc:creator>
      <pubDate>Wed, 09 Apr 2025 14:01:44 +0000</pubDate>
      <link>https://dev.to/bobysf12/done-perfect-how-i-built-my-personal-website-and-actually-shipped-it-58h4</link>
      <guid>https://dev.to/bobysf12/done-perfect-how-i-built-my-personal-website-and-actually-shipped-it-58h4</guid>
      <description>&lt;p&gt;For years, I kept telling myself I'd build a personal website. Between full-time work, side projects, and life in general, it always took a backseat. But last week, I finally decided to stop overthinking and just build it.&lt;/p&gt;

&lt;p&gt;Here’s how I built my personal website from scratch and some thoughts along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Build Your Own Site?
&lt;/h2&gt;

&lt;p&gt;Sure, there are plenty of platforms where you can spin up a portfolio, blog, or even an entire personal brand in minutes. But I wanted something that was completely mine. No templates. No boilerplates. No limitations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I wanted full control&lt;/strong&gt;. Design, Code, Content, everything.&lt;br&gt;
Because at the end of the day, this is what gives me satisfaction as a programmer.&lt;/p&gt;

&lt;p&gt;Over the years, I’ve built countless side projects in my free time, most of which never saw the light of day. They usually ended with a voice in my head whispering, &lt;em&gt;“Someone must’ve built this already… so what’s the point?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But this time, the project was different. &lt;strong&gt;It wasn’t about trying to build the next big thing&lt;/strong&gt;. It was about building my own thing: &lt;strong&gt;a small corner of the internet that reflects who I am, what I love, and what I’m building.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Idea
&lt;/h2&gt;

&lt;p&gt;I wanted to build something simple, quick, so that I have &lt;em&gt;small wins&lt;/em&gt; which gives me the satisfaction and motivation to continue building. I know that if I pursue the perfect website, like 3D elements, animations, stylings, I will never publish this website and stop at the 2nd week.&lt;br&gt;
That's why I kept things very simple for now. I know that I will improve this as time goes, but right now this is good enough for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;p&gt;I kept the tech stack simple and familiar from my day to day use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt;: NextJs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend&lt;/strong&gt;: ExpressJs + Sqlite, to support my mini project like &lt;a href="https://dev.to/zap"&gt;Zap (URL Shortener)&lt;/a&gt; and &lt;a href="https://dev.to/squeeze"&gt;Image Squeezer&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Styling&lt;/strong&gt;: Material UI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment&lt;/strong&gt;: Dockerized, hosted on a DigitalOcean Droplet&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD&lt;/strong&gt;: Github Action&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blogs&lt;/strong&gt;: Simple Markdown file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I containerized everything, set up CI/CD using GH Action, and used Caddy as a reverse proxy. This is something that I am very familiar with. &lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges
&lt;/h2&gt;

&lt;p&gt;Even with a minimal setup, I ran into a few bumps.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Overengineering
&lt;/h3&gt;

&lt;p&gt;As a developer, it’s tempting to build everything from scratch, optimize everything, and make the perfect architecture. But I had to remind myself that this wasn’t a work project. This was for me. It’s okay to cut corners. It’s okay to leave TODOs. It’s okay to keep it simple.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Design paralysis
&lt;/h3&gt;

&lt;p&gt;I spent way too long researching the perfect portofolio design. Debating between dark mode, minimal mode, grid layouts, and font choices. Eventually, I just picked one and stuck with it. I told myself: You can always tweak it later. This helped me move forward.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Writing content
&lt;/h3&gt;

&lt;p&gt;Writing code? No problem. Writing about myself? Surprisingly difficult. I found myself rewriting the About page and landing intro multiple times. I wanted to be authentic, but also clear and concise. In the end, I just wrote like I talk, and that helped a lot.&lt;/p&gt;

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

&lt;p&gt;This project reminded me why I love building things. Not for work. Not for metrics. Not for validation. Just because I enjoy it.&lt;/p&gt;

&lt;p&gt;It’s easy to get caught up in the noise. Trends, AI hype, startup pressure, but this site is my way of slowing down and building something for myself. A place I can call home on the internet.&lt;/p&gt;

&lt;p&gt;And the best part? It’s not finished. I’ll keep adding to it, improving it, and sharing more along the way.&lt;/p&gt;

&lt;p&gt;If you’ve been putting off building your own site, let this be a sign. &lt;strong&gt;Start small. Make it yours. Ship it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You don’t need a perfect design, a clever domain name, or a flashy landing page. &lt;strong&gt;You just need to start.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Originally posted at &lt;a href="https://bybobyy.com/blog/how-i-built-personal-website-and-shipped-it" rel="noopener noreferrer"&gt;https://bybobyy.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>nextjs</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
