<?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: genuineswe</title>
    <description>The latest articles on DEV Community by genuineswe (@genuineswe).</description>
    <link>https://dev.to/genuineswe</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%2F201967%2Fc327c548-0540-4f1d-987e-0b12c6978c99.jpeg</url>
      <title>DEV Community: genuineswe</title>
      <link>https://dev.to/genuineswe</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/genuineswe"/>
    <language>en</language>
    <item>
      <title>Why I Finally Put My Frontend in a Container</title>
      <dc:creator>genuineswe</dc:creator>
      <pubDate>Tue, 14 Apr 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/genuineswe/why-i-finally-put-my-frontend-in-a-container-5deo</link>
      <guid>https://dev.to/genuineswe/why-i-finally-put-my-frontend-in-a-container-5deo</guid>
      <description>&lt;p&gt;Last month, a new developer joined our team. &lt;br&gt;
I pointed them to the project README. &lt;br&gt;
It had 15 steps to set up the environment. &lt;br&gt;
By lunch, he was still stuck on step 4.&lt;/p&gt;

&lt;p&gt;His Node version was 20. &lt;br&gt;
The project needed Node 16. &lt;br&gt;
He had a Windows machine. &lt;br&gt;
I was on a Mac. &lt;br&gt;
The "Standard Setup" was a myth.&lt;/p&gt;


&lt;h3&gt;
  
  
  The Wrong Approach
&lt;/h3&gt;

&lt;p&gt;For months, I tried to solve this with documentation. &lt;br&gt;
I added more "Note:" sections to the README. &lt;br&gt;
I shared my &lt;code&gt;.nvmrc&lt;/code&gt; file. &lt;br&gt;
I even wrote a setup script in Bash.&lt;/p&gt;

&lt;p&gt;But documentation is &lt;strong&gt;Knowledge that Rots&lt;/strong&gt;. &lt;br&gt;
Every time we updated a package, the README broke. &lt;br&gt;
The principle of "Don't Repeat Yourself" (DRY) applies to setups too. &lt;br&gt;
I was repeating the same manual fixes for every new hire. &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Effort&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Writing long READMEs&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Quickly outdated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom Bash scripts&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;OS-dependent issues&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Screen-sharing fixes&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Total time sink&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h3&gt;
  
  
  The Realization
&lt;/h3&gt;

&lt;p&gt;I realized that a README is just a wish list. &lt;br&gt;
It &lt;em&gt;hopes&lt;/em&gt; the developer has the right tools. &lt;br&gt;
I needed to provide the &lt;em&gt;actual&lt;/em&gt; environment. &lt;/p&gt;

&lt;p&gt;In &lt;em&gt;The Pragmatic Programmer&lt;/em&gt;, they talk about &lt;strong&gt;Orthogonality&lt;/strong&gt;. &lt;br&gt;
My application code was tied too closely to my global OS. &lt;br&gt;
If I updated my system, I broke my app. &lt;br&gt;
I needed a boundary.&lt;/p&gt;


&lt;h3&gt;
  
  
  The Correct Approach
&lt;/h3&gt;

&lt;p&gt;I replaced the 15-step README with one &lt;code&gt;Dockerfile&lt;/code&gt;. &lt;br&gt;
This file defines the operating system, Node, and dependencies. &lt;br&gt;
It is the "Single Source of Truth" for the environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json package-lock.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 5173&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["npm", "run", "dev"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;This works because it creates an isolated virtual environment where the Node version and OS are identical on every machine.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For full-stack features, I used &lt;strong&gt;Docker Compose&lt;/strong&gt;. &lt;br&gt;
Instead of telling devs to "install Redis locally," I just added it to the config.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.8'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5173:5173"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis:alpine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;This works because it orchestrates multiple services to run in a private network without cluttering the developer's laptop.&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  The Results
&lt;/h3&gt;

&lt;p&gt;I asked that same new developer to try the Docker setup instead. &lt;br&gt;
He ran &lt;code&gt;docker-compose up&lt;/code&gt;. &lt;br&gt;
The environment was ready in minutes. &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before Docker&lt;/th&gt;
&lt;th&gt;After Docker&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;First install&lt;/td&gt;
&lt;td&gt;2 hours&lt;/td&gt;
&lt;td&gt;5 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local OS pollution&lt;/td&gt;
&lt;td&gt;High (Global DBs)&lt;/td&gt;
&lt;td&gt;Zero&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment drift&lt;/td&gt;
&lt;td&gt;Constant&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Reflection
&lt;/h3&gt;

&lt;p&gt;Consistency is a feature. &lt;br&gt;
We spend too much time on "Incidental Complexity." &lt;br&gt;
Setting up a dev environment shouldn't be a test of patience. &lt;br&gt;
Docker is just a better way to communicate requirements.&lt;/p&gt;

&lt;p&gt;It isn't about being a DevOps expert. &lt;br&gt;
It is about being a professional who values their time. &lt;br&gt;
Keep your laptop clean. &lt;br&gt;
Containers are the modern README.&lt;/p&gt;




&lt;h3&gt;
  
  
  Your Next Step
&lt;/h3&gt;

&lt;p&gt;Check your project's README today. &lt;br&gt;
Count how many steps are just environment setup. &lt;br&gt;
Try to move the first three steps into a &lt;code&gt;Dockerfile&lt;/code&gt;. &lt;br&gt;
Run it locally and see if you can delete those lines from your documentation.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>docker</category>
      <category>productivity</category>
      <category>frontend</category>
    </item>
    <item>
      <title>How I Took My PageSpeed Score from 35 to 92...</title>
      <dc:creator>genuineswe</dc:creator>
      <pubDate>Wed, 25 Mar 2026 07:17:07 +0000</pubDate>
      <link>https://dev.to/genuineswe/pagespeed-35-to-90-what-i-got-wrong-then-right-1o2m</link>
      <guid>https://dev.to/genuineswe/pagespeed-35-to-90-what-i-got-wrong-then-right-1o2m</guid>
      <description>&lt;p&gt;Have you ever shipped a feature you were really proud of, only to watch your PageSpeed score sit at a painful &lt;strong&gt;35&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;That was me. My app worked fine locally. But once deployed, Lighthouse painted the screen red. Users noticed too: "Why is this so slow?"&lt;/p&gt;

&lt;p&gt;This is the story of how I diagnosed the problem, wasted time on the wrong fix, and eventually pushed the score past &lt;strong&gt;90&lt;/strong&gt;. If you've ever stared at a bad Lighthouse report and didn't know where to start, this one's for you.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Was Actually Wrong
&lt;/h2&gt;

&lt;p&gt;Before jumping into fixes, I needed to understand what was happening. I opened Chrome DevTools, ran Lighthouse, and found three clear issues:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;What I Found&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Huge JS bundle&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;main.js&lt;/code&gt; was over 2MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Render-blocking resources&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Components loaded upfront even when users didn't need them yet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Unoptimized images&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;PNG/JPEG files, some over 1MB each&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip for fellow devs:&lt;/strong&gt; Before optimizing anything, always run Lighthouse first. It tells you exactly where the bottleneck is. Don't guess.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This step alone saved me hours. In &lt;em&gt;The Pragmatic Programmer&lt;/em&gt;, Hunt and Thomas call this &lt;strong&gt;"Don't Assume It — Prove It."&lt;/strong&gt; It's tempting to jump straight into fixing things based on gut feeling, but the data almost always tells a different story than you expect.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Tried First (And Why It Failed)
&lt;/h2&gt;

&lt;p&gt;My instinct was to compress everything. I spent hours tweaking &lt;code&gt;Terser&lt;/code&gt; settings and adding image compression plugins to my build pipeline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; Score went from 35 to... 42. That's it.&lt;/p&gt;

&lt;p&gt;Here's what I got wrong: compressing a 2MB bundle down to 1.8MB still means the browser has to &lt;strong&gt;parse and execute 1.8MB of JavaScript&lt;/strong&gt; before the page becomes interactive. I was treating the symptom, not the cause.&lt;/p&gt;

&lt;p&gt;In hindsight, I was doing what &lt;em&gt;The Pragmatic Programmer&lt;/em&gt; warns against: &lt;strong&gt;"Programming by Coincidence"&lt;/strong&gt; — making changes and hoping something improves without truly understanding the underlying problem. The config tweaks felt productive, but I had no clear mental model of &lt;em&gt;why&lt;/em&gt; the page was slow.&lt;/p&gt;

&lt;p&gt;The real question wasn't &lt;em&gt;"How do I make this smaller?"&lt;/em&gt; It was &lt;em&gt;"Does the user actually need all of this right now?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That reframe is what John Ousterhout describes in &lt;em&gt;A Philosophy of Software Design&lt;/em&gt; as tackling &lt;strong&gt;complexity at its source&lt;/strong&gt; rather than patching around it. The bundle wasn't too big because it lacked compression. It was too big because it was loading things nobody asked for.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Actually Worked
&lt;/h2&gt;

&lt;p&gt;Once I reframed the problem, three changes made the biggest difference:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Code Splitting by Route
&lt;/h3&gt;

&lt;p&gt;Instead of shipping the entire app in one bundle, I split it so each page only loads what it needs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before: everything loads at once&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Dashboard&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./pages/Dashboard&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="nx"&gt;Settings&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./pages/Settings&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// After: each page loads on demand&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Suspense&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lazy&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="s1"&gt;react&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;Dashboard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lazy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./pages/Dashboard&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;Settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lazy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./pages/Settings&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="nx"&gt;fallback&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;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;}&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Routes&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/dashboard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;element&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;lt;&lt;/span&gt;&lt;span class="nx"&gt;Dashboard&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/settings&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;element&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;lt;&lt;/span&gt;&lt;span class="nx"&gt;Settings&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Routes&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Suspense&lt;/span&gt;&lt;span class="err"&gt;&amp;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;Why this works:&lt;/strong&gt; The user visiting &lt;code&gt;/dashboard&lt;/code&gt; doesn't download the code for &lt;code&gt;/settings&lt;/code&gt;. Simple, but the impact is massive.&lt;/p&gt;

&lt;p&gt;This is the &lt;strong&gt;YAGNI principle&lt;/strong&gt; &lt;em&gt;(You Aren't Gonna Need It)&lt;/em&gt; applied to delivery: don't send the browser code it doesn't need yet. Martin Fowler and Kent Beck popularized YAGNI for writing code, but it applies just as well to &lt;em&gt;shipping&lt;/em&gt; code.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Lazy Loading Images
&lt;/h3&gt;

&lt;p&gt;Images below the fold don't need to load immediately. HTML5 makes this a one-liner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Above the fold: load immediately --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"hero.webp"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Hero banner"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Below the fold: load when user scrolls --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"footer-graphic.webp"&lt;/span&gt; &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Footer"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No library needed. Just one attribute.&lt;/p&gt;

&lt;p&gt;Robert C. Martin writes in &lt;em&gt;Clean Code&lt;/em&gt; that &lt;strong&gt;"the best code is no code at all."&lt;/strong&gt; The same idea applies here: the best optimization is often the simplest one. A single HTML attribute replaced what used to require a JavaScript library with scroll listeners.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Switching to WebP
&lt;/h3&gt;

&lt;p&gt;I converted all images from PNG/JPEG to WebP, with a fallback for older browsers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;picture&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"photo.webp"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/webp"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"photo.jpg"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Example"&lt;/span&gt; &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;WebP files are typically 25-35% smaller&lt;/strong&gt; than JPEG at the same quality. Multiply that across every image on your site and the savings add up fast.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Initial Bundle Size&lt;/td&gt;
&lt;td&gt;2.2 MB&lt;/td&gt;
&lt;td&gt;340 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;First Contentful Paint&lt;/td&gt;
&lt;td&gt;4.2s&lt;/td&gt;
&lt;td&gt;0.8s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PageSpeed Score&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;35&lt;/strong&gt; 🔴&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;92&lt;/strong&gt; 🟢&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




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

&lt;p&gt;The biggest takeaway wasn't about any specific tool or technique. It was a mindset shift:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance optimization isn't about making things smaller. It's about loading the right things at the right time.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Pragmatic Programmer&lt;/em&gt; has a concept called &lt;strong&gt;"Tracer Bullets"&lt;/strong&gt; — find the path that lights up the whole system, then iterate. Applied to performance work, this means: identify the critical rendering path first. Everything that isn't on that path can wait.&lt;/p&gt;

&lt;p&gt;Before applying any optimization, ask yourself: &lt;em&gt;"Does the user need this resource right now?"&lt;/em&gt; If the answer is no, defer it.&lt;/p&gt;

&lt;p&gt;Three things that made the real difference:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Code Splitting&lt;/strong&gt; — only ship the JS the user actually needs &lt;em&gt;(YAGNI)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lazy Loading&lt;/strong&gt; — defer images and components below the fold &lt;em&gt;(simplicity over cleverness)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modern Formats&lt;/strong&gt; — use WebP instead of legacy image formats&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you're facing a similar problem, start with a Lighthouse audit. Look at what's blocking your initial load. Chances are, the fix isn't compression — it's prioritization.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Kaizen"&lt;/em&gt; — the Japanese philosophy of continuous, small improvements — is how I think about performance now. You don't need a massive rewrite. A few targeted changes, guided by data, can transform your app.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;strong&gt;Your action step:&lt;/strong&gt; Open your app right now. Run Lighthouse. Check what your biggest bundle is. If it's over 500KB, try code splitting that one route first. Come back and tell me what happened in the comments.&lt;/p&gt;

</description>
      <category>webperf</category>
      <category>react</category>
      <category>javascript</category>
      <category>engineering</category>
    </item>
  </channel>
</rss>
