<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ performance - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ performance - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Mon, 25 May 2026 05:05:50 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/performance/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Use PostgreSQL as a Cache, Queue, and Search Engine ]]>
                </title>
                <description>
                    <![CDATA[ "Just use Postgres" has been circulating as advice for years, but most articles arguing for it are opinion pieces. I wanted hard numbers. So I built a benchmark suite that pits vanilla PostgreSQL agai ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-postgresql-as-a-cache-queue-and-search-engine/</link>
                <guid isPermaLink="false">69e7accfe43672781470ff97</guid>
                
                    <category>
                        <![CDATA[ PostgreSQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ database ]]>
                    </category>
                
                    <category>
                        <![CDATA[ backend ]]>
                    </category>
                
                    <category>
                        <![CDATA[ performance ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Databases ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Aaron Yong ]]>
                </dc:creator>
                <pubDate>Tue, 21 Apr 2026 16:58:55 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/6fcdd3c0-eead-42a7-b2f0-cf4c6a3d06dc.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>"Just use Postgres" has been circulating as advice for years, but most articles arguing for it are opinion pieces. I wanted hard numbers.</p>
<p>So I built a benchmark suite that pits vanilla PostgreSQL against a feature-optimized PostgreSQL instance — measuring caching, message queues, full-text search, and pub/sub under controlled conditions.</p>
<p>In this article, you'll learn how to use PostgreSQL's built-in features for caching, job queues, full-text search, and pub/sub. You'll see actual benchmark results (latency percentiles, throughput, and error rates) comparing naive PostgreSQL patterns against optimized ones, and understand where PostgreSQL's limits are so you can decide whether you really need that extra service in your stack.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-the-setup">The Setup</a></p>
</li>
<li><p><a href="#heading-benchmark-1-caching-with-unlogged-tables">Benchmark 1: Caching with UNLOGGED Tables</a></p>
</li>
<li><p><a href="#heading-benchmark-2-job-queues-with-skip-locked">Benchmark 2: Job Queues with SKIP LOCKED</a></p>
</li>
<li><p><a href="#heading-benchmark-3-full-text-search-with-tsvector">Benchmark 3: Full-Text Search with tsvector</a></p>
</li>
<li><p><a href="#heading-benchmark-4-pubsub-with-listennotify">Benchmark 4: Pub/Sub with LISTEN/NOTIFY</a></p>
</li>
<li><p><a href="#heading-the-combined-workload-the-honest-test">The Combined Workload: The Honest Test</a></p>
</li>
<li><p><a href="#heading-what-i-learned">What I Learned</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow along or reproduce the benchmarks, you'll need:</p>
<ul>
<li><p>Docker and Docker Compose</p>
</li>
<li><p>Node.js 20+ (for the Express TypeScript API layer)</p>
</li>
<li><p><a href="https://k6.io/">k6</a> for load testing</p>
</li>
<li><p>Basic familiarity with SQL and PostgreSQL</p>
</li>
</ul>
<p>The full benchmark project is <a href="https://github.com/aaronhsyong2/pg-stack-benchmark">open source on GitHub</a> — you can clone it and run every test yourself.</p>
<h2 id="heading-the-setup">The Setup</h2>
<p>The benchmark uses two identical PostgreSQL 17 instances running in Docker containers, each with fixed resource constraints (2 CPUs, 2 GB RAM). Both share the same Express TypeScript API layer — the only difference is which PostgreSQL features are enabled.</p>
<pre><code class="language-plaintext">┌─────────┐     ┌──────────────────┐     ┌─────────────────┐
│   k6    │────&gt;│  Express API     │────&gt;│  PG Baseline    │
│  (load  │     │  (TypeScript)    │     │  (vanilla PG17) │
│  test)  │────&gt;│  Port 3001/3002  │────&gt;│  PG Modded      │
└─────────┘     └──────────────────┘     │  (features on)  │
                                         └─────────────────┘
</code></pre>
<p>The baseline instance uses naïve approaches (regular tables, <code>ILIKE</code> search, polling). The modded instance uses PostgreSQL's built-in features (UNLOGGED tables, <code>tsvector</code> with GIN indexes, <code>LISTEN/NOTIFY</code>, partial indexes). Same hardware, same API code, same data. Only the database features differ.</p>
<p>Both instances share this tuned <code>postgresql.conf</code>:</p>
<pre><code class="language-ini"># Memory allocation
shared_buffers = 512MB           # 25% of available RAM
effective_cache_size = 1536MB    # 75% of RAM — helps the query planner
work_mem = 16MB                  # per-sort/hash operation memory

# SSD-optimized planner settings
random_page_cost = 1.1           # default 4.0 assumes spinning disks
effective_io_concurrency = 200   # allow parallel I/O on SSDs
</code></pre>
<p>These settings matter. The defaults assume spinning disks from the early 2000s. Setting <code>random_page_cost = 1.1</code> tells the query planner that random reads are nearly as fast as sequential reads on SSDs, which encourages index usage over sequential scans.</p>
<h2 id="heading-benchmark-1-caching-with-unlogged-tables">Benchmark 1: Caching with UNLOGGED Tables</h2>
<p><strong>The idea:</strong> Use an UNLOGGED table as an in-database cache. UNLOGGED tables skip PostgreSQL's Write-Ahead Log (WAL) — the mechanism that guarantees durability. Since cache data is ephemeral by nature, losing it on a crash is acceptable, and skipping WAL removes the biggest write bottleneck.</p>
<pre><code class="language-sql">-- Modded: UNLOGGED table for cache entries
CREATE UNLOGGED TABLE cache_entries (
    key TEXT PRIMARY KEY,
    value JSONB NOT NULL,
    expires_at TIMESTAMPTZ
);

-- Baseline: same schema, but a regular (logged) table
CREATE TABLE cache_entries (
    key TEXT PRIMARY KEY,
    value JSONB NOT NULL,
    expires_at TIMESTAMPTZ
);
</code></pre>
<h3 id="heading-results-200-virtual-users">Results (200 Virtual Users)</h3>
<table>
<thead>
<tr>
<th>Mode</th>
<th>p50</th>
<th>p95</th>
<th>avg</th>
<th>req/s</th>
</tr>
</thead>
<tbody><tr>
<td>Baseline (regular table)</td>
<td>1.87ms</td>
<td>6.00ms</td>
<td>2.50ms</td>
<td>1,754/s</td>
</tr>
<tr>
<td>Modded (UNLOGGED table)</td>
<td>1.71ms</td>
<td>5.24ms</td>
<td>2.17ms</td>
<td>1,760/s</td>
</tr>
</tbody></table>
<p>A consistent 13% improvement across all percentiles. Not dramatic, but free — you change one keyword in your <code>CREATE TABLE</code> statement.</p>
<h3 id="heading-under-stress-1000-virtual-users-no-sleep">Under Stress (1,000 Virtual Users, No Sleep)</h3>
<table>
<thead>
<tr>
<th>Mode</th>
<th>p50</th>
<th>p95</th>
<th>req/s</th>
<th>Total Requests</th>
</tr>
</thead>
<tbody><tr>
<td>Baseline</td>
<td>83.38ms</td>
<td>143.23ms</td>
<td>7,663/s</td>
<td>728,021</td>
</tr>
<tr>
<td>Modded</td>
<td>77.69ms</td>
<td>126.39ms</td>
<td>8,062/s</td>
<td>765,934</td>
</tr>
</tbody></table>
<p>The relative improvement stays locked at 12-13% regardless of load level. The UNLOGGED advantage is a per-write optimization — it saves the same amount of I/O whether you are doing 100 or 10,000 writes per second. The modded instance served 37,000 more requests in the same time window.</p>
<h3 id="heading-the-verdict">The Verdict</h3>
<p>UNLOGGED tables won't match Redis for sub-millisecond hot-path caching (real-time bidding, gaming leaderboards). But for web applications where the difference between 2ms and 5ms is invisible to users, they eliminate an entire infrastructure dependency for zero additional complexity.</p>
<p>You do give up Redis data structures (sorted sets, HyperLogLog, streams). If you need those, a dedicated cache is still the right call.</p>
<h2 id="heading-benchmark-2-job-queues-with-skip-locked">Benchmark 2: Job Queues with SKIP LOCKED</h2>
<p><strong>The idea:</strong> Use PostgreSQL as a job queue with <code>SELECT ... FOR UPDATE SKIP LOCKED</code>. Multiple workers poll the same table, and <code>SKIP LOCKED</code> ensures each worker gets a different row — no duplicates, no contention.</p>
<pre><code class="language-sql">-- Queue table with a partial index on pending jobs only
CREATE TABLE job_queue (
    id SERIAL PRIMARY KEY,
    payload JSONB NOT NULL,
    status TEXT NOT NULL DEFAULT 'pending',
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Partial index: only indexes pending jobs
-- As jobs complete, they leave the index — it stays small forever
CREATE INDEX idx_pending_jobs ON job_queue (created_at)
    WHERE status = 'pending';
</code></pre>
<p>The dequeue pattern:</p>
<pre><code class="language-sql">-- Atomic dequeue: select + update in one statement
UPDATE job_queue SET status = 'processing'
WHERE id = (
    SELECT id FROM job_queue
    WHERE status = 'pending'
    ORDER BY created_at
    LIMIT 1
    FOR UPDATE SKIP LOCKED  -- skip rows locked by other workers
) RETURNING *;
</code></pre>
<p>How <code>SKIP LOCKED</code> works: Worker A locks row 1. Worker B tries row 1, sees the lock, skips it, and takes row 2 instead. No blocking, no duplicates. If a worker crashes, the transaction rolls back and the row becomes available again.</p>
<h3 id="heading-results-100-producers-50-consumers">Results (100 Producers + 50 Consumers)</h3>
<table>
<thead>
<tr>
<th>Mode</th>
<th>p50</th>
<th>p95</th>
<th>avg</th>
<th>req/s</th>
</tr>
</thead>
<tbody><tr>
<td>Baseline (full index)</td>
<td>1.90ms</td>
<td>5.01ms</td>
<td>2.30ms</td>
<td>1,053/s</td>
</tr>
<tr>
<td>Modded (partial index)</td>
<td>1.81ms</td>
<td>5.28ms</td>
<td>2.29ms</td>
<td>1,052/s</td>
</tr>
</tbody></table>
<p>They're virtually identical. The partial index doesn't show its value in a 60-second benchmark because the table doesn't accumulate enough completed rows for the index size difference to matter. In a production system with millions of completed jobs, the partial index keeps the index at kilobytes while a full index grows to gigabytes.</p>
<h3 id="heading-the-verdict">The Verdict</h3>
<p><code>SKIP LOCKED</code> is production-ready for job queues. Libraries like <a href="https://github.com/timgit/pg-boss">pg-boss</a> (Node.js) and <a href="https://github.com/riverqueue/river">river</a> (Go) build on this exact pattern.</p>
<p>You do give up exchange/routing patterns (fan-out, topic-based routing) and consumer groups with message replay. If you need those, a dedicated message broker is still the right tool. For simple "process this job once" workloads, PostgreSQL handles it.</p>
<h2 id="heading-benchmark-3-full-text-search-with-tsvector">Benchmark 3: Full-Text Search with tsvector</h2>
<p><strong>The idea:</strong> Use PostgreSQL's built-in full-text search instead of a separate search service. A <code>tsvector</code> column stores pre-processed search tokens, and a GIN (Generalized Inverted Index) enables fast lookups using the same inverted index concept that powers Elasticsearch.</p>
<pre><code class="language-sql">-- Search-optimized article table
CREATE TABLE articles (
    id SERIAL PRIMARY KEY,
    title TEXT NOT NULL,
    body TEXT NOT NULL,
    search_vector tsvector  -- pre-computed search tokens
);

-- GIN index for full-text search
CREATE INDEX idx_search ON articles USING GIN (search_vector);

-- Auto-update search_vector on insert/update
CREATE OR REPLACE FUNCTION update_search_vector() RETURNS trigger AS $$
BEGIN
    NEW.search_vector := to_tsvector('english',
        COALESCE(NEW.title, '') || ' ' || COALESCE(NEW.body, ''));
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_search
    BEFORE INSERT OR UPDATE ON articles
    FOR EACH ROW EXECUTE FUNCTION update_search_vector();
</code></pre>
<p>The baseline uses <code>ILIKE</code> with a leading wildcard — the approach most developers reach for first:</p>
<pre><code class="language-sql">-- Baseline: sequential scan on every query
SELECT * FROM articles
WHERE title ILIKE '%postgresql%' OR body ILIKE '%postgresql%';

-- Modded: GIN index lookup with relevance ranking
SELECT id, title,
    ts_rank(search_vector, plainto_tsquery('english', 'postgresql')) AS rank
FROM articles
WHERE search_vector @@ plainto_tsquery('english', 'postgresql')
ORDER BY rank DESC LIMIT 20;
</code></pre>
<h3 id="heading-results-500-virtual-users">Results (500 Virtual Users)</h3>
<table>
<thead>
<tr>
<th>Mode</th>
<th>p50</th>
<th>p95</th>
<th>avg</th>
<th>req/s</th>
</tr>
</thead>
<tbody><tr>
<td>Baseline (ILIKE)</td>
<td>1.96ms</td>
<td>101.83ms</td>
<td>25.22ms</td>
<td>561/s</td>
</tr>
<tr>
<td>Modded (tsvector + GIN)</td>
<td>2.76ms</td>
<td>10.39ms</td>
<td>3.76ms</td>
<td>675/s</td>
</tr>
</tbody></table>
<p>This is the standout result. The baseline's p95 of 101ms versus the modded's 10ms is a 10x improvement.</p>
<p>Why the baseline's p50 (1.96ms) is slightly better than the modded's (2.76ms): simple <code>ILIKE</code> queries on small result sets can be fast when the data fits in <code>shared_buffers</code>. But as load increases and the buffer cache is contested, sequential scans degrade dramatically. The GIN index stays stable.</p>
<h3 id="heading-under-stress-500-virtual-users-no-sleep">Under Stress (500 Virtual Users, No Sleep)</h3>
<table>
<thead>
<tr>
<th>Mode</th>
<th>p50</th>
<th>p95</th>
<th>req/s</th>
<th>Total Requests</th>
</tr>
</thead>
<tbody><tr>
<td>Baseline (ILIKE)</td>
<td>599ms</td>
<td>1,000ms</td>
<td>558/s</td>
<td>50,212</td>
</tr>
<tr>
<td>Modded (tsvector)</td>
<td>209ms</td>
<td>396ms</td>
<td>1,441/s</td>
<td>129,679</td>
</tr>
</tbody></table>
<p>ILIKE collapses to 1-second p95 latencies. Each query forces a sequential scan of all 10,000 articles, blocking shared buffers and starving concurrent queries. The tsvector approach serves 2.6x more requests in the same time window because the GIN index lookup is O(log n) regardless of concurrency.</p>
<h3 id="heading-the-verdict">The Verdict</h3>
<p>This is the strongest argument in the entire benchmark. The fix requires zero extensions — <code>to_tsvector()</code>, <code>plainto_tsquery()</code>, and <code>CREATE INDEX USING GIN</code> are all built into core PostgreSQL. If you're doing <code>WHERE column ILIKE '%term%'</code> on any table with more than a few thousand rows, you're leaving massive performance on the table.</p>
<p>You do give up distributed search across shards, complex analyzers for CJK languages, and aggregation/faceted search pipelines. For a product search bar, blog search, or internal tool — PostgreSQL is enough.</p>
<h2 id="heading-benchmark-4-pubsub-with-listennotify">Benchmark 4: Pub/Sub with LISTEN/NOTIFY</h2>
<p><strong>The idea:</strong> Use PostgreSQL's native <code>LISTEN/NOTIFY</code> for pub/sub messaging, triggered automatically on INSERT via a database trigger.</p>
<pre><code class="language-sql">-- Trigger that fires pg_notify on every new message
CREATE OR REPLACE FUNCTION notify_message() RETURNS trigger AS $$
BEGIN
    PERFORM pg_notify(NEW.channel, NEW.payload::text);
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_notify
    AFTER INSERT ON messages
    FOR EACH ROW EXECUTE FUNCTION notify_message();
</code></pre>
<h3 id="heading-results-200-virtual-users">Results (200 Virtual Users)</h3>
<table>
<thead>
<tr>
<th>Mode</th>
<th>p50</th>
<th>p95</th>
<th>avg</th>
<th>req/s</th>
</tr>
</thead>
<tbody><tr>
<td>Baseline (poll-based)</td>
<td>1.99ms</td>
<td>6.04ms</td>
<td>2.84ms</td>
<td>1,116/s</td>
</tr>
<tr>
<td>Modded (LISTEN/NOTIFY)</td>
<td>1.65ms</td>
<td>4.80ms</td>
<td>2.13ms</td>
<td>1,131/s</td>
</tr>
</tbody></table>
<p>Here we have a 20% improvement at p95. The trigger-based approach does more work per INSERT (INSERT + NOTIFY), but the reduced round trips and better connection reuse patterns offset the overhead.</p>
<h3 id="heading-the-verdict">The Verdict</h3>
<p><code>LISTEN/NOTIFY</code> works for real-time features where you would otherwise reach for Redis pub/sub. The main limitation is payload size (8,000 bytes maximum) and the requirement for dedicated connections (incompatible with PgBouncer in transaction mode).</p>
<h2 id="heading-the-combined-workload-the-honest-test">The Combined Workload: The Honest Test</h2>
<p>Individual benchmarks are flattering. The real question: can one PostgreSQL instance handle caching, queues, search, and pub/sub simultaneously without degrading?</p>
<h3 id="heading-results-all-four-workloads-running-together">Results (All Four Workloads Running Together)</h3>
<table>
<thead>
<tr>
<th>Mode</th>
<th>p50</th>
<th>p95</th>
<th>avg</th>
<th>req/s</th>
</tr>
</thead>
<tbody><tr>
<td>Baseline</td>
<td>1.65ms</td>
<td>5.24ms</td>
<td>2.17ms</td>
<td>1,424/s</td>
</tr>
<tr>
<td>Modded</td>
<td>1.86ms</td>
<td>6.05ms</td>
<td>2.47ms</td>
<td>1,417/s</td>
</tr>
</tbody></table>
<p>Under combined load, the baseline marginally outperforms the modded setup. The modded PostgreSQL does more work per operation — maintaining GIN indexes, firing triggers, running <code>pg_cron</code> in the background. When all these features are active simultaneously, the overhead is measurable: about 15% higher p95 latency.</p>
<p>But both setups stay comfortably under 10ms at p95. For most web applications, that's more than good enough.</p>
<h2 id="heading-what-i-learned">What I Learned</h2>
<p>After running all these benchmarks, here's what I would tell a team evaluating whether to "just use Postgres":</p>
<ol>
<li><p><strong>Do it for full-text search:</strong> Switching from <code>ILIKE</code> to <code>tsvector</code> with a GIN index is a 10x improvement that requires zero extensions. This is the single highest-ROI change in the entire PostgreSQL ecosystem, and most developers don't know it exists.</p>
</li>
<li><p><strong>Do it for job queues:</strong> <code>SKIP LOCKED</code> is production-ready and eliminates RabbitMQ for simple "process this job" workloads. Use a library like pg-boss or river rather than rolling your own.</p>
</li>
<li><p><strong>Consider it for caching:</strong> UNLOGGED tables give a steady 13% improvement over regular tables. If sub-millisecond latency is not a hard requirement (and for most web apps, it is not), you can drop Redis entirely.</p>
</li>
<li><p><strong>Be honest about the overhead:</strong> Running all four roles simultaneously adds about 15% latency compared to running any single role. Whether that matters depends on your latency budget.</p>
</li>
<li><p><strong>Know where to stop:</strong> PostgreSQL won't match Redis for sub-millisecond caching, Kafka for millions of messages per second, or Elasticsearch for distributed multi-node search with complex analyzers. The line is at extreme throughput or extreme specialization.</p>
</li>
</ol>
<p>The honest conclusion is not "PostgreSQL does everything." It is: for most applications, a single well-configured PostgreSQL instance handles 80% of what you would otherwise need three to five additional services for. That is less infrastructure to deploy, monitor, and maintain — and fewer things to break at 3 AM.</p>
<p>Enterprise-scale applications processing millions of messages per second, serving sub-millisecond cache hits to millions of concurrent users, or running distributed search across terabytes of documents will still need specialized tools. Those tools exist for a reason, and at that scale the operational cost of running them is justified by the performance you get back.</p>
<p>But most of us aren't building at that scale — and may never need to. Starting with PostgreSQL for these roles means you ship faster with fewer moving parts. If and when you outgrow what PostgreSQL can handle, your benchmarks will tell you exactly which role needs to be extracted into a dedicated service. That is a much better position than starting with five services on day one because you assumed you would need them.</p>
<p>The <a href="https://github.com/aaronhsyong2/pg-stack-benchmark">benchmark project</a> is open source if you want to reproduce these results or adapt the tests for your own workload.</p>
<p>You can find more of my writing at <a href="https://site.aaronhsyong.com">site.aaronhsyong.com</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Find Any File on Windows Like a Linux User (using Windows Powershell) ]]>
                </title>
                <description>
                    <![CDATA[ Sometimes you might struggle to find a file or program when you have no idea where it could be saved or installed. And the Windows user interface may not always give you the results you want. If that' ]]>
                </description>
                <link>https://www.freecodecamp.org/news/find-any-file-on-windows-like-a-linux-user/</link>
                <guid isPermaLink="false">69c44ce410e664c5daef3e59</guid>
                
                    <category>
                        <![CDATA[ Windows ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Powershell ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Scripting ]]>
                    </category>
                
                    <category>
                        <![CDATA[ automation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ performance ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Piotr &quot;NotBlackMagic&quot; Opoka ]]>
                </dc:creator>
                <pubDate>Wed, 25 Mar 2026 16:00:00 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/527c9267-0583-49c4-9e90-89abcf186b9d.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Sometimes you might struggle to find a file or program when you have no idea where it could be saved or installed. And the Windows user interface may not always give you the results you want. If that's the case for you, you're in the right place.</p>
<p><code>Get-ChildItem</code> (also known as <code>gci</code>, <code>ls</code>, <code>dir</code> ) is a very powerful command. And one of its most iconic uses is to find/search for a file. It's more precise and more reliable than Windows Explorer. It even has better filtering options that show the results that are more relevant to you.</p>
<p>In this tutorial, you'll learn how to use <code>gci</code> and how to combine it with other commands so that it becomes an even more powerful tool. Remember to enable copy-pasting in Windows PowerShell, so it's easier for you to follow along. You can see how to enable it <a href="https://notblackmagic.hashnode.dev/enable-copy-pasting-in-windows-powershell-cli-in-3-steps">here</a>.</p>
<h3 id="heading-what-well-cover">What we'll cover:</h3>
<ol>
<li><p><a href="#heading-1-basic-explanation-of-the-get-childitem-command">Basic explanation of the Get-ChildItem command</a></p>
<ul>
<li><a href="#heading-most-used-examples-of-searching-by-gci-command">Most used examples of searching by gci command</a></li>
</ul>
</li>
<li><p><a href="#heading-2-setup-for-other-more-complex-examples">Setup for other more complex examples</a></p>
</li>
<li><p><a href="#heading-3-when-is-the-path-option-not-needed">When is the -Path option not needed?</a></p>
</li>
<li><p><a href="#heading-4-advanced-searching-combining-getchildren-with-the-whereobject-command">Advanced Searching – Combining Get-ChildItem with the Where-Object Command</a></p>
<ul>
<li><p><a href="#heading-41-how-to-search-through-only-a-particular-directory">4.1. How to search through only a particular directory</a></p>
</li>
<li><p><a href="#heading-42-how-to-search-while-excluding-a-particular-directory">4.2. How to search while excluding a particular directory</a></p>
</li>
<li><p><a href="#heading-43-searching-only-1-directory-from-many-with-exactly-the-same-name">4.3 Searching only 1 directory from many with exactly the same name</a></p>
</li>
<li><p><a href="#heading-44-filter-how-deep-how-many-folders-in-you-want-to-search-for-the-file">4.4 Filter how deep (how many folders in) you want to search for the file</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-5-how-to-search-through-hidden-files">How to Search Through Hidden Files</a></p>
</li>
<li><p><a href="#heading-6-how-can-you-know-all-the-properties-that-you-can-use-as-a-filter">How can you know all the properties that you can use as a filter?</a></p>
<ul>
<li><a href="#heading-how-to-retrieve-only-1-desired-property">How to retrieve only 1 desired property</a></li>
</ul>
</li>
<li><p><a href="#heading-7-i-dont-know-the-files-name-but-i-know-whats-inside-it-how-do-i-find-the-file-by-its-content">I don't know the file’s name, but I know what's inside it. How do I find the file by its content?</a></p>
</li>
<li><p><a href="#heading-8-i-cant-see-the-full-path-how-do-i-fix-this">I can't see the full path - how do I fix this?</a></p>
</li>
<li><p><a href="#heading-9-hard-to-read-open-the-results-in-the-text-editor-of-your-choice">Hard to read? Open the results in the text editor of your choice</a></p>
</li>
<li><p><a href="#heading-10-summary-the-ultimate-commands-for-searching-and-finding-whatever-you-need">Summary - the ultimate commands for searching and finding whatever you need</a></p>
</li>
</ol>
<h2 id="heading-1-basic-explanation-of-the-get-childitem-command">1. Basic Explanation of the <code>Get-ChildItem</code> Command</h2>
<p>Let's take a look at the example searching script to understand how it works:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Path "C:\path to\your directory\" -Filter "*whatImLookingFor*"
</code></pre>
<p><code>Get-ChildItem</code> (aliases: <code>dir</code>, <code>ls</code>, <code>gci</code>) lists the content of a folder or directory just like the Linux <code>ls</code> command does.</p>
<p>This command works by searching every single file and directory <strong>in the path specified.</strong> It shows you everything it found that <strong>matches the filter</strong>. It doesn't mean that this command doesn't look everywhere else – because it does.</p>
<p>So you specify the path that is the parent (folder), which means that every folder and file under it is its child. If you know some CSS and JavaScript, treat it the same way that these languages do.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/0bd18776-72bc-46be-bbaa-b616c5ce1c3a.png" alt="Picture: a visual explanation of -depth and -recurse parameters. It shows &quot;Documents&quot; folder at the bottom, which is tagged both as Parent and Depth 0. It points upwords to its child folders and a child file. Those are tagged as Depth 0 children of our Documents folder. They are simultaneously tagged as Depth 1 parents, so files and folders. to which they are pointing upwards, are their Depth 1 children." style="display:block;margin:0 auto" width="821" height="656" loading="lazy">

<p>If you don't use <code>-Recurse</code> or <code>-Depth</code>, then the command works only in your current directory (parent Depth level 0) and searches for its children inside that directory (children Depth level 0).</p>
<p>If you use <code>-Recurse</code>, then the <code>gci</code> will search for what you want on ALL LEVELS. But by using<code>-Depth</code>, you can specify how deep you want it to look for a file/folder.</p>
<p>To recurse means "to repeat an operation". So, <code>-Recurse</code> means that <code>gci</code> will repeat the search for your file or folder in every child element of the <em>"Documents"</em> directory, and every directory inside it, all levels deep.</p>
<p>All of these files and folders are children of your <em>"Documents"</em> folder. If you delete the folder, you delete everything inside it too.</p>
<p><code>-Filter</code> filters the output of the command to only show what matches the filter (examples of how to use filter are further in the article).</p>
<p><code>-Path</code> tells where the command should be looking for files (by using "C:\", for example, you're telling it to look at the very basis of your computer). If you want to search in certain directory it would look like this:</p>
<pre><code class="language-powershell">Get-ChildItem -Path "C:\path to\your directory\"
</code></pre>
<p>OR</p>
<pre><code class="language-powershell">Get-ChildItem -Path "~\Documents\path to\your directory\"
</code></pre>
<p><code>~\</code> here is a shorthand for "inside current user's folder" or <strong>"C:\Users\YourUsername"</strong>.</p>
<p>Next, we can specify whether we'd like to look for a <strong>file</strong> or a <strong>folder</strong>, so we have fewer results to look at:</p>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Recurse -Filter "*whatImLookingFor*" -File
</code></pre>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Recurse -Filter "*whatImLookingFor*" -Directory
</code></pre>
<p>You might be wondering how you can stop the search if it takes too long. When you're using <code>-Recurse,</code> the output that you'll get might become quite overwhelming, especially if you didn't specify your command enough (more about that in <a href="#heading-3-when-is-the-path-option-not-needed">step 3</a> and <a href="#heading-4-advanced-searching-combining-getchildren-with-the-whereobject-command">step 4</a>). Luckily, you can stop any command in PowerShell after starting it with <strong>Ctrl + C</strong> OR <strong>Ctrl + Z</strong> OR <strong>Ctrl + X</strong>. All of them should work.</p>
<h2 id="heading-most-used-examples-of-searching-by-gci-command">Most Used Examples of Searching by <code>gci</code> Command</h2>
<p>Here are some handy examples of searching scripts that you can use:</p>
<p><strong>Example #1</strong>: search for all executive files on your PC (remember that you can stop this command with one of shortcuts, like <strong>Ctrl + C</strong>):</p>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Recurse -Filter "*.exe" -File
</code></pre>
<p>REMEMBER:<br>In order to paste commands into the PowerShell, you have to first enable it. <a href="https://notblackmagic.hashnode.dev/enable-copy-pasting-in-windows-powershell-cli-in-3-steps">Here's how</a>.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768406611072/f23a475e-071d-42d6-b300-f442d7f926c9.png" alt="Picture: gci command pasted into PowerShell." style="display:block;margin:0 auto" width="1108" height="645" loading="lazy">

<p>This command will show you a very long list of executable files and their folders (as shown in the image below).</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768406617447/c787e9a7-b2b8-4149-84fa-1320f94c7e48.png" alt="Picture: used gci command shows all the executable files it can find." style="display:block;margin:0 auto" width="933" height="542" loading="lazy">

<p>These lists might be so long that it's impossible to find anything in them. That's why you'll learn how to use more advanced techniques of filtering in <a href="#heading-4-advanced-searching-combining-getchildren-with-the-whereobject-command">step 4</a> to see fewer unnecessary results that don't fit your criteria.</p>
<p><strong>Example #2</strong>: search for an executable file that has <em>"notepad"</em> in its name (or search for any program you need, basically):</p>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Recurse -Filter "notepad*.exe" -File
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768406643047/06e37fba-04d8-4806-839f-19982cd011ea.png" alt="Picture: gci command showing all executable &quot;notepad&quot; files." style="display:block;margin:0 auto" width="1100" height="568" loading="lazy">

<p>One of the results will show you the location of the file you want:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768406956329/e7582c25-7c96-4b01-aa36-8660d68a4d37.png" alt="Picture: gci command showing the path to the found executable file." style="display:block;margin:0 auto" width="560" height="106" loading="lazy">

<p>In our case it's the <code>C:\Windows\System32</code> folder.</p>
<p>You can mix it however you want! Thanks to that command, you don't have to remember much about your file and it will still work.</p>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Recurse -Filter "n*pad*.*xe"
</code></pre>
<p>So what if you see some errors while scanning the whole system. Should you worry?</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768406995588/3b23deb1-1530-4fa7-8f52-427386bc37e9.png" alt="Picture: gci command showing error messages while searching for files." style="display:block;margin:0 auto" width="823" height="287" loading="lazy">

<p>It's ok! Sometimes you might get lots of errors. They will most likely occur when a script scours the system folders/files. If you want to get rid of them, add <code>-ErrorAction SilentlyContinue</code>, like you see here:</p>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Recurse -Filter "notepad*.exe" -File -ErrorAction SilentlyContinue
</code></pre>
<p>You can try it now ;)</p>
<h2 id="heading-2-setup-for-other-more-complex-examples">2. Setup for Other More Complex Examples</h2>
<p>Now, let's look at even more use cases for this command. But first, we'll create a space where I can show you examples.</p>
<p>First, create new folder inside your <em>"Documents"</em> folder. Let's call it <em>"Items"</em>.</p>
<p>Inside it, create two text documents. Name one of them <em>"Item 1- Green Bracelet"</em> and the other <em>"Item 2- Blue Bracelet"</em> (Yes, make sure you write the first letter of each word in <strong>UPPER CASE</strong>).</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768407060476/eeb559d5-2627-4674-a780-e23f4e67f5a9.png" alt="Picture: example setup of files inside &quot;Items&quot; folder inside &quot;Documents folder&quot;." style="display:block;margin:0 auto" width="1135" height="288" loading="lazy">

<p>Copy these files now.</p>
<p>Go one folder back (you can use the <strong>Ctrl + UpArrow</strong> shortcut ) and create another folder next to <em>"Items"</em> called <em>"More items"</em>:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768407053259/06d23003-aaa5-457b-8356-af10327d3436.png" alt="Picture: example setup. New &quot;More items&quot; folder created next to the &quot;Items&quot; folder." style="display:block;margin:0 auto" width="1059" height="288" loading="lazy">

<p>Paste the copied files inside the "More items" folder and change their names, so they have only <strong>lower case</strong> letters (<em>"item 1- green bracelet"</em> and <em>"item 2- blue bracelet"</em> ).</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768407154078/314b476a-8062-4d77-82f0-0ce76cf4c39b.png" alt="Picture: example setup. All files inside &quot;More items&quot; folder have names with only lowercase letters." style="display:block;margin:0 auto" width="1071" height="287" loading="lazy">

<p>PRO TIP:<br>You can click once on a file with your mouse and then type the <strong>F2</strong> key on your keyboard in order to change their names.</p>
<h3 id="heading-3-when-is-the-path-option-not-needed">3. When is the <code>-Path</code> option not needed?</h3>
<p>You don't have to specify the path every time. You can always just move to the desired directory with the <code>cd</code> (change directory) command.</p>
<p>This command will move you to your <code>Documents</code> folder:</p>
<pre><code class="language-powershell">cd ~\Documents\
</code></pre>
<p>Now, you should be able to see PowerShell pointing to your <code>Documents</code> folder on the left of the screen:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/1cd597e0-ae5e-4ea4-b066-b573a3cc2b4b.png" alt="Picture: PowerShell pointing to the Documents folder." style="display:block;margin:0 auto" width="485" height="139" loading="lazy">

<p>If you don't see this, then you can use double quotes <code>" "</code>, like in this command:</p>
<pre><code class="language-powershell">cd "~\Documents\"
</code></pre>
<p>Make sure that PowerShell is pointing to our desired folder. Now, the searching command looks like this without the <code>-Path</code> option:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*item*" -File
</code></pre>
<p>Pretty simple, right?</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768407625727/e83b7cb3-9bdc-420b-b0ae-28fa9a5ceb42.png" alt="Picture: you can first use &quot;cd&quot; command to move to the directory you want. Then you don't have to use  &quot;Path&quot; option in your &quot;gci&quot; command." style="display:block;margin:0 auto" width="593" height="311" loading="lazy">

<p>As you can see in the image above, we first moved to our desired directory, so later we could perform the search inside it without specifying the <code>-Path</code> option/parameter.</p>
<p>But the <code>-Path</code> option is very useful, either when you're creating a script or you want to search for something without moving away from the current directory:</p>
<pre><code class="language-powershell">Get-ChildItem -Path ~\Documents\ -Recurse -Filter "*item*" -File
</code></pre>
<pre><code class="language-powershell">Get-ChildItem -Path ~\Documents\ -Recurse -Filter "*item*" -Directory
</code></pre>
<p>Here's an example. I'm inside the <code>System32</code> folder and I want to know whether the thing I'm looking for is inside the <code>Documents</code> folder without moving in there:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768407360312/60915425-8432-40c3-a395-a59e1b363667.png" alt="Picture: &quot;gci&quot; command can looks for a file in a specific directory without moving us to this directory. All thanks to &quot;Path&quot; option." style="display:block;margin:0 auto" width="640" height="205" loading="lazy">

<p>And it really is there!</p>
<p>From now on, because you already know what the <code>-Path</code> option is being used for, I won't be using it unless it's necessary.</p>
<h2 id="heading-4-advanced-searching-combining-get-childitem-with-the-where-object-command">4. Advanced Searching – Combining <code>Get-ChildItem</code> with the <code>Where-Object</code> Command</h2>
<p>Sometimes you might have several folders named exactly the same, but they're in different places. You might want to exclude them based on their content, which folder they are in, or based on their<code>-Depth</code> level (see the graphic with the explanation about <code>-Depth</code> level in <a href="#heading-1-basic-explanation-of-the-get-childitem-command">step 1</a>). That's what we're going to cover in the next few points.</p>
<p>For this part of the tutorial, make sure you've gone through <a href="#heading-2-setup-for-other-more-complex-examples">step 2</a> (but you can skip step 3 if you want).</p>
<h3 id="heading-41-searching-through-only-a-particular-directory">4.1. Searching through only a particular directory</h3>
<p>Let's say that we're now looking for the bracelets that we created in <strong>step 2</strong>. But, we want to see the results from only one folder. For that, we'll use case-sensitive search (<code>-clike</code>) to get only our preferred results. But <code>-clike</code> doesn't work with <code>gci</code> alone. We need to apply another filter with the <code>Where-Object { }</code> command:</p>
<pre><code class="language-powershell">Get-ChildItem -Path ~\Documents\ -Recurse -Filter "*item*" |   
Where-Object { $_.Name -clike "*Item*" }
</code></pre>
<p>OR (clearer version, without the <code>-Path</code> option):</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*item*" |   
Where-Object { $_.Name -clike "*Item*" }
</code></pre>
<p>Let's review what's going on here:</p>
<ul>
<li><p><code>Get-ChildItem -Recurse -Filter "*item*"</code> searches for all files and folders with "item" in their name</p>
</li>
<li><p><code>|</code> – the "pipe" symbol is used to get the output of the previous command (the list of all files and folders filtered by <code>gci</code>) and send it to the next command (<code>Where-Object</code> is applying another filter to what is already filtered by <code>gci</code>).</p>
</li>
<li><p><code>Where-Object { }</code> is the command used for filtering the lists of objects. The filter is being specified inside the <code>{ }</code> curly brackets.</p>
</li>
<li><p><code>\(_</code> refers to all the separate objects. Treat it as <em>"ForEachObjectFromList".</em> And treat the whole sequence after the <code>|</code> as <em>"FindObjectsFromList that have a name with 'Item' "</em>.<br><code>\)_</code> is very often used with <code>Where-Object</code>, but also with some other commands.</p>
</li>
<li><p><code>.Name</code> – we choose a Name property to get from every object.</p>
</li>
<li><p><code>-clike</code> finds a match that is 100% correct. All letters must be the exact same case as the phrase we specified. <code>c</code> stands for "case sensitive" and it checks every letter to see if it's <strong>upper case</strong> or <strong>lower case</strong>.</p>
</li>
</ul>
<p>So, <code>Where-Object { $_.Name -clike "*Item*" }</code> is a filter that takes the <code>Name</code> parameter of every object from the list (created by <code>gci</code>) and checks with <code>-clike</code> if any <code>Name</code> has the word "Item" in it.</p>
<p>As you can see in the image below, now we'll get only the files with <strong>upper case</strong> names in our result:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768408072301/d2ffaae0-f688-40c6-9c1e-4ddd37159146.png" alt="command looking for file in specific directory case-sensitive" style="display:block;margin:0 auto" width="697" height="391" loading="lazy">

<p>IMPORTANT:<br><code>-like</code> alone means that we're looking for a certain pattern, no matter what case the letters are. The <code>c</code> in <code>-clike</code> means that we look for the thing with exactly the same capitalization of the letters (both upper and lower case, hence the <em>"c"</em>).</p>
<p>If you want to see the files <strong>without the upper case</strong> first letter, you can do that by changing "*Item*" from our current command to "*item*":</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*item*" |   
Where-Object { $_.Name -clike "*item*" }
</code></pre>
<p>Let's try it out!</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768407562860/84b82488-8ee5-4b23-ac8f-831d9c586c41.png" alt="Picture: command looking for files with only lowercase letters in their names" style="display:block;margin:0 auto" width="747" height="321" loading="lazy">

<h3 id="heading-42-how-to-search-while-excluding-a-particular-directory">4.2. How to search while excluding a particular directory</h3>
<p>In <strong>step 4.1</strong> we learned how to search only for files/folders with specific case-sensitive names in them. After applying only two changes to our previous code, we can exclude certain directories from our search.</p>
<p>Here's our starting command once again:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*item*" |   
Where-Object { $_.Name -clike "*Item*" }
</code></pre>
<h4 id="heading-change-1">Change #1</h4>
<p>In the example above, <code>-clike</code> shows only files/folders <strong>including</strong> specific phrase in their names. If we change it to <code>-cnotlike</code>, we'll <strong>exclude</strong> from the search all files/folders with that specific phrase in their name.</p>
<p>Now our code looks like this:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*item*" |   
Where-Object { $_.Name -cnotlike "*Item*" }
</code></pre>
<h4 id="heading-change-2">Change #2</h4>
<p>After the first change, <code>Where-Object { \(_.Name -cnotlike "*Item*" }</code> only excludes the names, not full paths. In order to avoid that, we need to exclude an actual path to these files. We can do that by changing <code>\)_.Name</code> to <code>$_.FullName</code>, which checks for a certain phrase in the whole path to the file <strong>and</strong> in the file's name.</p>
<p>Now, your command should look like this:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*item*" |   
Where-Object { $_.FullName -cnotlike "*Item*" }
</code></pre>
<p>We excluded the "Items" folder from our search. You should now be able to see the files only from the "More items" directory. Try it out yourself!</p>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/547801a8-a825-4607-9dbe-42e3c4af238e.png" alt="Picture: excluding part of path with FullName -cnotlike." style="display:block;margin:0 auto" width="1054" height="371" loading="lazy">

<p>What if you want to exclude the "More items" directory instead? Just change the phrase inside the filter to something like this:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*green*" -File |   
Where-Object { $_.FullName -cnotlike "*More*" }
</code></pre>
<p>We also changed the name of the file from "*item*" to "*green*" in our <code>gci</code> search (first line of code). That's why now we'll see only one bracelet in our result list:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768408276671/f4043a3f-ba14-4ff4-be60-ad5c3e321003.png" alt="command looking for file with exclusion case-sensitive" style="display:block;margin:0 auto" width="1000" height="182" loading="lazy">

<p>The <code>gci</code> command has two filters applied. First, it searches for files with phrase "green" in their names. The second filter is the "Where-Object" command, which <strong>excludes</strong> anything that has the word "More" in its path. In our case, the "More items" folder got excluded.</p>
<p>We don't even need the case-sensitive filter in our case. The command will work the same when we <strong>exclude</strong> just a <strong>lowercase</strong> word "more". So let's change <code>-cnotlike "*More*"</code> to <code>-notlike "*more*"</code> and see if it's true:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*green*" -File |   
Where-Object { $_.FullName -notlike "*more*" }
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768408250133/f6dac0bd-fc74-4c24-8b4b-9ba780683956.png" alt="Picture: case-sensitive search working the same in current example as a not case-sensitive search." style="display:block;margin:0 auto" width="836" height="188" loading="lazy">

<p>As you can see, the result is the same! Despite different cases of the letters, we still got the right <strong>keyword</strong>. So, case-sensitive search isn't always needed&nbsp;– only when you want to be very specific.</p>
<p>Sometimes, being too specific might be bad and make your code not work as intended. To see what I mean, let's look at the example below. Let's apply case-sensitive search once again, but to our unchanged, lowercase keyword "more" and see if it still works:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*green*" -File |   
Where-Object { $_.FullName -cnotlike "*more*" }
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768408273177/ee37dcf7-8720-44b7-aa04-1c09e1cfbe52.png" alt="Picture: case-sensitive search doesn't filter out anything now, because it's too specific. &quot;More items&quot; folder omits the filter now." style="display:block;margin:0 auto" width="1011" height="271" loading="lazy">

<p>Case-sensitive search doesn't filter out anything now, because it's too specific. Both the "Items" and "More items" folders omit the filter now.</p>
<h4 id="heading-faq">FAQ:</h4>
<p>If the <code>Where-Object</code> command is what actually filters the output for us, shouldn't we drop (delete) the <code>-Filter</code> option from <code>gci</code>?</p>
<p>No, we should still use the <code>-Filter</code> option, because it already separates around 99% of the possible files, so the <code>Where-Object</code> command has to work roughly only on 1% of the objects. It makes this part of the command AT LEAST 100 times faster (more often 100,000 times or even faster).</p>
<p>You can try using this command in <code>-Path C:/</code> with and without the <code>-Filter</code> option. In my case, using the <code>-Filter</code> shortened the time needed for the whole sequence of commands to finish from 16 seconds to 8 seconds (first 7.99 seconds is used by <code>gci</code>, so that's why the time got shortened only by a half). That's what we call ✨<em>optimization</em>✨ :D</p>
<h3 id="heading-43-searching-only-1-directory-from-many-with-exactly-the-same-name">4.3 Searching only 1 directory from many with exactly the same name</h3>
<p>We've learned how to search for a phrase anywhere inside the path of a file. But what if we want to search inside exactly the "More items" folder? For that, we'll use the <code>-match</code> filter (which works similarly to the <code>-like</code> filter).</p>
<p>Our phrase will also use "\", instead of "\". This is because "\" is the symbol for a folder, but alone in programming it also has some other features, which we don't want.</p>
<p>This command will look for a match for the "More items" folder in the path of every file from the list. Then, it will show you this file if it matches.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/028f1187-e371-4496-8152-df778337a465.png" alt="Picture: &quot;gci&quot; with a filter for an exact folder." style="display:block;margin:0 auto" width="908" height="155" loading="lazy">

<p>What if we want to check for two folders, one next to the other, simultaneously? Very easy! Just connect them with the sign for a folder "\". Here, the command will search inside the "More items" folder only if it's inside the "Documents" folder:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/6faef3fa-e2a8-46ff-bd5c-7c9cc27c1ab8.png" alt="Picture: searching for &quot;DocumentsMore*&quot;" style="display:block;margin:0 auto" width="908" height="140" loading="lazy">

<p>As you can see, we didn't use "More items", only "More". You can shorten that filter how you want. It will still be applied to the whole path. See the example below:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*green*" -File |
Where-Object { $_.FullName -match "s\\Mo*" }
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/817fb9cb-ddd3-4d9f-8ff5-2dbbc3eb7d0d.png" alt="Picture: filter works, even if it could be more specific" style="display:block;margin:0 auto" width="908" height="140" loading="lazy">

<p>Earlier, we used the <code>not</code> statement in <code>-like</code> filter to exclude certain files and directories. The same can be done with <code>-notmatch</code>:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*green*" -File | 
Where-Object { $_.FullName -notmatch "ents\\Ite*" }
</code></pre>
<p>Be aware that we're now excluding the "Items" folder from the search, not "More items".</p>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/acf4d31f-9c8f-4092-b0bd-ed577ca982cf.png" alt="Picture: excluding &quot;Documentstems&quot; folders from search by using &quot;notmatch&quot; filter" style="display:block;margin:0 auto" width="908" height="140" loading="lazy">

<p>And, with <code>-cmatch</code> we can apply the same case-sensitive filter as with <code>-clike</code>:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*green*" -File | 
Where-Object { $_.FullName -cmatch "green*" }
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/7db7d3bb-9a9e-4881-b0e9-4a119c3f93d8.png" alt="7db7d3bb-9a9e-4881-b0e9-4a119c3f93d8" style="display:block;margin:0 auto" width="908" height="140" loading="lazy">

<p>I hope you get the gist of it now.</p>
<h3 id="heading-44-filter-how-deep-how-many-folders-in-you-want-to-search-for-the-file">4.4 Filter how deep (how many folders in) you want to search for the file</h3>
<p>Sometimes you might have a very long path to some of your files. If you don't want to waste time searching every folder on your computer recursively, you can use <code>-Depth</code> option. It specifies how many folders to search inside your folder tree. I already showed you the picture of a folder tree in the beginning of this article, but you should take a look at it here once again.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/7a433d89-cfe5-4bf6-8f6d-b7b812414a93.png" alt="Picture: a visual explanation of -depth and -recurse parameters. It shows &quot;Documents&quot; folder at the bottom, which is tagged both as Parent and Depth 0. It points upwords to its child folders and a child file. Those are tagged as Depth 0 children of our Documents folder. They are simultaneously tagged as Depth 1 parents, so files and folders. to which they are pointing upwards, are their Depth 1 children." style="display:block;margin:0 auto" width="821" height="656" loading="lazy">

<p>So, how does the <code>-Depth</code> parameter work?</p>
<p><code>-Depth 0</code> means that our command will search only the current folder. It will show results of all children of Depth level 0. Those results are:<br>1 "child file" and 2 "child folders".</p>
<p><code>-Depth 1</code> searches the current folder and its child-folders. It will show the results of all children of Depth level 1. Those results are:<br>1 "child file", 2 "child folders", 2 "grandchild files" and 1 "grandchild folder".</p>
<p><code>-Depth 2</code> searches the current folder and its child and grandchild folders. It will show results of all children of Depth level 2. Those results are:<br>1 "child file", 2 "child folders", 2 "grandchild files", 1 "grandchild folder" and 1 "great grandchild file".</p>
<p>Let's see the difference between these two commands:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*item*" -Depth 0
</code></pre>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*item*" -Depth 1
</code></pre>
<p>The first command will show you only the files and folders inside our current directory.<br>The second command will also search for them inside every folder found inside the current folder.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768408448152/2d875580-e463-4903-b99f-d3b457f5eb5a.png" alt="depth parameter explanation" style="display:block;margin:0 auto" width="612" height="583" loading="lazy">

<p>For the sake of practice, let's combine it with <code>Where-Object</code> to find the green bracelet:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*item*" -Depth 1 | Where-Object { $_.name -clike"*green*" }
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768408504470/4af154e6-9d24-43e4-b228-770da7de9151.png" alt="Picture: gci looking for file with set depth" style="display:block;margin:0 auto" width="812" height="162" loading="lazy">

<p>I hope that this example showed you how easy it is to use multiple options ( <code>-Depth</code>, <code>-Recurse</code>) and filters (<code>-Filter</code>, <code>Where-Object</code>).</p>
<h2 id="heading-5-how-to-search-through-hidden-files">5. How to Search Through Hidden Files</h2>
<p>Some files are not that easily accessible to the user. You can see some of the hidden files and folders in Windows Explorer (<a href="https://notblackmagic.hashnode.dev/how-to-see-hidden-files-and-folders-in-windows-file-explorer">here's how</a>). But sometimes it's easier to find what you need if you see <strong>only</strong> those hidden files. That's possible with PowerShell.</p>
<p>The options we're going to use for that are:</p>
<ul>
<li><p><code>-Force</code>: show files otherwise not accessible by the user, such as hidden files.</p>
</li>
<li><p><code>-Hidden</code>: show <strong>only</strong> those hidden files and directories.</p>
</li>
</ul>
<p>This example will search for hidden files in our user's folder:</p>
<pre><code class="language-powershell">gci -Path ~\ -Force -Hidden
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768408531210/22319e7b-f9f1-40cb-8037-7abcb9225c3b.png" alt="Picture: gci with -Forece and -Hidden parameters showing hidden files and folders" style="display:block;margin:0 auto" width="1315" height="553" loading="lazy">

<p>Everything here is usually invisible to the typical user. But not for you now :D</p>
<p>The interesting thing is that there are more files not available to the user than the available ones. If you're brave enough, you can see them yourself (Remember! <strong>Ctrl + C</strong> stops the command!):</p>
<pre><code class="language-powershell">gci -Path ~\ -Force -Hidden -Recurse
</code></pre>
<h2 id="heading-6-how-can-you-know-all-the-properties-that-you-can-use-as-a-filter">6. How can you know all the properties that you can use as a filter?</h2>
<p>Up until now, we'vce used some common properties, like <code>Name</code> and <code>Fullname</code>. But there are many others that you might want to access, like <code>CreationTime</code> (date of creating the file) or <code>LastWriteTime</code> (date of last edit of the file).</p>
<p>In this section, I'll first show you how to see all the possible properties. After that, you'll learn how to retrieve only the property you want for scripting purposes.</p>
<p>Go through <strong>step 2</strong> above if you haven't already, because we're going to use the same files that we created before.</p>
<p>Move to the <code>Documents</code> folder in PowerShell.</p>
<p>I hope that this script looks familiar to you now. It searches for files with "item" in their names and checks if these names contain the word "green" (all lowercase letters):</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*item*" | 
Where-Object { $_.Name -clike "*green*" }
</code></pre>
<p>We know that only one file should appear (if you don't trust me, just see for yourself). So, we're going to see every possible property we can use by appending (adding at the end) this fragment of code:<br><code>| Select-Object -Property *</code></p>
<p><code>Select-Object</code> (alias: <code>select</code>) is used for selecting different types of properties. By using an option <code>-Property</code> we tell it to show both values and names of all the properties.</p>
<p>For example:</p>
<p>Name of property: <code>FullName</code><br>Value of property: <code>~\Documents\More items\item 1- green bracelet.txt</code></p>
<p>The asterisk <code>*</code> at the end tells this command to show these names and values for every property possible.</p>
<p>The final version of this command looks like this:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*item*" | 
Where-Object { $_.Name -clike "*green*" } | 
Select-Object -Property *
</code></pre>
<p>Try finding the <code>FullName</code> property in there :D</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768408562185/710a4835-a415-4500-af4f-6371a83ae3b6.png" alt="getting all command options or properties" style="display:block;margin:0 auto" width="962" height="830" loading="lazy">

<p>This command showed us all possible properties that we can use for that 1 file that it found. If there were more files fitting the filter, then every single one of them would have a similar list of properties. But for different types of files you will get different results.</p>
<h3 id="heading-how-to-retrieve-only-1-desired-property">How to retrieve only 1 desired property</h3>
<p>You've already learned how to check for all possible properties. So, how do we use any of them? Just put one of them instead an asterisk <code>*</code> at the end of the command, like we put <code>CreationTime</code> in here:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*green*" -File |
Where-Object { $_.Name -clike "*green*" } | 
Select-Object -Property CreationTime
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/060de2e6-909f-4679-987c-e715fb7ee19b.png" alt="Picture: Select-Object shows only the CreationTime property" style="display:block;margin:0 auto" width="1120" height="182" loading="lazy">

<p>You can use any other property for the sake of this exercise, like <code>LastWriteTime</code>:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*green*" -File |
Where-Object { $_.Name -clike "*green*" } | 
Select-Object -Property LastWriteTime
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/8223f738-9467-4961-8518-82e010a6e425.png" alt="Picture: Select-Object shows only the LastWriteTime property" style="display:block;margin:0 auto" width="1112" height="184" loading="lazy">

<p>What if you want to retrieve only the value of the property without its name (because you already know its name and it also messes up your script)? You can retrieve just the value, by changing the <code>-Property</code> to <code>-ExpandProperty</code>:</p>
<pre><code class="language-powershell">Get-ChildItem -Recurse -Filter "*green*" -File |
Where-Object { $_.Name -clike "*green*" } | 
Select-Object -ExpandProperty LastWriteTime
</code></pre>
<p>See the result:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/7f08ecfd-4a19-4276-9d2a-eac53d5cdb93.png" alt="Picture: Changing -Property to -ExpandProperty makes the script to show only the value of the property without its name. the" style="display:block;margin:0 auto" width="1114" height="170" loading="lazy">

<h2 id="heading-7-i-dont-know-the-files-name-but-i-know-whats-inside-it-how-do-i-find-the-file-by-its-content">7. I don't know the file’s name, but I know what's inside it. How do I find the file by its content?</h2>
<p>Sometimes it's easier to find a file by searching it by its content. Or perhaps you have lots of similar files and you'd like to check them quickly without opening and closing them. I'll show you some techniques that will let you achieve that in no time.</p>
<p>This command will search every file on your system for the specified word or phrase (in our case, the phrase is "match"):</p>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Recurse -File | 
Select-String -Pattern 'match' -List
</code></pre>
<p>Here's what's happening:</p>
<ul>
<li><p><code>Get-ChildItem -Path C:\ -Recurse -File</code>: as you already know, this part searches for every file on your computer.</p>
</li>
<li><p><code>|</code> – passes the list of files to the next command. So, the next command will search for a certain phrase only in the files listed by <code>gci</code>.</p>
</li>
<li><p><code>Select-String</code> – "String" is a common word in programming used to describe a word/phrase/some text. So, we select the phrase that we want to search for. That phrase is specified by the <code>-Pattern</code> parameter (in our case it's "match").</p>
</li>
<li><p><code>-List</code> tells the command to show only the first found match in every file (great if you want to just see the list of all found files).</p>
</li>
</ul>
<p>Here's an example output of our command:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/cb91d732-c15a-4b10-a127-1a78af2efe63.png" alt="Picture: Select-String showing path to the file and the place in the file where the pattern was found." style="display:block;margin:0 auto" width="1119" height="127" loading="lazy">

<p>Of course, you have quite a lot of files, and some images may also appear in your search (like .svg files that are basically text files that tell the system how to draw an icon). So, it's always best to specify what type of file you're searching for. Let's look for the phrase "red" inside .svg files:</p>
<pre><code class="language-powershell">Get-ChildItem -Filter "*.svg" -Recurse | 
Select-String -Pattern 'red' -List
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/c1d33d66-014b-49af-9e8d-508a581d51fa.png" alt="Picture: gci looking for text inside svg graphic files." style="display:block;margin:0 auto" width="1300" height="250" loading="lazy">

<p>On the other hand, some text documents will never appear in your search (for example .doc and .docx documents are encoded in such a way that they're impossible to decode without Word).</p>
<p>But in regular text files, you can search for phrases with an emphasis on big and small letters with the <code>-CaseSensitive</code> option. Here, we're going to search for the phrase "github" with only lowercase letters:</p>
<pre><code class="language-powershell">Get-ChildItem -Filter "*.txt" -Recurse | 
Select-String -Pattern 'github' -List -CaseSensitive
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/affa057b-3fc4-402d-9f93-4a1b93bcb98f.png" alt="affa057b-3fc4-402d-9f93-4a1b93bcb98f" style="display:block;margin:0 auto" width="1116" height="325" loading="lazy">

<p>Other options that you'll often use with the <code>Select-String</code> command are:</p>
<ul>
<li><code>Select-String -AllMatch</code> will show you all matches found in every searched file (instead of only 1 match found per file, like with <code>-List</code>).<br><code>Select-String -Context 3</code> shows the three lines of text before and after the line in which the match is found.<br><code>Select-String -Raw</code> won't show you the paths, just the content of the files. This is great for automation and scripts. It's often combined with the <code>-Context</code> option.</li>
</ul>
<p>Let's see some of these options in action:</p>
<pre><code class="language-powershell">Get-ChildItem -Filter "*.txt" -Recurse | 
Select-String -Pattern 'github' -AllMatch -Context 3
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771434300827/4a61d396-755c-49e8-a1f5-f0fd24347b5c.png" alt="looking for file based on its content" style="display:block;margin:0 auto" width="1920" height="350" loading="lazy">

<p>Thanks to the <code>-Context</code> parameter, you can see a total of seven lines (three lines before and three lines after the match) in this file, one after another. This makes it easier to differentiate it from all the other matches found by <code>-AllMatch</code> that might be put in a very similar context.</p>
<p>If you ever feel like there's too much clutter on your screen, you can combine <code>Select-String</code> with <code>Select-Object</code> to get only the paths of the files with matched phrases.</p>
<p>The command below will search every .txt file on your computer for the phrase specified:</p>
<pre><code class="language-powershell">Get-ChildItem -Filter "*.txt" -Recurse | 
Select-String -Pattern 'github' -List
</code></pre>
<p>Let's add the <code>Select-Object -Property Path</code> filter at the end. Now, the command will only show the paths, so there's less clutter on your screen:</p>
<pre><code class="language-powershell">Get-ChildItem -Filter "*.txt" -Recurse | 
Select-String -Pattern 'github' -List | 
Select-Object -Property Path
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/71b9b173-3dd4-40a1-911f-0d494c6a44bb.png" alt="Picture: adding Select-Object makes the results more readable and easier to understand." style="display:block;margin:0 auto" width="1051" height="585" loading="lazy">

<p>Some of the paths are not fully visible. We'll fix that in the next step.</p>
<h2 id="heading-8-i-cant-see-the-full-path-how-do-i-fix-this">8. I can't see the full path - how do I fix this?</h2>
<p>Let's format the results with the <code>Format-Table -Wrap -AutoSize</code> command. <code>-Autosize</code> allows the result to take the whole available space. <code>-Wrap</code> allows wrapping (continuing the text in the next line when it doesn't fit in the space available), which creates more space if it's needed.</p>
<p>Here's an example:</p>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Filter "*.txt" -Recurse | 
Select-String -Pattern 'github' -List | 
Select -Property Path | 
Format-Table -Wrap -AutoSize
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69289f73296ac6cbc0e5620b/bcea9d57-e966-4c41-8d24-bfb3acf95281.png" alt="bcea9d57-e966-4c41-8d24-bfb3acf95281" style="display:block;margin:0 auto" width="1057" height="426" loading="lazy">

<p>Now, you can see the whole paths (or any other results you need) even in PowerShell!</p>
<h2 id="heading-9-hard-to-read-open-the-results-in-the-text-editor-of-your-choice">9. Hard to read? Open the results in the text editor of your choice</h2>
<p>You can send the results of any script/command in two ways:</p>
<p><code>&gt; ~\Documents\command_output.txt</code><br>AND<br><code>| Out-File ~\Documents\command_output.txt</code></p>
<p>Both of these will create a file inside your <code>Documents</code> folder, which you can later open in any program of your choice and edit.</p>
<p>Just add whichever solution you prefer to the end of your command, like here:</p>
<pre><code class="language-powershell">Get-ChildItem -Filter "*.txt" -Recurse | 
Select-String -Pattern 'match' -List | 
Select -Property Path | 
Out-File ~\Documents\command_output.txt
</code></pre>
<p>In the image below, first you'll see the same command, but without exporting the results to another file. The second command, at the bottom of the image, will export the results to the other file without showing them in PowerShell:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771431975160/41f0445e-1a0c-41f6-83b5-446f21e9bea9.png" alt="Picture: gci looking for file based on its content, but showing only paths to the files with found matches." style="display:block;margin:0 auto" width="1920" height="650" loading="lazy">

<p>You'll see the results from second command after opening the file in any text editor:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771432401191/851770d9-6596-4081-b537-45bf8373ac44.png" alt="Picture: command results are possible to open in any text editor." style="display:block;margin:0 auto" width="1920" height="650" loading="lazy">

<p>But, what if you can't see the full path even in your text editor?</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771432650086/f21b1bf6-6aad-4799-a34b-c5889b8f8ee7.png" alt="Picture: command results don't show all information you need. They sometimes stop showing, if it's more then default settings allow for." style="display:block;margin:0 auto" width="700" height="650" loading="lazy">

<p>To address this, you can add <code>| Format-Table -Wrap -AutoSize</code> right before sending the results to the file:</p>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Filter "*.txt" -Recurse | 
Select-String -Pattern 'match' -List | 
Select -Property Path | 
Format-Table -Wrap -AutoSize |
Out-File ~\Documents\command_output.txt
</code></pre>
<p>And open the file to see the whole path!</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771434628016/b8bebea1-f6db-4d12-a0f5-70ca18492b9b.png" alt="Picture: bug fixed. Now, you can see all the information." style="display:block;margin:0 auto" width="1920" height="350" loading="lazy">

<p>Just remember that you have to copy each line one by one. Where you see the arrows in the screenshot above is a "newline" character, which you have to delete. Only after doing that can you copy the whole path and paste it into Windows Explorer or into some script.</p>
<h2 id="heading-10-summary-the-ultimate-commands-for-searching-and-finding-whatever-you-need">10. Summary: the Ultimate Commands for Searching and Finding Whatever You Need</h2>
<p><a href="https://github.com/NotBlackMagician/NBM-cheat-sheets/blob/main/windows_powershell/NBM_cheat_sheet_Get-ChildItem_find_any_file_like_on_linux.txt">Here</a> you can download a free cheat sheet with explanations of the commands and examples in one place.</p>
<h3 id="heading-most-used-commands">Most used commands:</h3>
<ul>
<li>Case-sensitive search:</li>
</ul>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Recurse -Filter "*whatYouNeed*" |   
Where-Object { $_.Name -clike "*whatYouNeed*" } |   
Select-Object { $_.FullName } |
Format-Table -Wrap -AutoSize
</code></pre>
<ul>
<li>Alternatively, send the result to a file:</li>
</ul>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Recurse -Filter "*whatYouNeed*" |   
Where-Object { $_.Name -clike "*whatYouNeed*" } |   
Select-Object { $_.FullName } |
Format-Table -Wrap -AutoSize |
Out-File ~\Documents\command_output.txt
</code></pre>
<ul>
<li>Search by file's content:</li>
</ul>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Recurse | 
Select-String -Pattern 'what you remember' -AllMatch -Context 2 |
Format-Table -Wrap -AutoSize
</code></pre>
<ul>
<li>Alternatively, send the result to the file:</li>
</ul>
<pre><code class="language-powershell">Get-ChildItem -Path C:\ -Recurse | 
Select-String -Pattern 'what you remember' -CaseSensitive -AllMatch -Context 2 |
Format-Table -Wrap -AutoSize |
Out-File ~\Documents\command_output.txt
</code></pre>
<p>These commands should work for anything you want to find. I hope you understand now how they function after reading through this tutorial ;)</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>If you want to learn more about these commands, I show you how to work with them in depth in my tutorial <a href="https://notblackmagic.hashnode.dev/learn-windows-powershell-commands-like-a-linux-user">“Learn PowerShell commands like a Linux user”</a>.</p>
<p>If what you found here helped you in any way, consider following me on my social media in order to help me reach further audience: <a href="https://social.linux.pizza/@SecretDevil">Mastodon</a>, <a href="https://www.linkedin.com/in/piotr-opoka-4320143a5/">LinkedIn</a>.</p>
<p>You can also rate me on <a href="https://github.com/NotBlackMagician">Github</a> and support me on <a href="https://ko-fi.com/piotropoka">Ko-fi!</a></p>
<p>Thank you for any support you're able to give. Have a great day!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Elevate Your Database Game: Supercharging Query Performance with Postgres FDW ]]>
                </title>
                <description>
                    <![CDATA[ Foreign data wrappers (FDWs) make remote Postgres tables feel local. That convenience is exactly why FDW performance surprises are so common. A query that looks like a normal join can execute like a distributed system: rows move across the network, r... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/fdw-pushdown/</link>
                <guid isPermaLink="false">69963f00d35b661838993bd0</guid>
                
                    <category>
                        <![CDATA[ performance ]]>
                    </category>
                
                    <category>
                        <![CDATA[ PostgreSQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Databases ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Hamdaan Ali ]]>
                </dc:creator>
                <pubDate>Wed, 18 Feb 2026 22:36:48 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1771357398917/8db8c3fd-9f16-4631-aa48-2537e8a4cb45.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Foreign data wrappers (FDWs) make remote Postgres tables feel local. That convenience is exactly why FDW performance surprises are so common.</p>
<p>A query that looks like a normal join can execute like a distributed system: rows move across the network, remote statements get executed repeatedly, and the local planner quietly becomes a coordinator. In that world, “fast SQL” is not mainly about CPU or indexes. It’s about <strong>data movement</strong> and <strong>round-trips</strong>.</p>
<p>This handbook covers the mechanism that determines whether a federated query behaves like a clean remote query or a chatty distributed workflow: <strong>pushdown</strong>.</p>
<p>Pushdown is not “moving compute”. Pushdown determines whether filtering, joining, ordering, and aggregation occur at the data source or after the data has already crossed the wire. When pushdown works, the local server receives a reduced result set. When it doesn’t, Postgres often has to fetch broad intermediate sets and finish the work locally.</p>
<p>The chapters ahead will help you build a practical mental model of what is “shippable” in <code>postgres_fdw</code>, why some expressions are blocked, and how to read <code>EXPLAIN (ANALYZE, BUFFERS, VERBOSE)</code> without getting tricked by familiar plan shapes.</p>
<p>After the core method, the handbook covers tuning knobs that matter in production, schema and indexing considerations, benchmarking methodology, monitoring and logging, and a case study that shows what a real pushdown win looks like end-to-end.</p>
<p>The later sections go deeper into advanced shippability edge cases, cost model calibration, and regression-proofing FDW workloads.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-executive-summary">Executive Summary</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-motivation">Motivation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-fdw-basics-without-the-setup-tax">FDW Basics Without the Setup Tax</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-pushdown-mechanics">Pushdown Mechanics</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-shippable-operations-a-deep-dive">Shippable Operations: a Deep Dive</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-pushdown-blockers-and-why-they-exist">Pushdown Blockers and Why They Exist</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-reading-explain-like-a-pro">Reading EXPLAIN Like a Pro</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-tune-postgresfdw">How to Tune postgres_fdw</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-schema-and-index-recommendations">Schema and Index Recommendations</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-benchmarking-methodology">Benchmarking Methodology</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-monitoring-and-logging">Monitoring and Logging</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-case-study-refactoring-a-keycloak-coverage-query">Case Study: Refactoring a Keycloak Coverage Query</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-checklist-and-troubleshooting-guide">Checklist and Troubleshooting Guide</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-case-study-takeaways">Case Study Takeaways</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-advanced-operations-a-deeper-dive-into-shippability">Advanced Operations: A Deeper Dive into Shippability</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-common-antipatterns-and-how-to-avoid-them">Common Anti‑Patterns and How to Avoid Them</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-extending-tuning-calibrating-cost-models">Extending Tuning: Calibrating Cost Models</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-further-case-studies-and-practical-examples">Further Case Studies and Practical Examples</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-monitoring-diagnostics-and-regression-testing">Monitoring, Diagnostics, and Regression Testing</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-extended-guidelines-for-advanced-dbas">Extended Guidelines for Advanced DBAs</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-bringing-it-all-together">Bringing it All Together</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-references">References</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>This handbook assumes basic comfort with Postgres query plans. It builds on <code>EXPLAIN (ANALYZE, BUFFERS)</code> rather than reintroducing SQL fundamentals, indexing, or join algorithms.</p>
<p>The focus here is federated execution: how foreign queries behave, and how to reason about them with the same clarity as local plans.</p>
<p>Here’s what you should already be comfortable with:</p>
<ul>
<li><p>Reading <code>EXPLAIN (ANALYZE, BUFFERS)</code> output and spotting obvious plan smells (row explosions, bad join order, missed indexes).</p>
</li>
<li><p>Basic join mechanics (nested loop, hash join, merge join) and why cardinality estimates matter.</p>
</li>
<li><p>Postgres statistics at a practical level (<code>ANALYZE</code>, correlation, and what “estimated rows vs actual rows” implies).</p>
</li>
</ul>
<p>And here’s what you need to follow along with the examples:</p>
<ul>
<li><p>A Postgres “local” instance that will run <code>postgres_fdw</code> and act as the coordinator.</p>
</li>
<li><p>A Postgres “remote” instance that holds the foreign tables.</p>
</li>
<li><p>Permission on the local side to:</p>
<ul>
<li><p><code>CREATE EXTENSION postgres_fdw;</code></p>
</li>
<li><p>create a <code>SERVER</code> and <code>USER MAPPING</code></p>
</li>
<li><p>create <code>FOREIGN TABLE</code> objects (or permission to use existing ones)</p>
</li>
</ul>
</li>
<li><p>A way to run queries and capture plans:</p>
<ul>
<li><code>psql</code> is enough, and so is any GUI, as long as you can run <code>EXPLAIN (ANALYZE, BUFFERS, VERBOSE)</code>.</li>
</ul>
</li>
</ul>
<p>We won’t go through a long environment setup walkthrough. The examples assume the FDW objects exist and focus on plans and behavior.</p>
<p>We also won’t go into general distributed systems theory. Only the pieces that show up in an FDW plan are used.</p>
<h2 id="heading-executive-summary">Executive Summary</h2>
<p>The single most important lesson of this handbook is that <strong>FDW pushdown reduces data movement</strong>. It’s tempting to think of pushdown as merely changing where a calculation happens (“move the work to the remote”). But what really matters is whether the remote server is asked for only the rows you need.</p>
<p>When pushdown is working, the remote server performs the selective join and filtering, and the local Postgres receives a small, already reduced result set. When pushdown fails, the local server becomes a distributed query coordinator: it pulls large intermediate sets over the network and then finishes the heavy lifting locally.</p>
<p>Why does this matter? Because a refactor that makes more of your query shippable to the remote server can slash end‑to‑end latency without changing a single row of output. In the case study we'll explore later, rewriting a query so that the FDW can ship a joined remote query instead of performing multiple foreign scans and local joins reduces runtime from approximately <strong>166 ms to 25 ms</strong>. The business logic did not change – the <em>shape</em> of the work changed.</p>
<p>Below is a simple bar chart illustrating that dramatic drop. The chart uses actual timings from the case study. If you run the experiment yourself, the numbers may differ depending on your hardware and network, but the relative difference should be clear.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771117284661/ecadfc8b-7e45-4122-921d-5b06215d627a.png" alt="Bar chart titled &quot;Query Execution Time: Before vs After Refactor.&quot; The chart shows execution time in milliseconds on the vertical axis. The &quot;Before&quot; bar is much taller, over 160 ms, compared to the &quot;After&quot; bar, which is below 20 ms, indicating a significant improvement in execution time after refactoring." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-motivation">Motivation</h2>
<p>Foreign data wrappers let you query remote data using the same SQL syntax you use locally. That convenience is exactly why they can be so deceptive.</p>
<p>A federated query may look like a normal join, but under the hood, it behaves like a distributed system: some part of the plan runs on the remote server, some on the local server, and every boundary between them is a network hop. The slow path is rarely “bad SQL” – it’s usually a combination of two things:</p>
<ol>
<li><p><strong>Too many rows are pulled over the network.</strong> Without pushdown, the FDW retrieves a large slice of the remote table and applies your filters and joins locally. This may lead to tens of thousands or millions of rows being shipped across the network when you only needed hundreds or fewer.</p>
</li>
<li><p><strong>Too many round-trips.</strong> If the plan performs a nested loop that drives a foreign scan, it can end up executing the same remote query hundreds or thousands of times. Each call might be fast on its own, but latency adds up.</p>
</li>
</ol>
<p>This isn't speculation. PostgreSQL's documentation makes clear that a foreign table <strong>has no local storage</strong> and that Postgres “asks the FDW to fetch data from the external source” <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=It%20is%20generally%20recommended%20that,differently%20from%20the%20local%20server">[1]</a>. There is no local buffer cache or heap storage to hide mistakes. Every row you retrieve must traverse the network at least once. If your plan fetches more rows than it needs, or repeatedly does so, performance can degrade quickly.</p>
<p>That’s why you should treat the Remote SQL shown in <code>EXPLAIN (VERBOSE)</code> as part of your query plan. It tells you exactly what the remote server is being asked to do. If it’s missing your filters or joins, you know the local server will have to finish the job. The rest of this handbook will teach you how to read that plan, how to force pushdown when possible, and how to recognize the signs that something has gone wrong.</p>
<h2 id="heading-fdw-basics-without-the-setup-tax">FDW Basics Without the Setup Tax</h2>
<p>You might be tempted to skip this section if you've already created foreign tables in your own databases. Don't. Understanding the architecture of foreign data wrappers is essential to understanding why pushdown matters.</p>
<h3 id="heading-sqlmed-in-a-nutshell">SQL/MED in a nutshell</h3>
<p>PostgreSQL implements the <strong>SQL/MED</strong> (Management of External Data) standard through its FDW framework. To access a remote Postgres server via <code>postgres_fdw</code>, you perform four steps:</p>
<ol>
<li><p><strong>Install the extension</strong>: <code>CREATE EXTENSION postgres_fdw</code> tells Postgres to load the FDW code.</p>
</li>
<li><p><strong>Create a foreign server</strong>: <code>CREATE SERVER foreign_server FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '...', port '...', dbname '...')</code>defines where the remote server resides and how to connect.</p>
</li>
<li><p><strong>Create a user mapping</strong>: <code>CREATE USER MAPPING FOR your_user SERVER foreign_server OPTIONS (user 'remote_user', password '...')</code> tells Postgres how to authenticate on the remote side.</p>
</li>
<li><p><strong>Create a foreign table</strong>: <code>CREATE FOREIGN TABLE remote_table (...) SERVER foreign_server OPTIONS (schema_name '...', table_name '...');</code> defines the columns and references the remote table.</p>
</li>
</ol>
<p>Once you've done that, you can run <code>SELECT</code> statements against the foreign table as if it were local. But the definition hides an important detail: there is no storage associated with that foreign table <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=It%20is%20generally%20recommended%20that,differently%20from%20the%20local%20server">[1]</a>. Every time you <code>SELECT</code>, <code>INSERT</code>, <code>UPDATE</code>, or <code>DELETE</code>, the FDW must connect to the remote server, build a remote query, send it, and read the results. This overhead is small for simple queries but becomes critical as queries get more complex.</p>
<h3 id="heading-what-postgresfdw-does-and-does-not-do">What postgres_fdw does and does not do</h3>
<p><code>postgres_fdw</code> does two things for you:</p>
<ol>
<li><p>It builds remote SQL from your query, including pushing down safe filters, joins, sorts, and aggregates when it can.</p>
</li>
<li><p>It fetches rows from the remote server and hands them to the local executor. If some part of your query cannot be executed remotely, the local executor performs that part.</p>
</li>
</ol>
<p>The FDW tries hard to minimize data transfer by sending as much of your <code>WHERE</code> clause as possible to the remote server and by not retrieving unused columns <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=">[2]</a>. It also has a number of tuning knobs that we'll explore later (such as <code>fetch_size</code>, <code>use_remote_estimate</code>, <code>fdw_startup_cost</code>, and <code>fdw_tuple_cost</code><a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=This%20option%2C%20which%20can%20be,false">[3]</a>). But the real win often comes from structuring your query so that the FDW can push work down.</p>
<p>There's one last architectural point to keep in mind: the remote server runs with a restricted session environment. In remote sessions opened by <code>postgres_fdw</code>, the <code>search_path</code> is set to <code>pg_catalog</code> only, and <code>TimeZone</code>, <code>DateStyle</code>, and <code>IntervalStyle</code> are set to specific values <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=In%20the%20remote%20sessions%20opened,their%20expected%20search%20path%20environment">[4]</a>. This means that any functions you expect to run remotely must be schema‑qualified or packaged in a way that the FDW can find them. It also underscores why you should not override session settings for FDW connections unless you know exactly what you are doing <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=In%20the%20remote%20sessions%20opened,their%20expected%20search%20path%20environment">[4]</a>.</p>
<h2 id="heading-pushdown-mechanics">Pushdown Mechanics</h2>
<p>At a high level, “pushdown” means pushing as much of your SQL query as possible to the remote server. But the FDW cannot simply send arbitrary SQL. It must be <em>safe</em> and <em>portable</em> for remote evaluation. Postgres uses the term <strong>shippable</strong> to describe expressions and operations that can be evaluated on the foreign server.</p>
<h3 id="heading-what-shippable-means-in-practice">What “shippable” means in practice</h3>
<p>An expression is considered shippable if it meets several conditions:</p>
<ol>
<li><p><strong>It uses built‑in functions, operators, or data types</strong>, or functions/operators from extensions that have been explicitly allow‑listed via the extensions option on the foreign server <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=">[2]</a>. If you use a custom function or an extension that has not been declared, the FDW assumes it cannot run remotely.</p>
</li>
<li><p><strong>It’s marked IMMUTABLE.</strong> Postgres distinguishes between <code>IMMUTABLE</code>, <code>STABLE</code>, and <code>VOLATILE</code> functions. Only immutable functions – those that always return the same output for the same inputs and don’t depend on session state – are candidates for pushdown <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=functions%20in%20such%20clauses%20must,to%20reduce%20the%20risk%20of">[5]</a>. This rule prevents time‑dependent functions, such as <code>now()</code> or <code>random()</code> from being evaluated remotely, because the result might differ between the local and remote servers.</p>
</li>
<li><p><strong>It doesn’t depend on local collations or type conversions</strong>. PostgreSQL’s docs warn that type or collation mismatches can lead to semantic anomalies <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=It%20is%20generally%20recommended%20that,differently%20from%20the%20local%20server">[1]</a>. If the FDW cannot guarantee that a comparison behaves identically on both servers, it will refuse to push it down. For example, comparing a <code>citext</code> column to a <code>text</code> constant could be unsafe if the remote server doesn’t have the <code>citext</code> extension installed.</p>
</li>
</ol>
<p>From these rules, you can derive a mental checklist: avoid non‑immutable functions in your <code>WHERE</code> clause, keep your join conditions simple and typed correctly, and list any third‑party extensions you want to use in the foreign server’s extensions option so that they are considered shippable <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=">[2]</a>.</p>
<h3 id="heading-where-pushdown">WHERE pushdown</h3>
<p>If a <code>WHERE</code> clause consists entirely of shippable expressions, it will be included in the remote query. Otherwise, it will be evaluated locally. This matters because pushing a filter down reduces the number of rows returned to the local server.</p>
<p>Consider a predicate like this:</p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">WHERE</span> created_at &gt;= now() - <span class="hljs-type">interval</span> <span class="hljs-string">'30 days'</span>
</code></pre>
<p>Because <code>now()</code> is volatile (it returns a different value each time it’s called), Postgres cannot assume the remote server will interpret <code>now()</code> the same way. The FDW therefore pulls the entire table and applies the filter locally.</p>
<p>A better approach is to pass a parameter into the query or compute the cutoff timestamp once in the application and embed it into the SQL.</p>
<h3 id="heading-join-pushdown-conditions">Join pushdown conditions</h3>
<p>Joins are the next big lever. When <code>postgres_fdw</code> encounters a join between foreign tables on the <strong>same foreign server</strong>, it will send the entire join to the remote server unless it believes it will be more efficient to fetch the tables individually or unless the tables use different user mappings <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=When%20,clauses">[6]</a>.</p>
<p>It applies the same precautions described for <code>WHERE</code> clauses: the join condition must be shippable, and both tables must be on the same server. Cross‑server joins are never pushed down – the FDW will perform them locally.</p>
<h3 id="heading-shippability-decision-tree">Shippability decision tree</h3>
<p>It can be helpful to visualize the shippability rules as a flowchart. Below is a simple decision tree that you can use when inspecting an expression or join clause.</p>
<p>It starts with the question of whether an expression is in a WHERE or JOIN clause. Further decisions are made based on factors like using volatile functions, built-in functions, type mismatches, or cross-server joins. The flowchart concludes with outcomes like "Not shippable, evaluated locally" or "Shippable, included in Remote SQL."</p>
<p>If you reach the left side of the tree, the expression will be evaluated locally. If you reach the right side, the FDW can ship it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771109842865/9dafcd32-c390-487d-8b35-2911d6075b13.png" alt="Flowchart for determining SQL expression shippability. It starts with the question of whether an expression is in a WHERE or JOIN clause. Further decisions are made based on factors like using volatile functions, built-in functions, type mismatches, or cross-server joins. The flowchart concludes with outcomes like &quot;Not shippable, evaluated locally&quot; or &quot;Shippable, included in Remote SQL.&quot;" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-shippable-operations-a-deep-dive">Shippable Operations: a Deep Dive</h2>
<p>Postgres has been expanding what <code>postgres_fdw</code> can be pushed down over several versions. This section walks through each operation class and the conditions required for pushdown.</p>
<h3 id="heading-filters-where-clauses">Filters (WHERE clauses)</h3>
<p>As explained above, simple filters that use built‑in operators and immutable functions are generally pushed down. If you see a <code>Filter:</code> node above a Foreign Scan in your plan, it means some part of your predicate didn’t qualify. Common reasons include using <code>now()</code>, <code>timezone()</code> or other volatile functions, referencing a non‑allow‑listed extension, or comparing different collation settings.</p>
<p>When this happens, the entire table (or at least all rows matching other shippable conditions) is fetched, and the filter is applied locally.</p>
<p><strong>Plan smell:</strong> Look for a Foreign Scan node with a <code>Filter:</code> line directly above it. That means filtering happened locally. Also look for broad Remote SQL such as:</p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> remote_table <span class="hljs-keyword">WHERE</span> (<span class="hljs-type">name</span> = <span class="hljs-string">'Hamdaan'</span>)
</code></pre>
<p>with no group constraints. That's a sign that the filter was not pushed down.</p>
<h3 id="heading-joins">Joins</h3>
<p>Simple inner joins between foreign tables on the same foreign server are usually pushable. The join condition must satisfy the same shippability rules as filters. If the join involves more than one foreign server, if the join condition uses an unshippable function, or if the foreign tables use different user mappings, the FDW will fetch each table separately and join them locally <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=When%20,clauses">[6]</a>. This can lead to large intermediate sets being transferred.</p>
<p><strong>Plan smell:</strong> A Hash Join or Merge Join where both inputs are Foreign Scan nodes indicates that the join was performed locally. Conversely, a single Foreign Scan representing a join and containing the <code>JOIN ... ON</code> clause in Remote SQL indicates that the join was pushed down.</p>
<h3 id="heading-aggregates-group-by-count-sum-and-so-on">Aggregates (GROUP BY, COUNT, SUM, and so on)</h3>
<p>Starting in PostgreSQL 10, aggregates can be pushed to the remote server when possible. The release notes state explicitly: “push aggregate functions to the remote server,” and explain that this <strong>reduces the amount of data that must be transferred from the remote server and offloads aggregate computation</strong> <a target="_blank" href="https://www.postgresql.org/docs/release/10.0/#:~:text=,Jeevan%20Chalke%2C%20Ashutosh%20Bapat">[7]</a>.</p>
<p>To qualify, both the grouping expressions and the aggregate functions themselves must be shippable. If the FDW cannot push an aggregate, it will fetch the raw rows and perform the aggregation locally.</p>
<p><strong>Plan smell:</strong> Look for a <code>GroupAggregate</code> node above a Foreign Scan that returns many rows. When the aggregate is pushed down, there will be no local aggregate node. Instead, the Remote SQL will include a <code>GROUP BY</code> clause.</p>
<h3 id="heading-order-by-and-limit">ORDER BY and LIMIT</h3>
<p>Prior to PostgreSQL 12, sorting and limiting were rarely pushed down. In version 12, Etsuro Fujita’s patch allows ORDER BY sorts and LIMIT clauses to be pushed to <code>postgres_fdw</code> foreign servers <strong>in more cases</strong> <a target="_blank" href="https://www.postgresql.org/docs/release/12.0/#:~:text=,Etsuro%20Fujita%29%20%C2%A7%20%C2%A7">[8]</a>. For the sort or limit to be pushed, the underlying scan must be pushable, and the ordering expression must be shippable. Partitioned queries or complicated join trees may still cause the sort or limit to be applied locally.</p>
<p><strong>Plan smell:</strong> A local Sort or Limit node above a Foreign Scan indicates the operation was not pushed down. Conversely, a Remote SQL statement containing ORDER BY and LIMIT indicates that pushdown succeeded.</p>
<h3 id="heading-distinct">DISTINCT</h3>
<p>Distinct operations can be pushed down when the distinct expression list is shippable. But if the distinct is combined with unshippable expressions, or if the distinct is applied after a join that cannot be pushed down, the FDW will retrieve all rows and perform the distinct locally.</p>
<h3 id="heading-window-functions">Window functions</h3>
<p>In practice, window functions are rarely pushed down through <code>postgres_fdw</code>. They often require ordering or partitioning semantics that are difficult to represent portably. If you see a <code>WindowAgg</code> node in your plan, it’s almost always local. That doesn’t mean you can't use window functions with foreign tables, but you should expect them to incur network and CPU costs.</p>
<h3 id="heading-version-differences">Version differences</h3>
<p>Postgres developers continue to improve the FDW layer. Here are some notable changes by version:</p>
<ol>
<li><p><strong>PostgreSQL 9.6</strong> introduced remote join pushdown and allowed UPDATE/DELETE pushdown. Before 9.6, all joins were local.</p>
</li>
<li><p><strong>PostgreSQL 10</strong> introduced aggregate pushdown, enabling remote GROUP BY and aggregate functions <a target="_blank" href="https://www.postgresql.org/docs/release/10.0/#:~:text=,Jeevan%20Chalke%2C%20Ashutosh%20Bapat">[7]</a>.</p>
</li>
<li><p><strong>PostgreSQL 12</strong> expanded ORDER BY and LIMIT pushdown <a target="_blank" href="https://www.postgresql.org/docs/release/12.0/#:~:text=,Etsuro%20Fujita%29%20%C2%A7%20%C2%A7">[8]</a>.</p>
</li>
<li><p><strong>PostgreSQL 15</strong> added pushdown for certain CASE expressions and other improvements.</p>
</li>
</ol>
<p>If you learned FDW behavior on an older version, revisit your assumptions.</p>
<h2 id="heading-pushdown-blockers-and-why-they-exist">Pushdown Blockers and Why They Exist</h2>
<p>When pushdown fails, it’s not due to bad luck. There’s always a reason grounded in safety or correctness. Here are the most common blockers and how to diagnose them.</p>
<h3 id="heading-nonimmutable-functions">Non‑immutable functions</h3>
<p>Functions marked <code>VOLATILE</code> or <code>STABLE</code> cannot be pushed down because their results may differ between the local and remote server. Examples include <code>now()</code>, <code>random()</code>, <code>current_user</code>, and user‑defined functions that look at session variables or query the database. Even functions you might think are harmless, like <code>age()</code> or <code>clock_timestamp()</code>, can cause pushdown to fail.</p>
<p><strong>Fix:</strong> Compute volatile values in your application or in a CTE before referencing the foreign table. For example, compute timestamp <code>'now' - interval '30 days'</code> as a constant and compare your <code>created_at</code> column against that constant. Alternatively, move the logic into a stored generated column on the remote table.</p>
<h3 id="heading-type-and-collation-mismatches">Type and collation mismatches</h3>
<p>The documentation warns that when types or collations don’t match between the local and remote tables, the remote server may interpret conditions differently <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=It%20is%20generally%20recommended%20that,differently%20from%20the%20local%20server">[1]</a>. This is particularly insidious when text comparisons, case‑insensitive collations, or non‑default locale settings are used. If Postgres can't guarantee the same semantics, it will pull rows locally and evaluate the expression.</p>
<p><strong>Fix:</strong> Make sure that your foreign table definition uses the same data types and collations as the remote table. When in doubt, explicitly cast values to a common type.</p>
<h3 id="heading-crossserver-joins">Cross‑server joins</h3>
<p>Joins across different foreign servers cannot be pushed down. The FDW can only ship a join when both tables reside on the same remote server and use the same user mapping <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=When%20,clauses">[6]</a>. Otherwise, it will perform two separate scans and join the results locally.</p>
<p><strong>Fix:</strong> If you frequently join tables across servers, consider consolidating the tables on a single server, materializing a view on one side, or pulling the smaller table into a temporary local table before joining.</p>
<h3 id="heading-mixed-local-and-foreign-joins">Mixed local and foreign joins</h3>
<p>A join between a local table and a foreign table will not be pushed down. Even though the foreign side might be pushdown‑eligible, the FDW cannot join it with local data on the remote server. A nested loop with a parameterized foreign scan is the typical pattern here, resulting in many remote calls.</p>
<p><strong>Fix:</strong> Filter or aggregate as much as possible on the foreign side first (via a CTE or by materializing a subset) before joining to local tables.</p>
<h3 id="heading-remote-session-settings-and-search-paths">Remote session settings and search paths</h3>
<p>Because <code>postgres_fdw</code> sets a restricted <code>search_path</code>, <code>TimeZone</code>, <code>DateStyle</code>, and <code>IntervalStyle</code> in remote sessions <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=In%20the%20remote%20sessions%20opened,their%20expected%20search%20path%20environment">[4]</a>, any functions you call must be schema‑qualified or otherwise compatible. If a function relies on the current search path or session settings, it may break or produce different results on the remote side.</p>
<p><strong>Fix:</strong> Schema‑qualify remote functions and ensure that any environment‑dependent logic is safe to execute under the default FDW session settings. If necessary, attach <code>SET search_path</code> or other settings to your remote functions.</p>
<h3 id="heading-troubleshooting-matrix">Troubleshooting matrix</h3>
<p>The table below maps symptoms in your <code>EXPLAIN</code> plan to likely causes and fixes. Use it as a quick diagnostic tool when something looks off.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Symptom in plan</strong></td><td><strong>Likely cause</strong></td><td><strong>Suggested fix</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Foreign Scan has loops much greater than 1</td><td>Parameterized remote lookup caused by nested loop, join conditions not shippable</td><td>Rewrite join so the FDW can ship a single joined query, or batch remote requests via an <code>IN</code> list or temporary table</td></tr>
<tr>
<td>Broad Remote SQL that lacks scope predicates</td><td><code>WHERE</code> clause contains non‑immutable functions or unsupported operators</td><td>Replace volatile functions with constants or allow‑list extension functions, ensure types and collations match</td></tr>
<tr>
<td>Local Hash Join or Merge Join between two foreign tables</td><td>Join could not be pushed down (different servers, user mappings, or unshippable join expression)</td><td>Consolidate tables on one server, align user mappings, or rewrite the join condition</td></tr>
<tr>
<td>Local Sort, Limit, or Unique on top of a Foreign Scan</td><td><code>ORDER BY</code>, <code>LIMIT</code>, or <code>DISTINCT</code> could not be pushed down</td><td>Simplify sort expressions, push filters deeper, check PG version for improvements</td></tr>
<tr>
<td>Plan runs but gives wrong results when pushdown is enabled</td><td>Semantic mismatch due to type/collation differences or remote session settings <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=It%20is%20generally%20recommended%20that,differently%20from%20the%20local%20server">[1]</a> <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=In%20the%20remote%20sessions%20opened,their%20expected%20search%20path%20environment">[4]</a></td><td>Align types/collations, schema‑qualify functions, use stable session settings</td></tr>
</tbody>
</table>
</div><h2 id="heading-reading-explain-like-a-pro">Reading EXPLAIN Like a Pro</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771117830315/62ca8fde-2638-4ae1-b968-1100ac5251bb.png" alt="SQL execution plan analysis table with columns: exclusive, inclusive, rows x, rows, loops, and node details. Rows display Nested Loop Join, Hash Join, and Seq Scan operations with costs, times, and buffers. Highlighted cells indicate notable metrics." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Many developers skim <code>EXPLAIN</code> plans for local queries, looking at the top nodes and overall cost. For FDW queries, you must invert that habit: read the foreign parts first. The Remote SQL string tells you what the remote server is being asked to do, and the loops field tells you how many times that remote call is executed.</p>
<h3 id="heading-inspect-the-foreign-scan-nodes">Inspect the Foreign Scan nodes</h3>
<p>Start by finding the Foreign Scan node(s). In <code>EXPLAIN (VERBOSE)</code>, each foreign scan includes a line like:</p>
<pre><code class="lang-pgsql">Remote <span class="hljs-keyword">SQL</span>: <span class="hljs-keyword">SELECT</span> ...
</code></pre>
<p>This line is not a trivial – it’s the actual SQL that will run on the remote server. Read it carefully. Does it include your <code>WHERE</code> predicates? Does it include your join conditions? If not, you know the local server will pick up the slack.</p>
<p>Look at the loops column. If the loops exceed 1, the same remote query is executed multiple times. For example:</p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">Foreign</span> Scan <span class="hljs-keyword">on</span> <span class="hljs-built_in">public</span>.user_entity  (<span class="hljs-keyword">rows</span>=<span class="hljs-number">1</span> loops=<span class="hljs-number">416</span>)
  Remote <span class="hljs-keyword">SQL</span>: <span class="hljs-keyword">SELECT</span> id, tenant_id <span class="hljs-keyword">FROM</span> <span class="hljs-built_in">public</span>.user_entity <span class="hljs-keyword">WHERE</span> enabled <span class="hljs-keyword">AND</span> service_account_client_link <span class="hljs-keyword">IS</span> <span class="hljs-keyword">NULL</span> <span class="hljs-keyword">AND</span> id = <span class="hljs-meta">$1</span>
</code></pre>
<p>This is the “N+1” problem in disguise. The plan executes the foreign scan once per outer row. Multiply the per‑loop cost by the number of loops to understand why the query is slow. The fix is to rewrite the query so that the join and filters are applied in a single remote call.</p>
<h3 id="heading-recognize-initplan-vs-subplan">Recognize InitPlan vs SubPlan</h3>
<p>An InitPlan runs once and caches its result. A SubPlan can run per outer row. In FDW queries, subplans often drive parameterized remote scans. If you see a SubPlan attached to a nested loop that feeds a foreign scan, suspect a parameterized remote lookup and look for ways to turn it into an InitPlan or merge it into a single remote query.</p>
<h3 id="heading-understand-cte-materialization">Understand CTE materialization</h3>
<p>Common table expressions (CTEs) behave differently depending on whether they are marked <code>MATERIALIZED</code> or <code>NOT MATERIALIZED</code>. A materialized CTE is computed once and stored in a temporary structure, then read by the rest of the query. A non‑materialized CTE is inlined into the parent query, allowing optimizations to span across the boundary.</p>
<p>In PostgreSQL 12 and later, CTEs are inlined by default unless they’re referenced multiple times or explicitly marked <code>MATERIALIZED</code>. Materializing a CTE that contains a foreign scan can freeze a broad remote fetch and prevent later clauses from being pushed down. On the other hand, materialization can prevent repeated remote scans if the CTE is referenced multiple times. Use this lever deliberately to control where remote work happens.</p>
<h3 id="heading-annotated-example">Annotated example</h3>
<p>Let's annotate a simplified excerpt from a real plan. The goal is to show how to quickly read the relevant parts.</p>
<pre><code class="lang-pgsql">Nested <span class="hljs-keyword">Loop</span>  (<span class="hljs-keyword">rows</span>=<span class="hljs-number">414</span> loops=<span class="hljs-number">1</span>)
  -&gt; Hash <span class="hljs-keyword">Join</span>  (<span class="hljs-keyword">rows</span>=<span class="hljs-number">416</span> loops=<span class="hljs-number">1</span>)
       -&gt; <span class="hljs-keyword">Foreign</span> Scan <span class="hljs-keyword">on</span> <span class="hljs-built_in">public</span>.user_entity (<span class="hljs-keyword">rows</span>=<span class="hljs-number">1</span> loops=<span class="hljs-number">416</span>)
            Remote <span class="hljs-keyword">SQL</span>: <span class="hljs-keyword">SELECT</span> id, tenant_id <span class="hljs-keyword">FROM</span> <span class="hljs-built_in">public</span>.user_entity <span class="hljs-keyword">WHERE</span> enabled <span class="hljs-keyword">AND</span> service_account_client_link <span class="hljs-keyword">IS</span> <span class="hljs-keyword">NULL</span> <span class="hljs-keyword">AND</span> id = <span class="hljs-meta">$1</span>
  -&gt; <span class="hljs-keyword">Foreign</span> Scan <span class="hljs-keyword">on</span> <span class="hljs-built_in">public</span>.user_attribute (<span class="hljs-keyword">rows</span>=<span class="hljs-number">671</span> loops=<span class="hljs-number">1</span>)
       Remote <span class="hljs-keyword">SQL</span>: <span class="hljs-keyword">SELECT</span> ua.user_id, ua.<span class="hljs-keyword">value</span> <span class="hljs-keyword">FROM</span> user_attribute ua <span class="hljs-keyword">JOIN</span> user_entity u <span class="hljs-keyword">ON</span> ua.user_id = u.id <span class="hljs-keyword">JOIN</span> tenant r <span class="hljs-keyword">ON</span> u.tenant_id = r.id <span class="hljs-keyword">WHERE</span> ua.name = <span class="hljs-string">'attribute A'</span> <span class="hljs-keyword">AND</span> r.name = <span class="hljs-string">'demo'</span> <span class="hljs-keyword">AND</span> u.enabled <span class="hljs-keyword">AND</span> u.service_account_client_link <span class="hljs-keyword">IS</span> <span class="hljs-keyword">NULL</span> <span class="hljs-keyword">AND</span> (g.name = <span class="hljs-string">'keycloak-group-a'</span> <span class="hljs-keyword">OR</span> g.parent_group = <span class="hljs-meta">$1</span>)
</code></pre>
<p>In the old plan, the first Foreign Scan executed 416 times, each time retrieving a single row. The Remote SQL only applies the filter on enabled and service_account_client_link – it doesn’t include the tenant or group scoping. That scoping is applied by the nested loop outside the foreign scan.</p>
<p>In the refactored plan, the second Foreign Scan results from combining user_attribute, user_entity, user_group_membership, keycloak_group, and tenant into a single remote query. It retrieves 671 rows in a single query and includes all relevant filters. There is no repeated remote call. The timing difference is driven by the different loop values and the selectivity of the Remote SQL.</p>
<h2 id="heading-how-to-tune-postgresfdw">How to Tune postgres_fdw</h2>
<p>Once you've structured your query for maximum pushdown, tuning knobs let you squeeze out further performance improvements and adjust planner decisions.</p>
<h3 id="heading-fetchsize">fetch_size</h3>
<p><code>fetch_size</code> controls how many rows <code>postgres_fdw</code> retrieves per network fetch. The default is <code>100</code> rows <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=">[9]</a>. A small fetch size means more round-trips and lower memory usage. A larger fetch size reduces network overhead at the cost of buffering more rows in memory.</p>
<p>In practice, increasing <code>fetch_size</code> to a few thousand can reduce latency for large result sets. It’s specified either at the foreign server or foreign table level:</p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">SERVER</span> foreign_server <span class="hljs-keyword">OPTIONS</span> (<span class="hljs-keyword">ADD</span> fetch_size <span class="hljs-string">'1000'</span>);
<span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">FOREIGN</span> <span class="hljs-keyword">TABLE</span> remote_table <span class="hljs-keyword">OPTIONS</span> (<span class="hljs-keyword">ADD</span> fetch_size <span class="hljs-string">'1000'</span>);
</code></pre>
<h3 id="heading-useremoteestimate">use_remote_estimate</h3>
<p>By default, the planner estimates the cost of foreign scans using local statistics. This can be wildly inaccurate if the foreign table has a different data distribution. Setting <code>use_remote_estimate</code> to true tells <code>postgres_fdw</code> to run <code>EXPLAIN</code> on the remote server to get row count and cost estimates. This can dramatically improve join order selection at the cost of an additional remote query during planning <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=This%20option%2C%20which%20can%20be,false">[3]</a>. You can set this per table or per server:</p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">SERVER</span> foreign_server <span class="hljs-keyword">OPTIONS</span> (<span class="hljs-keyword">SET</span> use_remote_estimate <span class="hljs-string">'true'</span>);
</code></pre>
<h3 id="heading-fdwstartupcost-and-fdwtuplecost">fdw_startup_cost and fdw_tuple_cost</h3>
<p>These cost parameters model the overhead of starting a foreign scan and the cost per row fetched. Adjusting them can influence the planner’s choice of join strategy. A higher <code>fdw_startup_cost</code> discourages the planner from choosing plans with many small foreign scans (which might generate many remote calls). A higher <code>fdw_tuple_cost</code> discourages plans that fetch large numbers of rows <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=This%20option%2C%20which%20can%20be,false">[3]</a>. Use these only after you have solid evidence from <code>EXPLAIN</code> and experiments.</p>
<h3 id="heading-analyze-and-analyzesampling">ANALYZE and analyze_sampling</h3>
<p>Running <code>ANALYZE</code> on a foreign table collects local statistics by sampling the remote table <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=This%20option%2C%20which%20can%20be,false">[3]</a>. Accurate stats are essential for good estimates when <code>use_remote_estimate</code> is false.</p>
<p>But if the remote table changes frequently, these stats become stale quickly. The <code>analyze_sampling</code> option controls whether sampling happens on the remote side or locally. When <code>analyze_sampling</code> is set to <code>random</code>, <code>system</code>, <code>bernoulli</code>, or <code>auto</code>, <code>ANALYZE</code> will sample rows remotely instead of pulling all rows into the local server<a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=This%20option%2C%20which%20can%20be,false">[3]</a>.</p>
<h3 id="heading-extensions">extensions</h3>
<p>The extensions option lists extensions whose functions and operators can be shipped to the remote server <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=">[2]</a>. If you rely on functions from citext, <code>pg_trgm</code>, or other extensions, add them to the server definition:</p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">SERVER</span> foreign_server <span class="hljs-keyword">OPTIONS</span> (<span class="hljs-keyword">SET</span> extensions <span class="hljs-string">'citext,pg_trgm'</span>);
</code></pre>
<h3 id="heading-a-quick-knob-impact-table">A quick knob impact table</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Knob</strong></td><td><strong>Primary effect</strong></td><td><strong>When to change it</strong></td><td><strong>Possible downside</strong></td></tr>
</thead>
<tbody>
<tr>
<td>fetch_size</td><td>Number of rows per fetch</td><td>Result sets are large and latency dominates</td><td>Too large consumes memory</td></tr>
<tr>
<td>use_remote_estimate</td><td>Better row count/cost estimates</td><td>Planner misestimates foreign scans</td><td>Extra remote queries during planning</td></tr>
<tr>
<td>fdw_startup_cost</td><td>Penalty per foreign scan</td><td>Planner chooses many small foreign scans</td><td>Wrong values bias the planner</td></tr>
<tr>
<td>fdw_tuple_cost</td><td>Cost per row fetched</td><td>Planner pulls too many rows</td><td>Mis‑tuned values mislead planner</td></tr>
<tr>
<td>extensions</td><td>Which extension functions are shippable</td><td>Using extension functions in predicates</td><td>Extensions must exist and match on both servers</td></tr>
</tbody>
</table>
</div><h2 id="heading-schema-and-index-recommendations">Schema and Index Recommendations</h2>
<p>Pushdown doesn’t eliminate the need for good indexes. In fact, effective pushdown depends on the remote server having indexes that support the filter and join predicates you’re shipping.</p>
<p>Below are some patterns to watch for in FDW queries and the indexes that support them. You can adapt these to your own schema.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Table</strong></td><td><strong>Access pattern</strong></td><td><strong>Recommended index</strong></td><td><strong>Why</strong></td></tr>
</thead>
<tbody>
<tr>
<td>tenant (remote)</td><td>Filter by tenant.name</td><td>UNIQUE (name) or BTREE (name)</td><td>Resolves tenant ID quickly</td></tr>
<tr>
<td>keycloak_group (remote)</td><td>Filter by name, join by tenant_id, filter on parent_group</td><td>Composite (tenant_id, name) and (parent_group)</td><td>Supports resolving root group and walking one‑level hierarchy</td></tr>
<tr>
<td>user_group_membership (remote)</td><td>Join by user_id, filter by group_id</td><td>BTREE (group_id, user_id)</td><td>Efficiently finds users in a set of groups</td></tr>
<tr>
<td>user_attribute (remote)</td><td>Filter by name, join by user_id</td><td>Composite (name, user_id) (optionally include value)</td><td>Matches “attribute name → users → values” flow</td></tr>
<tr>
<td>user_entity (remote)</td><td>Filter by tenant_id, enabled, service_account_client_link IS NULL, join by id</td><td>Partial index on (tenant_id, id) with predicate on enabled and service_account_client_link IS NULL</td><td>Helps remote planner start from user table when tenant and user filters are applied</td></tr>
<tr>
<td>filtercategory (local)</td><td>Filter by category &amp;&amp; uuid[], join on (entitytype, entityid)</td><td>GIN index on category, BTREE (entitytype, entityid)</td><td>Speeds array overlap checks and join predicate</td></tr>
</tbody>
</table>
</div><p>In general, indexes should reflect the join order you expect the remote planner to use. If your Remote SQL starts with:</p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">FROM</span> user_attribute ua <span class="hljs-keyword">JOIN</span> user_entity u <span class="hljs-keyword">ON</span> ua.user_id = u.id <span class="hljs-keyword">JOIN</span> user_group_membership ugm <span class="hljs-keyword">ON</span> ...
</code></pre>
<p>ensure that indexes exist on <code>user_attribute(user_id</code>) and <code>user_group_membership(user_id)</code>.</p>
<h2 id="heading-benchmarking-methodology">Benchmarking Methodology</h2>
<p>It’s easy to claim a performance improvement without proper measurement. Here's a repeatable method you can use to benchmark FDW query changes.</p>
<ol>
<li><p><strong>Warm the caches.</strong> Run each query once to load data into the remote buffer cache and the local FDW connection. Discard the timings.</p>
</li>
<li><p><strong>Measure latencies.</strong> Use EXPLAIN (ANALYZE, BUFFERS, VERBOSE) to capture execution times, buffer usage, and remote row counts. Be aware that EXPLAIN ANALYZE adds overhead, so record the raw execution time if possible by running the query directly.</p>
</li>
<li><p><strong>Record remote metrics.</strong> On the remote server, enable pg_stat_statements and track the calls, total_time, and rows for each remote query. This gives you a per‑query breakdown and confirms what Remote SQL is executed.</p>
</li>
<li><p><strong>Control for concurrency and network latency.</strong> Run benchmarks during a quiet period or isolate the test cluster. If your environment has high network latency, record the round‑trip time separately to attribute delays.</p>
</li>
<li><p><strong>Compare apples to apples.</strong> Benchmark the old and new queries under identical conditions. Use the same sample data, same remote server, and same connection settings.</p>
</li>
<li><p><strong>Look at row counts.</strong> The primary goal of pushdown is to reduce the number of rows shipped. Compare the rows column of each Foreign Scan node.</p>
</li>
</ol>
<p>Here's a simple matrix you can use to record your experiments:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Scenario</strong></td><td><strong>What you're testing</strong></td><td><strong>Expected change in Remote SQL</strong></td><td><strong>Metrics to record</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Baseline (old query)</td><td>Starting point: broad remote scans + local joins</td><td>Remote SQL lacks scoping predicates</td><td>p50/p95 latency, remote row count, local sort/hash time</td></tr>
<tr>
<td>Refactor (new query)</td><td>Join + filter pushdown</td><td>Remote SQL includes joins and filters</td><td>Same metrics, plus remote row count</td></tr>
<tr>
<td>Introduce a volatile function</td><td>Pushdown blocker test</td><td>Clause removed from Remote SQL</td><td>Remote row count increases, local filter cost increases</td></tr>
<tr>
<td>Type or collation mismatch</td><td>Semantic risk test</td><td>Remote SQL might change behavior or lose pushdown</td><td>Compare correctness and row counts</td></tr>
<tr>
<td>ORDER/LIMIT pushdown</td><td>Version‑dependent test</td><td>Remote SQL includes ORDER BY, LIMIT</td><td>Sort time shifts to remote. Row count should remain</td></tr>
<tr>
<td>use_remote_estimate on/off</td><td>Planning accuracy test</td><td>Planner uses remote estimates</td><td>Planning time, join order, and runtime difference</td></tr>
</tbody>
</table>
</div><h2 id="heading-monitoring-and-logging">Monitoring and Logging</h2>
<p>In production, you need to know when a query starts misbehaving. There are two places to look: the local server and the remote server.</p>
<h3 id="heading-local-metrics">Local metrics</h3>
<ol>
<li><p><strong>pg_stat_statements.</strong> This extension tracks planning and execution times, row counts, and buffer hits for each query. Look for high total times relative to rows or calls.</p>
</li>
<li><p><strong>Auto Explain or auto_explain.</strong> Turn on <code>auto_explain.log_min_duration_statement</code> to capture slow queries with plans. This will show you the Remote SQL executed and whether the plan changed.</p>
</li>
<li><p><strong>Connection pool metrics.</strong> Monitor connection counts and wait events related to FDW operations (for example, PostgresFdwConnect, PostgresFdwGetResult) as described in the documentation <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=,Extension">[10]</a>.</p>
</li>
</ol>
<h3 id="heading-remote-metrics">Remote metrics</h3>
<ol>
<li><p><strong>pg_stat_statements on the remote server.</strong> This lets you see which Remote SQL queries are being executed, how often, and how long they take. Compare these with the Remote SQL strings in your local EXPLAIN plans.</p>
</li>
<li><p><strong>Server logs.</strong> Increase <code>log_statement</code> or <code>log_min_duration_statement</code> on the remote server to capture long-running remote queries.</p>
</li>
</ol>
<p>Correlating local and remote metrics can reveal patterns such as a new code path causing a surge in remote queries or pushdown failures, leading to heavy remote scans.</p>
<h2 id="heading-case-study-refactoring-a-keycloak-coverage-query">Case Study: Refactoring a Keycloak Coverage Query</h2>
<p>The theory above may seem abstract until you see it play out in practice. Let's walk through a real example inspired by a Keycloak integration.</p>
<p>The original query calculated coverage: given a list of category IDs, it returned the percentage of users who had attributes mapped to those categories and a JSON array of entity counts. The query used a CTE to build a list of scoped users, then joined it with user attributes, category mappings, and a few other tables.</p>
<h3 id="heading-symptom">Symptom</h3>
<p>In a test environment with 100K user records, the query averaged 166 ms. This was slower than expected. Running <code>EXPLAIN (ANALYZE, BUFFERS, VERBOSE)</code> showed two foreign scans on the Keycloak database. The first scanned <code>user_entity</code> 416 times (loops = 416). The second pulled all rows from <code>user_attribute</code> where <code>name = 'attributeA'</code> before filtering by tenant and group locally.</p>
<p>Here's a simplified excerpt (numbers are approximate):</p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">Foreign</span> Scan <span class="hljs-keyword">on</span> <span class="hljs-built_in">public</span>.user_entity  (actual <span class="hljs-type">time</span>=<span class="hljs-number">0.117</span>.<span class="hljs-number">.0</span><span class="hljs-number">.117</span> <span class="hljs-keyword">rows</span>=<span class="hljs-number">1</span> loops=<span class="hljs-number">416</span>)
  Remote <span class="hljs-keyword">SQL</span>: <span class="hljs-keyword">SELECT</span> id, tenant_id <span class="hljs-keyword">FROM</span> <span class="hljs-built_in">public</span>.user_entity <span class="hljs-keyword">WHERE</span> (enabled <span class="hljs-keyword">AND</span> service_account_client_link <span class="hljs-keyword">IS</span> <span class="hljs-keyword">NULL</span> <span class="hljs-keyword">AND</span> id = <span class="hljs-meta">$1</span>)
<span class="hljs-keyword">Foreign</span> Scan <span class="hljs-keyword">on</span> <span class="hljs-built_in">public</span>.user_attribute  (actual <span class="hljs-type">time</span>=<span class="hljs-number">41.267</span>.<span class="hljs-number">.80</span><span class="hljs-number">.352</span> <span class="hljs-keyword">rows</span>=<span class="hljs-number">80739</span> loops=<span class="hljs-number">1</span>)
  Remote <span class="hljs-keyword">SQL</span>: <span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">value</span>, user_id <span class="hljs-keyword">FROM</span> <span class="hljs-built_in">public</span>.user_attribute <span class="hljs-keyword">WHERE</span> ((<span class="hljs-string">'attributeA'</span> = <span class="hljs-type">name</span>))
</code></pre>
<p>The first scan performed a single-row lookup 416 times. The second scan retrieved 80,739 rows because the only condition pushed down was <code>name = 'attributeA'</code>. Tenant and group scoping occurred locally. That meant 80k rows were transferred over the network and then filtered down to about 671 on the local side.</p>
<h3 id="heading-diagnosis">Diagnosis</h3>
<p>There were two main issues.</p>
<p>First was the N+1 remote calls on user_entity. The join to <code>user_entity</code> was not pushed down, so the plan executed a remote lookup for each row from <code>user_group_membership</code>. This created 416 remote queries.</p>
<p>Second was the unscoped attribute fetch. Because the <code>WHERE</code> clause included <code>user_entity.tenant_id = tenant.id</code> and <code>keycloak_group.name = 'groupA'</code> in a higher CTE, the FDW could not see those predicates when scanning <code>user_attribute</code>. It therefore fetched all rows with <code>name = 'attributeA'</code> and left the tenant and group filters to the local side.</p>
<h3 id="heading-refactor">Refactor</h3>
<p>The fix was to inline the tenant and group joins into the user_attribute scan to avoid the nested-loop pattern. The refactored <code>selected_user_attributes</code> CTE looked like this (simplified for readability):</p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">WITH</span> selected_user_attributes <span class="hljs-keyword">AS</span> (
  <span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">DISTINCT</span> ua.user_id, ua.<span class="hljs-keyword">value</span>
  <span class="hljs-keyword">FROM</span> <span class="hljs-built_in">public</span>.user_attribute ua
  <span class="hljs-keyword">JOIN</span> <span class="hljs-built_in">public</span>.user_entity u <span class="hljs-keyword">ON</span> u.id = ua.user_id
  <span class="hljs-keyword">JOIN</span> <span class="hljs-built_in">public</span>.user_group_membership ugm <span class="hljs-keyword">ON</span> ugm.user_id = u.id
  <span class="hljs-keyword">JOIN</span> <span class="hljs-built_in">public</span>.keycloak_group g <span class="hljs-keyword">ON</span> g.id = ugm.group_id
  <span class="hljs-keyword">JOIN</span> <span class="hljs-built_in">public</span>.tenant r <span class="hljs-keyword">ON</span> r.id = u.tenant_id
  <span class="hljs-keyword">WHERE</span> ua.name = <span class="hljs-string">'attributeA'</span>
    <span class="hljs-keyword">AND</span> u.enabled
    <span class="hljs-keyword">AND</span> u.service_account_client_link <span class="hljs-keyword">IS</span> <span class="hljs-keyword">NULL</span>
    <span class="hljs-keyword">AND</span> r.name = <span class="hljs-string">'tenantA'</span>
    <span class="hljs-keyword">AND</span> (g.name = <span class="hljs-string">'groupA'</span> <span class="hljs-keyword">OR</span> g.parent_group = (
         <span class="hljs-keyword">SELECT</span> id <span class="hljs-keyword">FROM</span> <span class="hljs-built_in">public</span>.keycloak_group <span class="hljs-keyword">WHERE</span> <span class="hljs-type">name</span> = <span class="hljs-string">'groupA'</span> <span class="hljs-keyword">AND</span> tenant_id= r.id
    ))
)
</code></pre>
<p>This single query expresses the same scoping logic that previously lived in separate CTEs. Because all the join conditions are on the same foreign server and use built‑in operators, the FDW can push down the entire join. The new plan looked like this:</p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">Foreign</span> Scan  (actual <span class="hljs-type">time</span>=<span class="hljs-number">7.840</span>.<span class="hljs-number">.7</span><span class="hljs-number">.856</span> <span class="hljs-keyword">rows</span>=<span class="hljs-number">671</span> loops=<span class="hljs-number">1</span>)
  Remote <span class="hljs-keyword">SQL</span>: <span class="hljs-keyword">SELECT</span> ua.user_id, ua.<span class="hljs-keyword">value</span> <span class="hljs-keyword">FROM</span> user_attribute ua <span class="hljs-keyword">JOIN</span> user_entity u <span class="hljs-keyword">ON</span> ua.user_id = u.id <span class="hljs-keyword">JOIN</span> user_group_membership ugm <span class="hljs-keyword">ON</span> ugm.user_id = u.id <span class="hljs-keyword">JOIN</span> keycloak_group g <span class="hljs-keyword">ON</span> g.id = ugm.group_id <span class="hljs-keyword">JOIN</span> tenant r <span class="hljs-keyword">ON</span> u.tenant_id= r.id <span class="hljs-keyword">WHERE</span> ua.name = <span class="hljs-string">'attributeA'</span> <span class="hljs-keyword">AND</span> u.enabled <span class="hljs-keyword">AND</span> u.service_account_client_link <span class="hljs-keyword">IS</span> <span class="hljs-keyword">NULL</span> <span class="hljs-keyword">AND</span> r.name = <span class="hljs-string">'tenantA'</span> <span class="hljs-keyword">AND</span> (g.name = <span class="hljs-string">'groupA'</span> <span class="hljs-keyword">OR</span> g.parent_group = <span class="hljs-meta">$1</span>)
</code></pre>
<p>Only one remote query is executed, and it returns 671 rows. Tenant and group scoping occur on the remote server. There is no nested loop or repeated remote scan. The final runtime dropped to <strong>about 25 ms</strong>.</p>
<h3 id="heading-why-it-improved">Why it improved</h3>
<ol>
<li><p><strong>Fewer rows crossing the network.</strong> The old plan fetched 80k attribute rows and filtered them locally. The new plan fetched only the 671 scoped rows.</p>
</li>
<li><p><strong>No repeated remote calls.</strong> The old plan executed 416 remote scans of <code>user_entity</code>. The new plan performs one joined remote query.</p>
</li>
<li><p><strong>Less local work.</strong> Because the join and filtering happen remotely, the local side no longer hashes or filters large sets.</p>
</li>
</ol>
<h3 id="heading-key-takeaway">Key takeaway</h3>
<p>If you see a Foreign Scan with a high loops count or a Remote SQL that doesn’t contain your filters and joins, you’re leaving performance on the table. Merging filters and joins into a single remote query (subject to shippability rules) often yields orders-of-magnitude improvements.</p>
<h2 id="heading-checklist-and-troubleshooting-guide">Checklist and Troubleshooting Guide</h2>
<p>The following steps summarize how to approach FDW performance tuning:</p>
<ol>
<li><p><strong>Inspect the Remote SQL.</strong> Always run <code>EXPLAIN (VERBOSE)</code> and look at what is being sent to the remote. If your predicates are missing, the FDW isn't pushing them down.</p>
</li>
<li><p><strong>Check loops.</strong> If the loops are greater than 1 on a Foreign Scan, you are paying for repeated remote calls. Rewrite the query or reorder the joins to make the foreign scan run once.</p>
</li>
<li><p><strong>Make predicates shippable.</strong> Replace volatile functions with constants or parameters. Ensure operators and functions are built‑in or explicitly allow‑listed via the extensions option <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=">[2]</a>.</p>
</li>
<li><p><strong>Align types and collations.</strong> Use the same data types and collations on both sides to avoid semantic mismatches <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=It%20is%20generally%20recommended%20that,differently%20from%20the%20local%20server">[1]</a>.</p>
</li>
<li><p><strong>Push joins to the same server.</strong> Consolidate tables on one foreign server if possible. Joins across servers cannot be pushed down <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=When%20,clauses">[6]</a>.</p>
</li>
<li><p><strong>Use use_remote_estimate when planning seems off.</strong> Enabling remote estimates can improve join order selection <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=This%20option%2C%20which%20can%20be,false">[3]</a>.</p>
</li>
<li><p><strong>Tune fetch_size and costs</strong> if your queries transfer many rows. A bigger fetch_size reduces round-trip; adjusting <code>fdw_startup_cost</code> and <code>fdw_tuple_cost</code> influences the planner <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=This%20option%2C%20which%20can%20be,false">[3]</a>.</p>
</li>
<li><p><strong>Analyze foreign tables</strong> if you rely on local cost estimates. Keep in mind that stats can get stale quickly <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=This%20option%2C%20which%20can%20be,false">[3]</a>.</p>
</li>
<li><p><strong>Monitor both servers.</strong> Use <code>pg_stat_statements</code> on local and remote servers to see how often remote queries run and how long they take.</p>
</li>
<li><p><strong>Test version upgrades.</strong> Each major release improves FDW pushdown semantics (for example, aggregates in 10 <a target="_blank" href="https://www.postgresql.org/docs/release/10.0/#:~:text=,Jeevan%20Chalke%2C%20Ashutosh%20Bapat">[7]</a>, ORDER/LIMIT in 12 <a target="_blank" href="https://www.postgresql.org/docs/release/12.0/#:~:text=,Etsuro%20Fujita%29%20%C2%A7%20%C2%A7">[8]</a>). Retest after upgrading.</p>
</li>
</ol>
<h2 id="heading-case-study-takeaways">Case Study Takeaways</h2>
<p>Querying remote data with PostgreSQL’s <code>postgres_fdw</code> can be fast and convenient if you respect the underlying mechanics. Pushdown is the difference between streaming a trickle of relevant rows and hauling an ocean of data across the network. It isn't simply a matter of moving CPU cycles – it changes how much data moves, how many network round-trip occur, and how much your local server has to do.</p>
<p>The rules may seem restrictive – use only immutable functions, avoid cross‑server joins, align types and collations – but they exist to preserve correctness while enabling optimization.</p>
<p>By reading <code>EXPLAIN</code> from the bottom up, inspecting the Remote SQL, and understanding the shippability rules, you can spot slow patterns quickly. Armed with tuning knobs like <code>fetch_size</code> and <code>use_remote_estimate</code>, and a willingness to rewrite queries to make joins and filters pushable, you can often achieve dramatic performance gains without touching your hardware.</p>
<p>This case study shows that rewriting a query to enable a single-joined remote query reduced runtime from around <strong>166 ms to 25 ms</strong>. That sort of improvement is not rare. It’s what happens when you treat FDW queries as distributed queries rather than local queries in disguise.</p>
<p>The next time you debug a slow FDW query, remember this handbook. Check the Remote SQL. Count the loops. Ask yourself: “Am I doing the work close to the data, or am I bringing the data to the work?” Adjust accordingly, and you'll write queries that make the most of Postgres's federated capabilities while keeping your latency in check.</p>
<p>This section closes the case study loop and summarizes exactly what changed in the plan and why it produced a large end-to-end win. The following sections of the handbook turn that single win into a repeatable method: how Postgres determines what is shippable, how to quickly read FDW plans, which operations and versions matter, and how to debug common failure modes that prevent pushdown.</p>
<h2 id="heading-advanced-operations-a-deeper-dive-into-shippability">Advanced Operations: A Deeper Dive into Shippability</h2>
<p>The previous sections introduced the basic rules around what can be pushed to the remote and why. To really make sense of those rules, you need to see how they play out on the operations you use every day.</p>
<p>This section walks through filters, joins, aggregates, ordering, and limits, DISTINCT queries, and window functions in more detail. By the end, you should have a mental map of which operations to trust and which to double‑check when reading your plans.</p>
<h3 id="heading-filters-and-simple-predicates">Filters and simple predicates</h3>
<h4 id="heading-where-clauses-matter-more-than-you-think">WHERE clauses matter more than you think</h4>
<p>When you specify <code>WHERE attribute = 'value'</code> on a foreign table, the FDW will happily transmit that predicate to the remote server as long as the comparison uses built‑in types and immutable operators. For example:</p>
<ul>
<li><p><code>WHERE id = 42</code> is fine</p>
</li>
<li><p><code>WHERE lower(username) = 'hamdaan'</code> is fine if <code>lower()</code> is allow‑listed and immutable</p>
</li>
<li><p><code>WHERE created_at &gt;= now() - interval '7 days'</code> is not shippable because <code>now()</code> is volatile</p>
</li>
</ul>
<p>When such a predicate cannot be pushed, the FDW will fetch every row that matches all the shippable predicates and apply the rest locally. That means that a seemingly innocuous call to <code>now()</code> can blow up your network traffic.</p>
<p>The lesson is simple: compute volatile values up front (in your application or in a CTE) and reference them as constants in the query against the foreign table.</p>
<h4 id="heading-complex-expressions-are-not-automatically-unsafe">Complex expressions are not automatically unsafe</h4>
<p>Suppose you have <code>WHERE (status = 'active' AND (age BETWEEN 18 AND 29 OR age &gt; 65))</code>. This entire expression is shippable because it uses built‑in boolean logic, simple comparisons, and immutable operators. The FDW will deparse it into remote SQL and forward it. You only need to worry when one of the subexpressions introduces a function or operator that the FDW doesn’t recognize or cannot safely assume exists on the remote.</p>
<p>A good heuristic is: if you can express your filter using only simple comparisons, boolean logic, and built‑in functions, pushdown should work. When in doubt, check the Remote SQL.</p>
<h4 id="heading-array-and-json-operators">Array and JSON operators</h4>
<p>Modern Postgres makes heavy use of array and JSON functions. Many of these functions, like the array overlap operator <code>&amp;&amp;</code> used in the case study, are built‑in and can be shipped. But some JSON functions are provided by extensions (like <code>jsonb_path_query</code> or functions from the <code>pgjson</code> family).</p>
<p>If your filter uses one of these, ensure that the extension is available and allow‑listed on the foreign server. Otherwise, the FDW will fetch rows and perform the JSON logic locally. This is rarely what you want when dealing with large JSON columns.</p>
<h3 id="heading-joins-the-good-the-bad-and-the-ugly">Joins: the good, the bad, and the ugly</h3>
<h4 id="heading-sameserver-joins-are-your-friend">Same‑server joins are your friend</h4>
<p>If you join multiple foreign tables that are all defined on the same foreign server and user mapping, and if the join condition uses only shippable expressions, then the FDW can generate a single remote join. This is the ideal case.</p>
<p>For example, joining orders and customers on <code>orders.customer_id = customers.id</code> is pushable, as long as both tables reside on the same foreign server. The remote planner will use its own statistics and indexes to plan the join, and the local server will simply iterate through the result. Postgres 9.6 and later support this pattern <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=When%20,clauses">[6]</a>.</p>
<h4 id="heading-crossserver-joins-break-pushdown">Cross‑server joins break pushdown</h4>
<p>If you attempt to join two foreign tables that live on different servers (or even on the same remote server but with different user mappings), postgres_fdw will fetch the tables separately and join them locally. This is almost always slower than pushing the join down, because you end up transferring both tables in their entirety.</p>
<p>The FDW design team chose not to support cross‑server joins because there is no portable way to tell two remote servers to cooperate on a join. Your options are: replicate one table on the other server, materialize the smaller table locally before joining, or restructure the query to filter aggressively on each side before joining locally.</p>
<h4 id="heading-mixed-localforeign-joins-are-tricky">Mixed local/foreign joins are tricky</h4>
<p>Joining a local table to a foreign table cannot be pushed down, for straightforward reasons: the remote server has no access to your local data. A common pattern that triggers repeated remote calls looks like this:</p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">SELECT</span> u.id, a.<span class="hljs-keyword">value</span>
<span class="hljs-keyword">FROM</span> users u
<span class="hljs-keyword">LEFT JOIN</span> user_attribute a
  <span class="hljs-keyword">ON</span> a.user_id = u.id <span class="hljs-keyword">AND</span> a.name = <span class="hljs-string">'favorite_color'</span>;
</code></pre>
<p>If <code>users</code> is a local table and <code>user_attribute</code> is foreign, the plan may use a nested loop: for each local u, it executes a remote lookup in user_attribute to retrieve attributes.</p>
<p>The fix is to flip the query: retrieve all relevant rows from <code>user_attribute</code> in one remote scan, then join them locally. Or, if possible, create a small temporary table on the remote side with your u.id values, perform the join entirely remotely, and then fetch the results.</p>
<h4 id="heading-join-conditions-matter">Join conditions matter</h4>
<p>Even when joining two foreign tables on the same server, an unshippable join condition will force the join to be local. For example, <code>JOIN ON textcol ILIKE '%foo%'</code> is not pushable because <code>ILIKE</code> might not exist or behave identically on the remote.</p>
<p>If you need case‑insensitive matching, consider lowercasing both sides: <code>LOWER(textcol) = 'foo'</code> (assuming the remote server has the <code>lower()</code> function available and allowed). Similarly, joining on a cast expression (for example, <code>JOIN ON CAST(a.id AS text) = b.text_id</code>) can block pushdown. Define your columns with matching types instead.</p>
<h3 id="heading-aggregates-and-grouping">Aggregates and grouping</h3>
<p>Aggregates are where the data movement story shines. When you can push down a <code>GROUP BY</code> and aggregate functions like <code>COUNT</code>, <code>SUM</code>, <code>AVG</code>, or <code>MAX</code>, you reduce the result set to just the aggregated rows. This can be a difference of several orders of magnitude.</p>
<p>Postgres 10 introduced aggregate pushdown <a target="_blank" href="https://www.postgresql.org/docs/release/10.0/#:~:text=,Jeevan%20Chalke%2C%20Ashutosh%20Bapat">[7]</a>. But not all aggregates are equal:</p>
<p><strong>Simple aggregates</strong> such as <code>COUNT(*)</code>, <code>SUM(col)</code>, <code>AVG(col)</code>, <code>MIN(col)</code>, and <code>MAX(col)</code> are shippable when applied to shippable expressions. Even <code>COUNT(DISTINCT col)</code> is often shippable, because the remote can deduplicate before counting. The FDW will wrap the aggregate in a remote query and return just the aggregated row.</p>
<p>If you see a GroupAggregate node on the local side, check whether all involved columns and functions are shippable. If they are, ensure that the join conditions above are also pushable.</p>
<p><strong>Filtered aggregates</strong> such as <code>COUNT(*) FILTER (WHERE x &gt; 5) or SUM(col) FILTER (WHERE status = 'active')</code> are often pushable, because they translate into <code>SUM(CASE WHEN condition THEN col ELSE 0 END) or COUNT(...)</code>. As long as the filter is shippable, the FDW will push it into the remote aggregate.</p>
<p><strong>User‑defined aggregates</strong> are rarely pushable. If you have a custom aggregate function, the FDW will not assume that it exists or behaves the same on the remote server. Even if you install the function on both servers, postgres_fdw won't push it unless the function is in an allow‑listed extension.</p>
<p><strong>Grouping sets and rollups</strong> are not currently pushable. When you write <code>GROUP BY GROUPING SETS (...) or ROLLUP(...)</code>, Postgres will compute the grouping locally even if the underlying scan is remote.</p>
<p>If you need complex rollups, consider performing them in two steps: push down the initial grouping to the remote server to reduce rows, then perform the rollup locally.</p>
<h3 id="heading-order-by-limit-and-distinct">ORDER BY, LIMIT, and DISTINCT</h3>
<p>Ordering and limiting rows may seem like purely cosmetic features, but they affect how much data is transferred. If the remote can sort and limit, the local server only receives the top N rows. If it cannot, the local server must sort everything.</p>
<p>Postgres 12 expanded the cases where <code>ORDER BY</code> and LIMIT are pushed down <a target="_blank" href="https://www.postgresql.org/docs/release/12.0/#:~:text=,Etsuro%20Fujita%29%20%C2%A7%20%C2%A7">[8]</a>. Here are guidelines:</p>
<ul>
<li><p><strong>Single foreign scan with simple sort:</strong> If your query selects from one foreign table and sorts by a shippable expression (for example, <code>ORDER BY created_at DESC</code>), the FDW will include <code>ORDER BY</code> in Remote SQL. It will also push down <code>LIMIT</code> and <code>OFFSET</code>. This is ideal because the remote server does the sort and sends only the top rows.</p>
</li>
<li><p><strong>Sort after join:</strong> If you sort after joining two foreign tables on the same server, and the join and sort expressions are shippable, the FDW may push both down. But if the sort requires columns from the local side or from a different remote server, the FDW cannot push it down.</p>
</li>
<li><p><strong>Sort after aggregation:</strong> Sorting aggregated results is often pushable as long as the aggregate itself is pushable. But when grouping occurs locally, the sort remains local.</p>
</li>
<li><p><strong>DISTINCT behaves like GROUP BY.</strong> If the distinct expression list is shippable, the FDW can push it down. If you write <code>SELECT DISTINCT ON (col1) col2, col3 FROM ...</code> and col3 is not part of the <code>DISTINCT</code> list, Postgres will treat this as <code>GROUP BY</code> and may push it. Be aware that <code>DISTINCT ON</code> semantics differ from plain <code>DISTINCT</code> and may not be pushable in older Postgres versions.</p>
</li>
</ul>
<h3 id="heading-window-functions-1">Window functions</h3>
<p>Window functions (for example, <code>ROW_NUMBER() OVER (PARTITION BY ...), RANK(), LAG(), LEAD()</code>) rely on ordering and partitioning across rows.</p>
<p>Postgres has not yet taught <code>postgres_fdw</code> how to push window functions. When you see a WindowAgg node in your plan, it’s almost always local. The FDW will fetch the rows, and the local server will sort, partition, and compute the window. If you need to run window functions on remote data, plan to transfer the data locally.</p>
<h3 id="heading-versionspecific-quirks">Version‑specific quirks</h3>
<p>The exact pushdown capabilities vary by release. When planning migrations or deciding whether to rely on a pushdown behavior, check the release notes:</p>
<ul>
<li><p><strong>9.6:</strong> first version to support pushdown of joins and sorts, and remote updates and deletes.</p>
</li>
<li><p><strong>10:</strong> introduced aggregate pushdown <a target="_blank" href="https://www.postgresql.org/docs/release/10.0/#:~:text=,Jeevan%20Chalke%2C%20Ashutosh%20Bapat">[7]</a>, significantly reducing network use for <code>GROUP BY</code> queries.</p>
</li>
<li><p><strong>11:</strong> improved partition pruning and join ordering for foreign tables.</p>
</li>
<li><p><strong>12:</strong> expanded <code>ORDER BY</code> and <code>LIMIT</code> pushdown <a target="_blank" href="https://www.postgresql.org/docs/release/12.0/#:~:text=,Etsuro%20Fujita%29%20%C2%A7%20%C2%A7">[8]</a>.</p>
</li>
<li><p><strong>15:</strong> added pushdown for simple <code>CASE</code> expressions and additional built‑in functions.</p>
</li>
<li><p><strong>17</strong> (development at the time of writing) continues to expand shippable constructs. Always test on your target version because subtle improvements can change what the FDW can ship.</p>
</li>
</ul>
<h2 id="heading-common-antipatterns-and-how-to-avoid-them">Common Anti‑Patterns and How to Avoid Them</h2>
<p>Everyone has run into FDW queries that seemed reasonable but turned out to be bottlenecks. Here are a few of the most common mistakes and how to correct them. These examples are deliberately simplified – so you can adapt them to your schema.</p>
<h3 id="heading-using-volatile-functions-in-predicates">Using volatile functions in predicates</h3>
<p><strong>Anti‑pattern:</strong></p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">SELECT</span> *
<span class="hljs-keyword">FROM</span> audit_logs
<span class="hljs-keyword">WHERE</span> event_ts &gt;= now() - <span class="hljs-type">interval</span> <span class="hljs-string">'1 day'</span>;
</code></pre>
<p><code>now()</code> is a volatile function, so the FDW refuses to push this predicate. It pulls all rows from audit_logs and filters them locally.</p>
<p><strong>Better:</strong></p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">SELECT</span> *
<span class="hljs-keyword">FROM</span> audit_logs
<span class="hljs-keyword">WHERE</span> event_ts &gt;= <span class="hljs-meta">$1</span>;
</code></pre>
<p>Compute <code>$1</code> (a timestamp) in your application or upstream query. Or compute it once in a CTE:</p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">WITH</span> cutoff <span class="hljs-keyword">AS</span> (<span class="hljs-keyword">SELECT</span> now() - <span class="hljs-type">interval</span> <span class="hljs-string">'1 day'</span> <span class="hljs-keyword">AS</span> ts) <span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> audit_logs, cutoff <span class="hljs-keyword">WHERE</span> event_ts &gt;= cutoff.ts;
</code></pre>
<p>The FDW sees a constant and pushes the predicate.</p>
<h3 id="heading-joining-local-and-foreign-data-first">Joining local and foreign data first</h3>
<p><strong>Anti‑pattern:</strong></p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">SELECT</span> u.email, ua.<span class="hljs-keyword">value</span>
<span class="hljs-keyword">FROM</span> users u
<span class="hljs-keyword">LEFT JOIN</span> user_attribute ua <span class="hljs-keyword">ON</span> u.id = ua.user_id <span class="hljs-keyword">AND</span> ua.name = <span class="hljs-string">'favorite_movie'</span>;
</code></pre>
<p>This uses a local table (users) to drive a join to a foreign table (user_attribute). The FDW receives 10,000 individual remote queries if users have 10,000 rows. Each call fetches one or zero rows from user_attribute.</p>
<p><strong>Better:</strong></p>
<pre><code class="lang-pgsql"><span class="hljs-comment">-- Fetch all favorite movies remotely and join locally</span>
<span class="hljs-keyword">WITH</span> remote_movies <span class="hljs-keyword">AS</span> (
  <span class="hljs-keyword">SELECT</span> ua.user_id, ua.<span class="hljs-keyword">value</span>
  <span class="hljs-keyword">FROM</span> user_attribute ua
  <span class="hljs-keyword">WHERE</span> ua.name = <span class="hljs-string">'favorite_movie'</span>
)
<span class="hljs-keyword">SELECT</span> u.email, rm.<span class="hljs-keyword">value</span>
<span class="hljs-keyword">FROM</span> users u
<span class="hljs-keyword">LEFT JOIN</span> remote_movies rm <span class="hljs-keyword">ON</span> u.id = rm.user_id;
</code></pre>
<p>Now the FDW issues one query to fetch all relevant attributes, and the join is done locally in one pass.</p>
<h3 id="heading-crossserver-joins-without-materialization">Cross‑server joins without materialization</h3>
<p><strong>Anti‑pattern:</strong></p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">SELECT</span> *
<span class="hljs-keyword">FROM</span> remote_db1.orders o
<span class="hljs-keyword">JOIN</span> remote_db2.customers c <span class="hljs-keyword">ON</span> o.customer_id = c.id;
</code></pre>
<p>This is not pushable because the two tables are on different foreign servers. Postgres will fetch orders and customers separately and join them locally. If orders have 1 million rows and customers have 50,000 rows, you will transfer 1.05 million rows.</p>
<p><strong>Better:</strong> Replicate or materialize one side on the other server (or locally) before joining. For example, create a materialized view m_customers on remote_db1 containing just the id and name of the customers you need, then join orders and m_customers on the same server. Alternatively, copy customers into a temporary table on the local server and join there.</p>
<h3 id="heading-complex-expressions-on-join-keys">Complex expressions on join keys</h3>
<p><strong>Anti‑pattern:</strong></p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">SELECT</span> *
<span class="hljs-keyword">FROM</span> remote_table a
<span class="hljs-keyword">JOIN</span> remote_table b <span class="hljs-keyword">ON</span> CAST(a.key <span class="hljs-keyword">AS</span> <span class="hljs-type">text</span>) = b.key_text;
</code></pre>
<p>Casting a numeric key to text prevents pushdown. The remote server cannot use indexes and must return both tables. The local server performs the join and cast.</p>
<p><strong>Better:</strong> Align your schemas so that the join columns use the same type. If you cannot change the schema, create a computed column on the remote server with the appropriate type and use it in the join.</p>
<h3 id="heading-ignoring-collation-and-type-mismatches">Ignoring collation and type mismatches</h3>
<p><strong>Anti‑pattern:</strong></p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">SELECT</span> *
<span class="hljs-keyword">FROM</span> remote_table
<span class="hljs-keyword">WHERE</span> citext_col = <span class="hljs-string">'abc'</span>;
</code></pre>
<p>If the remote server doesn’t have the citext extension installed, the comparison semantics will differ, and the FDW will refuse to ship the filter. This appears harmless until you see the plan and realize all rows were fetched.</p>
<p><strong>Better:</strong> Install the same extensions and collations on the remote server, or convert the column to a base type like text on both sides.</p>
<h2 id="heading-extending-tuning-calibrating-cost-models">Extending Tuning: Calibrating Cost Models</h2>
<p>Earlier, we discussed <code>fetch_size</code>, <code>use_remote_estimate</code>, and the cost knobs. This section expands on how to use them strategically.</p>
<h3 id="heading-balancing-fetch-size-and-memory">Balancing fetch size and memory</h3>
<p><code>fetch_size</code> controls how many rows the FDW asks for in each round trip <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=">[9]</a>. Think of it as the batch size. The default (100) works well for small result sets. If you expect to retrieve tens of thousands of rows, a higher fetch size reduces the overhead of many network requests. But there are trade‑offs:</p>
<ul>
<li><p><strong>Memory consumption:</strong> Each foreign scan buffers rows until they are consumed. A huge fetch size (for example, 10,000) may allocate more memory than you expect, especially when multiple scans run concurrently. Monitor memory usage as you increase this setting.</p>
</li>
<li><p><strong>Latency hiding:</strong> If network latency is high, overlapping network requests with local processing can hide some latency. But <code>postgres_fdw</code> does not pipeline multiple fetches – it waits for one batch before requesting the next. This means that a larger batch size reduces the number of waits, but cannot overlap them. If you operate across data centers, consider using a connection pooler or caching layer instead of just increasing fetch_size.</p>
</li>
</ul>
<h3 id="heading-remote-estimates-vs-local-estimates">Remote estimates vs. local estimates</h3>
<p>The planner uses statistics to estimate how many rows each node will produce, which in turn influences join order. When <code>use_remote_estimate</code> is false (the default), the planner guesses based on local stats collected by <code>ANALYZE</code> on the foreign table. This can be wrong if the remote table has a different distribution than the local sample, or if the table has changed since the last <code>ANALYZE</code>.</p>
<p>Setting <code>use_remote_estimate</code> to true instructs the FDW to run <code>EXPLAIN</code> on the remote server during planning to obtain row counts and cost estimates <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=This%20option%2C%20which%20can%20be,false">[3]</a>. This can improve join ordering, especially when joining multiple foreign tables or mixing local and foreign tables. The downside is increased planning time because each remote estimate runs an extra query.</p>
<p>In practice:</p>
<ul>
<li><p>Enable <code>use_remote_estimate</code> on queries with complex joins where the planner picks obviously wrong join orders. If enabling it improves the plan, consider leaving it on for that server or table.</p>
</li>
<li><p>Use <code>ANALYZE</code> on foreign tables periodically if your remote data is relatively static. This populates local stats and can avoid the overhead of remote estimates.</p>
</li>
<li><p>Don’t enable <code>use_remote_estimate</code> indiscriminately on simple lookups. The cost of additional round-trip remote flights may outweigh the benefit.</p>
</li>
</ul>
<h3 id="heading-tuning-cost-parameters">Tuning cost parameters</h3>
<p><code>fdw_startup_cost</code> and <code>fdw_tuple_cost</code> control how much the planner thinks it costs to start a foreign scan and fetch each row <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=This%20option%2C%20which%20can%20be,false">[3]</a>. If these are too low, the planner may choose a nested loop that generates many small remote calls. If they are too high, the planner might avoid remote scans even when they are efficient.</p>
<p>You can adjust these parameters based on empirical measurement:</p>
<ul>
<li><p>Increase <code>fdw_startup_cost</code> to discourage the planner from using nested loops that call the remote table repeatedly. You might set it to the average cost of a round-trip remote.</p>
</li>
<li><p>Increase <code>fdw_tuple_cost</code> if network bandwidth is limited or expensive. This indicates to the planner that each remote row incurs higher fetch costs than a local row. The planner will prefer plans that filter early on the remote side.</p>
</li>
</ul>
<p>Always adjust these settings gradually and observe the effect on the plan. Keep separate settings per foreign server if network conditions differ.</p>
<h3 id="heading-when-to-analyze-foreign-tables">When to analyze foreign tables</h3>
<p>Running <code>ANALYZE</code> on a foreign table collects sample statistics by pulling a subset of rows from the remote server. This helps the planner estimate row counts when <code>use_remote_estimate</code> is off. It also helps decide whether to use an index on the remote side. You should analyze foreign tables when:</p>
<ul>
<li><p>The remote table is large and static, and you want accurate local estimates without the overhead of remote estimates.</p>
</li>
<li><p>You have just defined a foreign table, and the default stats are empty.</p>
</li>
<li><p>You changed the extensions allow‑list to enable more pushdown and want the planner to see the effect.</p>
</li>
</ul>
<p>Conversely, if the remote data changes constantly, <code>ANALYZE</code> results will quickly become stale. In that case, rely on use_remote_estimate instead.</p>
<h2 id="heading-further-case-studies-and-practical-examples">Further Case Studies and Practical Examples</h2>
<p>The Keycloak coverage example is not the only place where pushdown matters. The following scenarios illustrate other patterns you may encounter.</p>
<h3 id="heading-reporting-on-a-sharded-logging-system">Reporting on a sharded logging system</h3>
<p>Imagine you store application logs across multiple shards, each a separate Postgres database. You want to produce a report of the number of error logs per service per day.</p>
<p>A naïve approach might join all shards in one query:</p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">SELECT</span> shard, service, date_trunc(<span class="hljs-string">'day'</span>, log_time) <span class="hljs-keyword">AS</span> day, COUNT(*)
<span class="hljs-keyword">FROM</span> shard1.logs
<span class="hljs-keyword">UNION</span> <span class="hljs-keyword">ALL</span>
<span class="hljs-keyword">SELECT</span> shard, service, date_trunc(<span class="hljs-string">'day'</span>, log_time) <span class="hljs-keyword">AS</span> day, COUNT(*)
<span class="hljs-keyword">FROM</span> shard2.logs
...;
</code></pre>
<p>This approach will fetch all log rows to the local server and aggregate them locally. A better solution is to push the grouping to each shard:</p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">SELECT</span> shard, service, day, sum(count)
<span class="hljs-keyword">FROM</span> (
  <span class="hljs-keyword">SELECT</span> <span class="hljs-number">1</span> <span class="hljs-keyword">AS</span> shard, service, date_trunc(<span class="hljs-string">'day'</span>, log_time) <span class="hljs-keyword">AS</span> day, COUNT(*) <span class="hljs-keyword">AS</span> count
  <span class="hljs-keyword">FROM</span> shard1.logs
  <span class="hljs-keyword">WHERE</span> log_time &gt;= <span class="hljs-meta">$1</span> <span class="hljs-keyword">AND</span> log_time &lt; <span class="hljs-meta">$2</span>
  <span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> service, day
  <span class="hljs-keyword">UNION</span> <span class="hljs-keyword">ALL</span>
  <span class="hljs-keyword">SELECT</span> <span class="hljs-number">2</span> <span class="hljs-keyword">AS</span> shard, service, date_trunc(<span class="hljs-string">'day'</span>, log_time) <span class="hljs-keyword">AS</span> day, COUNT(*)
  <span class="hljs-keyword">FROM</span> shard2.logs
  <span class="hljs-keyword">WHERE</span> log_time &gt;= <span class="hljs-meta">$1</span> <span class="hljs-keyword">AND</span> log_time &lt; <span class="hljs-meta">$2</span>
  <span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> service, day
  ...
) x
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> shard, service, day;
</code></pre>
<p>Here, each foreign server returns a small set of aggregated rows instead of raw logs. The outer aggregation sums across shards. This pattern generalizes: push grouping and filtering to the remote side, then combine locally.</p>
<h3 id="heading-combining-remote-and-local-data-for-analytics">Combining remote and local data for analytics</h3>
<p>Suppose you have a local table <code>users</code> and a remote table <code>orders</code>. You want to compute the average order amount per user segment. A naïve query might look like:</p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">SELECT</span> u.segment, AVG(o.amount)
<span class="hljs-keyword">FROM</span> users u
<span class="hljs-keyword">JOIN</span> orders o <span class="hljs-keyword">ON</span> o.user_id = u.id
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> u.segment;
</code></pre>
<p>This is a local join driving a remote nested loop. The better approach is to aggregate orders remotely by user_id and join on the small result:</p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">WITH</span> remote_totals <span class="hljs-keyword">AS</span> (
  <span class="hljs-keyword">SELECT</span> user_id, SUM(amount) <span class="hljs-keyword">AS</span> total, COUNT(*) <span class="hljs-keyword">AS</span> n
  <span class="hljs-keyword">FROM</span> orders
  <span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> user_id
)
<span class="hljs-keyword">SELECT</span> u.segment, AVG(rt.total / rt.n)
<span class="hljs-keyword">FROM</span> users u
<span class="hljs-keyword">JOIN</span> remote_totals rt <span class="hljs-keyword">ON</span> u.id = rt.user_id
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> u.segment;
</code></pre>
<p>This pushes the heavy aggregation to the remote and transfers only one row per user. The local join then groups by segment. As with other examples, the key is to reduce remote rows before they cross the network.</p>
<h3 id="heading-avoiding-pushdown-for-correctness">Avoiding pushdown for correctness</h3>
<p>There are legitimate cases where you should <em>prevent</em> pushdown because of semantic differences. Postgres allows you to do this by adding <code>OFFSET 0</code> or wrapping the foreign table in a CTE.</p>
<p>For example, if a built‑in function behaves differently on the remote due to a version mismatch, you can force local evaluation:</p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">WITH</span> local_eval <span class="hljs-keyword">AS</span> (<span class="hljs-keyword">SELECT</span>  <span class="hljs-keyword">FROM</span> remote_table)  <span class="hljs-comment">-- CTE prevents pushdown</span>
<span class="hljs-keyword">SELECT</span> 
<span class="hljs-keyword">FROM</span> local_eval
<span class="hljs-keyword">WHERE</span> some_complex_expression(local_eval.col) &gt; <span class="hljs-number">0</span>;
</code></pre>
<p>Alternatively, a <code>WHERE</code> clause like <code>random() &lt; 0.1</code> will not push down because <code>random()</code> is volatile – you don't need to force it. But adding <code>OFFSET 0</code> is a simple hack that prevents any pushdown:</p>
<pre><code class="lang-pgsql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> remote_table <span class="hljs-keyword">OFFSET</span> <span class="hljs-number">0</span>;
</code></pre>
<p>Knowing how to disable pushdown intentionally helps you debug. If a query returns different results when pushdown occurs, suspect type/collation mismatches or remote session settings <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=In%20the%20remote%20sessions%20opened,their%20expected%20search%20path%20environment">[4]</a>.</p>
<h2 id="heading-monitoring-diagnostics-and-regression-testing">Monitoring, Diagnostics, and Regression Testing</h2>
<p>Monitoring doesn't end at counting remote rows. To make pushdown reliable in production, you need to set up mechanisms to detect regressions and gather evidence when performance changes.</p>
<h3 id="heading-automate-explain-regression-tests">Automate EXPLAIN regression tests</h3>
<p>In addition to unit tests and integration tests, you can add tests that assert the shape of your plans. For instance, if a mission‑critical report must always push down a <code>WHERE</code> clause, you can write a test that runs <code>EXPLAIN (VERBOSE)</code> and checks that the Remote SQL contains the filter. You might even parse loops and assert that it is 1. When a developer inadvertently adds a non‑immutable function or changes a join, the test will fail. This is akin to snapshot testing for SQL.</p>
<h3 id="heading-monitor-pgstatstatements-across-servers">Monitor pg_stat_statements across servers</h3>
<p>Enable <code>pg_stat_statements</code> on both the local and remote servers. On the local side, track the total time, planning time, and rows for each FDW query. On the remote side, track which queries are being executed.</p>
<p>Look for outliers: a query whose remote calls spike or whose average remote rows jump from hundreds to thousands. Those are early signs of pushdown failure.</p>
<h3 id="heading-log-remote-sql-with-autoexplain">Log remote SQL with auto_explain</h3>
<p>Setting <code>auto_explain.log_min_duration_statement</code> (for example, to 500ms) causes Postgres to automatically log slow queries with their plans. Combine this with <code>auto_explain.log_verbose = true</code> and <code>auto_explain.log_nested_statements = true</code> to capture remote SQL as well. When a federated query slows down, the log will show you exactly what remote SQL was executed and how often. This is invaluable in production, where you cannot always run EXPLAIN interactively.</p>
<h3 id="heading-use-connection-pooling-and-prepare-statements">Use connection pooling and prepare statements</h3>
<p><code>postgres_fdw</code> maintains a connection pool keyed on the user mapping. It reuses connections between queries, but you can also use connection pooling at the network level (for example, pgbouncer or pgcat).</p>
<p>Keeping connections warm reduces the startup cost, as captured by <code>fdw_startup_cost</code>. Meanwhile, preparing statements on the remote server (via <code>PREPARE</code> and <code>EXECUTE</code>) can save parse time when the same remote SQL is executed frequently. <code>postgres_fdw</code> can use server‑side prepared statements for parameterized scans.</p>
<h3 id="heading-regression-testing-after-version-upgrades">Regression testing after version upgrades</h3>
<p>Every major Postgres release brings improvements to postgres_fdw pushdown semantics. But new releases also change planner heuristics and remote SQL generation. After an upgrade, rerun your key queries with EXPLAIN (VERBOSE), compare the Remote SQL, and benchmark them.</p>
<p>In some cases, a release may push down something previously local, revealing a latent type mismatch or a function difference. In other cases, pushdown may be withheld due to a new rule. Don’t assume that an upgrade automatically improves performance – test it.</p>
<h2 id="heading-extended-guidelines-for-advanced-dbas">Extended Guidelines for Advanced DBAs</h2>
<p>To close this handbook, here are consolidated guidelines distilled from the previous sections. They go beyond simple bullet points to capture nuances. Keep them handy for reference or print them out for your team.</p>
<ol>
<li><p><strong>Respect the FDW safety model.</strong> Immutable functions and built‑in operators are your friends. Anything outside that scope must be explicitly allowed or evaluated locally. Understand which items belong to each category and plan accordingly.</p>
</li>
<li><p><strong>Always read the Remote SQL.</strong> Don’t trust your intuition about what is being pushed down. The Remote SQL string is the only source of truth. It indicates whether a predicate, join, sort, or limit operation is occurring remotely. It also shows parameter placeholders (for example, $1) that correspond to values passed from the local plan.</p>
</li>
<li><p><strong>Reduce before you fetch.</strong> The network is the highest cost. If the remote can reduce rows through filtering, grouping, or limiting, let it. If it cannot, structure your query to enable it. Avoid queries that require pulling large raw tables and processing them locally.</p>
</li>
<li><p><strong>Beware of join order.</strong> The planner sometimes chooses a nested loop with a foreign table as the inner side, resulting in repeated remote calls. Examine loops: if you see a high number, consider rewriting the query or adjusting cost parameters.</p>
</li>
<li><p><strong>Use CTEs strategically.</strong> A CTE can isolate remote scans and let you control whether they are materialized once or inlined. Use <code>MATERIALIZED</code> to avoid repeated remote scans when a CTE is referenced multiple times. Use <code>NOT MATERIALIZED</code> to allow optimizations across CTE boundaries.</p>
</li>
<li><p><strong>Instrument, monitor, iterate.</strong> Good FDW performance is not a one‑off fix. Monitor queries and plans. Use tests to catch regressions. Adjust tuning knobs and indexes as your data or workload changes. Document your reasoning so others can understand why a particular plan is expected.</p>
</li>
<li><p><strong>Educate your team.</strong> Federated queries invite subtle bugs and performance traps. Share the high‑level rules – immutable functions only, cross‑server joins are local, always check remote SQL – so engineers write safer queries by default. A 30‑minute training can save hours of debugging later.</p>
</li>
</ol>
<h2 id="heading-bringing-it-all-together">Bringing it All Together</h2>
<p>This handbook has covered a lot of ground: from the high‑level principle that pushdown is about data movement, to the nitty‑gritty of join conditions and tuning knobs, to troubleshooting steps and case studies. It is intentionally opinionated and personal: these are the patterns and pitfalls encountered in real systems, not abstract guidelines. By sharing specific examples, I hoped to make the rules memorable and show how they interplay with actual workloads.</p>
<p>The goal is not just to tell you what to do, but to show you how to think and problem solve: review the plan, trace data movement, and determine whether the query is doing the heavy work in the right place.</p>
<p>That thinking process, practiced enough times, becomes second nature. When you write a new query, you'll automatically consider whether your predicates are immutable, whether the join can be shipped, and whether you are about to trigger an N+1 pattern. When you review plans, you'll start from the Foreign Scan nodes and remote SQL, not the top‑level node. When you tune, you'll know which knobs to twist and in which order.</p>
<p>Keep experimenting. Use the examples here as starting points. Try different structures in a test environment and measure the difference. The more you play with pushdown, the more comfortable you'll become with its constraints and superpowers.</p>
<p>If this handbook helps you avoid one performance incident or saves you from shipping a broken query, it has done its job. Enjoy exploring the federated world of Postgres.</p>
<h2 id="heading-references">References</h2>
<p><a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=It%20is%20generally%20recommended%20that,differently%20from%20the%20local%20server">[1]</a> <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=">[2]</a> <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=This%20option%2C%20which%20can%20be,false">[3]</a> <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=In%20the%20remote%20sessions%20opened,their%20expected%20search%20path%20environment">[4]</a> <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=functions%20in%20such%20clauses%20must,to%20reduce%20the%20risk%20of">[5]</a> <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=When%20,clauses">[6]</a> <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=">[9]</a> <a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html#:~:text=,Extension">[10]</a> PostgreSQL: Documentation: 18: F.38. postgres_fdw – access data stored in external PostgreSQL servers (<a target="_blank" href="https://www.postgresql.org/docs/current/postgres-fdw.html">https://www.postgresql.org/docs/current/postgres-fdw.html</a>)</p>
<p><a target="_blank" href="https://www.postgresql.org/docs/release/10.0/#:~:text=,Jeevan%20Chalke%2C%20Ashutosh%20Bapat">[7]</a> PostgreSQL: Release Notes (<a target="_blank" href="https://www.postgresql.org/docs/release/10.0/">https://www.postgresql.org/docs/release/10.0/</a>)</p>
<p><a target="_blank" href="https://www.postgresql.org/docs/release/12.0/#:~:text=,Etsuro%20Fujita%29%20%C2%A7%20%C2%A7">[8]</a> PostgreSQL: Release Notes (<a target="_blank" href="https://www.postgresql.org/docs/release/12.0/">https://www.postgresql.org/docs/release/12.0/</a>)</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ A Game Developer’s Guide to Understanding Screen Resolution ]]>
                </title>
                <description>
                    <![CDATA[ Every game developer obsesses over performance, textures, and frame rates, but resolution is the quiet foundation that makes or breaks visual quality.  Whether you are building a pixel-art indie game or a high-fidelity 3D world, understanding how res... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/a-game-developers-guide-to-understanding-screen-resolution/</link>
                <guid isPermaLink="false">691de96a0dec4f292a0f8ff0</guid>
                
                    <category>
                        <![CDATA[ Game Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ optimization ]]>
                    </category>
                
                    <category>
                        <![CDATA[ performance ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Accessibility ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Manish Shivanandhan ]]>
                </dc:creator>
                <pubDate>Wed, 19 Nov 2025 15:59:38 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763567809746/3fb2c926-9602-4765-9ef4-5ea565e0e148.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Every game developer obsesses over performance, textures, and frame rates, but resolution is the quiet foundation that makes or breaks visual quality. </p>
<p>Whether you are building a pixel-art indie game or a high-fidelity 3D world, understanding how resolution works is essential. </p>
<p>It affects how your art assets scale, how your UI appears, and how your game feels on different screens. Yet, many developers still treat resolution as a simple number instead of a design decision.</p>
<p>Let’s learn what resolutions are and why it matters for game developers. </p>
<h2 id="heading-what-we-will-cover">What we will Cover</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-resolution-really-means">What Resolution Really Means</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-evolution-of-resolution-in-gaming">The Evolution of Resolution in Gaming</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-dpi-scaling-and-texture-clarity">DPI, Scaling, and Texture Clarity</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-resolution-vs-performance">Resolution vs. Performance</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-aspect-ratio-and-display-diversity">Aspect Ratio and Display Diversity</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-art-of-testing-in-4k-and-hdr">The Art of Testing in 4K and HDR</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-preparing-for-next-gen-displays">Preparing for Next-Gen Displays</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-resolution-really-means">What Resolution Really Means</h2>
<p>Resolution defines how many pixels a screen can display horizontally and vertically.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763470514266/2ba4689a-6e8d-423d-8da7-694bf7bc6d9e.png" alt="Screen Resolution Sizes" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>A monitor labelled 1920x1080 has 1920 pixels across and 1080 down, which equals over two million pixels in total. More pixels mean more visual detail but also more rendering work for the GPU.</p>
<p>In game development, that tradeoff is constant. Rendering at higher resolutions improves clarity but reduces frame rates unless your code and assets are optimized. </p>
<p>Many developers solve this by offering resolution scaling options in their games, letting players balance visual quality and performance.</p>
<p>It’s also important to distinguish between screen size and resolution. A 27-inch monitor and a 15-inch laptop can both run at 1080p, but the larger display will have bigger, less dense pixels. </p>
<p>This is where pixel density comes in. High-density displays pack more pixels per inch, creating smoother edges and sharper textures even at the same resolution.</p>
<h2 id="heading-the-evolution-of-resolution-in-gaming">The Evolution of Resolution in Gaming</h2>
<p>Games have evolved alongside display technology. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763514379811/7a5bef4e-5441-4b40-99cb-3d925865ac87.jpeg" alt="Gameplay Resolution" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Early consoles ran at 240p, then 480p during the SD era. The jump to HD with 720p and 1080p transformed game visuals. Suddenly, developers had to think about anti-aliasing, texture resolution, and UI scaling in new ways.</p>
<p>Today, 4K and HDR have become the standard for modern consoles and PCs. Developers now design with higher fidelity in mind, baking in lighting systems, shaders, and art pipelines that scale up to Ultra HD. </p>
<p>That’s why testing on different display resolutions isn’t just good practice, it’s critical for consistent player experience.</p>
<p>If you want to see how your game performs on large high-resolution displays, try testing it on a modern TV for PS5. These screens are optimized for 4K and 120Hz refresh rates, giving you a realistic look at how your game will appear in a living-room setup. </p>
<p>They also help you spot UI scaling issues, frame pacing problems, and HDR color mismatches that might go unnoticed on a typical monitor.</p>
<h2 id="heading-dpi-scaling-and-texture-clarity">DPI, Scaling, and Texture Clarity</h2>
<p>For web developers, <a target="_blank" href="https://en.wikipedia.org/wiki/Dots_per_inch">DPI</a> mostly affects how images scale. But for game developers, DPI connects directly to texture resolution and how art assets are perceived at different screen sizes. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763470672635/57795a33-7700-4aee-8dd4-aceb8b71dd49.jpeg" alt="DPI Levels" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>A sprite that looks crisp on a 1080p monitor might appear tiny or blurry on a 4K display if not properly scaled. Engines like <a target="_blank" href="https://www.freecodecamp.org/news/game-development-for-beginners-unity-course/">Unity</a> and Unreal handle this with dynamic scaling options, but understanding the underlying math helps. </p>
<p>When your display density doubles, each asset needs four times as many pixels to appear at the same size and sharpness. If you do not plan for this, your carefully crafted textures might look soft or misaligned on higher-resolution displays.</p>
<p>This is why UI systems in modern engines rely on resolution-independent units. In Unity, Canvas Scaler helps ensure your interface looks the same on every device. In Unreal, DPI scaling rules allow developers to maintain consistent HUD layouts. Getting this right means your game remains legible on everything from handhelds to 8K TVs.</p>
<h2 id="heading-resolution-vs-performance">Resolution vs Performance</h2>
<p>The biggest cost of higher resolution is GPU load. Rendering in 4K means pushing four times as many pixels as 1080p. Without proper optimization, frame rates can drop sharply. </p>
<p>That’s why many <a target="_blank" href="https://en.wikipedia.org/wiki/AAA_%28video_game_industry%29">AAA games</a> use resolution scaling techniques like temporal upsampling or DLSS. These methods render frames at a lower resolution and then use AI or interpolation to upscale them without losing clarity.</p>
<p>As a developer, you should test your game across multiple resolutions and aspect ratios. This helps ensure your render pipeline, shaders, and assets adapt smoothly. Tools like <a target="_blank" href="https://developer.nvidia.com/nsight-systems">NVIDIA Nsight</a> or Unreal’s built-in profiler show how resolution affects frame time and GPU usage.</p>
<p>If your game includes video content or cinematic sequences, also remember that video compression behaves differently at higher resolutions. Encoding 4K video requires significantly more bandwidth and storage, which can affect your build size and performance during playback.</p>
<h2 id="heading-aspect-ratio-and-display-diversity">Aspect Ratio and Display Diversity</h2>
<p>Aspect ratio determines the shape of the display.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763476458560/52decf37-c4f4-4927-96b8-1c6fd9be074c.jpeg" alt="Aspect Ratios" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Most modern games target 16:9, but 21:9 ultrawide and 32:9 super-ultrawide displays are becoming more popular. Developers must ensure their camera framing and UI layouts adapt accordingly.</p>
<p>When a game is locked to one ratio, black bars or stretching can occur. To fix this, adjust your camera’s field of view dynamically or provide safe viewport settings.</p>
<p>Engines like Unreal let you script these adjustments easily, while Unity’s Cinemachine system handles FOV scaling automatically.</p>
<p>Even TVs now vary in aspect ratio capabilities, especially with new mini LED and OLED technologies. Testing across multiple ratios ensures your game looks balanced and cinematic on every screen.</p>
<h2 id="heading-the-art-of-testing-in-4k-and-hdr">The Art of Testing in 4K and HDR</h2>
<p>4K and HDR introduce new layers of visual complexity. HDR displays show a wider range of brightness and color depth, which means lighting and textures can look completely different compared to SDR monitors. To handle this, calibrate your color grading pipeline and use tone mapping tools within your engine.</p>
<p>When working with HDR assets, always test your output on real hardware. Emulators and monitors often fail to reproduce true HDR contrast. A proper HDR-certified TV helps you identify overexposure, color clipping, and banding issues before release.</p>
<h2 id="heading-preparing-for-next-gen-displays">Preparing for Next-Gen Displays</h2>
<p>The display industry continues to evolve fast. 8K and high refresh rate panels are already entering mainstream markets. </p>
<p>For developers, this means thinking ahead. Designing scalable rendering systems, supporting dynamic resolution, and maintaining flexible UI layouts are now essential parts of modern game design.</p>
<p>As displays get sharper, player expectations rise too. Textures, shaders, and post-processing all need to support higher levels of detail without compromising performance. By understanding how resolution interacts with your pipeline, you can future-proof your games for years to come.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Resolution is more than a number on a settings menu. It is a design constraint, a performance factor, and a creative opportunity. As a game developer, mastering resolution helps you build experiences that look sharp, play smoothly, and scale across every device.</p>
<p>The next time you polish your textures or fine-tune your rendering settings, remember that every pixel counts. Understanding how resolution, scaling, and density interact will not only make your games more beautiful but also more accessible to every player, whether they’re gaming on a laptop, a monitor, or the living-room tv that brings your visuals to life in stunning detail.</p>
<p><em>Hope you enjoyed this article. Find me on</em> <a target="_blank" href="https://linkedin.com/in/manishmshiva"><em>Linkedin</em></a> <em>or</em> <a target="_blank" href="https://manishshivanandhan.com/"><em>visit my website</em></a><em>.</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Optimize a Graphical React Codebase — Optimize d3-zoom and dnd-kit Code ]]>
                </title>
                <description>
                    <![CDATA[ Miro and Figma are online collaborative canvas tools that became very popular during the pandemic. Instead of using sticky notes on a physical wall, you can add a virtual post—and an array of other things—to a virtual canvas. This lets teams collabor... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-optimize-a-graphical-react-codebase/</link>
                <guid isPermaLink="false">68f0e6e94d664bb368c83f39</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ optimization ]]>
                    </category>
                
                    <category>
                        <![CDATA[ performance ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Cedd Burge ]]>
                </dc:creator>
                <pubDate>Thu, 16 Oct 2025 12:36:57 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1760617553059/99fd830f-39a8-4067-9727-e9b35850168d.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Miro and Figma are online collaborative canvas tools that became very popular during the pandemic. Instead of using sticky notes on a physical wall, you can add a virtual post—and an array of other things—to a virtual canvas. This lets teams collaborate virtually in ways that feel familiar from the physical world.</p>
<p>I previously wrote an article showing <a target="_blank" href="https://www.freecodecamp.org/news/how-to-create-a-figma-miro-style-canvas-with-react-and-typescript/">how to create a Figma/Miro Clone in React and TypeScript</a>. The <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-part-3">code in the article</a> was designed to be as easy to understand, and in this article, we’re going to optimize it. The code used <a target="_blank" href="https://dndkit.com/">DndKit</a> for dragging and dropping, and <a target="_blank" href="https://github.com/d3/d3-zoom">D3 Zoom</a> for panning and zooming. There were four components (<code>App</code>, <code>Canvas</code>, <code>Draggable</code> and <code>Addable</code>), and about 250 lines of code. You do not need to read the original article to understand this one.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759863968693/38c8784c-47c8-46e0-9f06-0567c0ebf668.gif" alt="38c8784c-47c8-46e0-9f06-0567c0ebf668" class="image--center mx-auto" width="1026" height="730" loading="lazy"></p>
<p>Standard optimizations such as <code>useCallback</code>, <code>memo</code>, and similar made it about twice as fast when dragging, but made no difference for panning and zooming. More creative/intensive optimizations made it about ten times as fast in most cases.</p>
<p>You can see the optimized code on <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised">GitHub</a> and there is a <a target="_blank" href="https://ceddlyburge.github.io/react-figma-miro-canvas-optimised/">live demo on GitHub pages</a> to test out the speed with 100,000 cards.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-how-to-measure-performance-in-react-apps">How to Measure Performance in React Apps</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-investigate-the-performance">How to Investigate the performance</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-optimize-panning-and-zooming-the-canvas">How to Optimize Panning and Zooming the Canvas</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-optimize-dragging-cards-around-the-canvas">How to Optimize Dragging Cards Around the Canvas</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-results">Results</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-summary">Summary</a></p>
</li>
</ul>
<h2 id="heading-how-to-measure-performance-in-react-apps">How to Measure Performance in React Apps</h2>
<p>There are three common ways to measure performance in React Apps</p>
<ul>
<li><p><a target="_blank" href="https://chromewebstore.google.com/detail/react-developer-tools">React Dev Tools profiler</a></p>
</li>
<li><p><a target="_blank" href="https://developer.chrome.com/docs/devtools/performance/overview">Chrome Dev Tools profiler</a>, especially using <a target="_blank" href="https://www.debugbear.com/blog/favourite-devtools-features-in-2025#add-custom-tracks-to-performance-recordings">custom tracks</a></p>
</li>
<li><p><a target="_blank" href="https://react.dev/reference/react/Profiler">Profiler component</a></p>
</li>
</ul>
<p>These tools are all great, but none of them are quite the right fit in this case. In most codebases, the time spent executing JavaScript code (both our code and that of the React framework) is the primary issue. However, after all your code has run and React has updated the Dom, the browser still has a lot of work to do:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1760025146277/7ae9ef2a-e248-491b-a07d-c674694d3fa9.png" alt="7ae9ef2a-e248-491b-a07d-c674694d3fa9" class="image--center mx-auto" width="898" height="219" loading="lazy"></p>
<p>In this case, this browser layout and rendering time was significant, and is not accounted for by the React profiling.</p>
<p>You can use custom tracks in the Chrome dev tools profiler, but it is very cumbersome to use.</p>
<p>For us, the JavaScript <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Performance">performance API</a> is the best option, which gives results that are closer to those experienced by the user, and is relatively easy to use.</p>
<p>First, we make a call to <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Performance/mark"><code>performance.mark</code></a> in the event handler that starts the action, with a string to describe the time point. For example, when starting a zoom or pan operation:</p>
<pre><code class="lang-javascript">zoomBehavior.on(<span class="hljs-string">"start"</span>, <span class="hljs-function">() =&gt;</span> {
    performance.mark(<span class="hljs-string">'zoomingOrPanningStart'</span>);
}
</code></pre>
<p>Then, in a <code>useEffect</code> hook, we call <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Performance/mark"><code>performance.mark</code></a> again, and call <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Performance/measure"><code>performance.measure</code></a> to calculate the time between the two points:</p>
<pre><code class="lang-javascript">useEffect(<span class="hljs-function">() =&gt;</span> {
    performance.mark(<span class="hljs-string">'zoomingOrPanningEnd'</span>);
    performance.measure(<span class="hljs-string">'zoomingOrPanning'</span>, <span class="hljs-string">'zoomingOrPanningStart'</span>, <span class="hljs-string">'zoomingOrPanningEnd'</span>);
});
</code></pre>
<p>The <a target="_blank" href="https://react.dev/reference/react/useEffect">React docs</a> states that <code>useEffect</code> usually fires after the browser has painted the updated screen, which is what we want.</p>
<p>This isn't perfect, and will vary depending on the machine specifications, and what else the machine is doing at the time, but it was good enough to verify which optimizations worked best. It is possible to go further if you need to. For example, using <a target="_blank" href="https://filiphric.com/testing-frontend-performance-with-cypress">Cypress to automate and profile scenarios</a>, potentially running many times to get a good mean, or using <a target="_blank" href="https://www.browserstack.com/guide/performance-testing-with-cypress">Browserstack to test on a variety of devices</a>.</p>
<h2 id="heading-how-to-investigate-the-performance">How to Investigate the Performance</h2>
<p>Most of the investigation involved using the <a target="_blank" href="https://chromewebstore.google.com/detail/react-developer-tools">React Dev Tools profiler</a> to record profiles of user interactions.</p>
<p>The performance data shows how many commits there were in the profile, and how long each one took, which is a great way to see if there are too many commits.</p>
<p>Each commit displays a flame chart showing which components rendered and why they re-rendered. This makes it much easier to find ways to avoid the re-rendering, and to check that memoization strategies are working as expected. This does have some caveats though. It often says <a target="_blank" href="https://github.com/facebook/react/issues/19732">'The parent component rendered'</a>, which is misleading default text for when it doesn’t understand what happened (and is often due to a change in a parent context). It also says things like 'hook 9 changed', which makes it time consuming to work out exactly which hook changed.</p>
<p>The flame chart also shows how long each component took to render. This helps target problem components that we need to focus on.</p>
<h2 id="heading-how-to-optimize-panning-and-zooming-the-canvas">How to Optimize Panning and Zooming the Canvas</h2>
<p>The original <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-part-3/blob/main/src/Canvas.tsx">Canvas</a> element used the CSS transform <code>translate3d(x, y, k)</code> to pan and zoom the canvas. This works, but it doesn't scale child elements, so when the zoom changes, all the cards on the canvas have to be re-rendered with a new CSS transform for the new zoom level (<a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-part-3/blob/97935f5b03ecff2f0732f6138e173a0c5e71a1ed/src/Draggable.tsx#L31"><code>scale(${canvasTransform.k})</code></a>).</p>
<pre><code class="lang-javascript"> &lt;div
    ...
    className=<span class="hljs-string">"canvas"</span>
    style={{
        <span class="hljs-attr">transform</span>: <span class="hljs-string">`translate3d(<span class="hljs-subst">${transform.x}</span>px, <span class="hljs-subst">${transform.y}</span>px, <span class="hljs-subst">${transform.k}</span>px)`</span>,
        ...
    }}&gt;
    ...
&lt;/div&gt;
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>
    <span class="hljs-attr">className</span>=<span class="hljs-string">"card"</span>
    <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span>
        <span class="hljs-attr">...</span>
        <span class="hljs-attr">transform:</span> `<span class="hljs-attr">scale</span>(${<span class="hljs-attr">canvasTransform.k</span>})`,
    }}&gt;</span>
    ...
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<p>I changed this to use <code>translateX(x) translateY(y) scale(k)</code>, which has the same effect, but does scale child elements. This way, when the zoom changes, none of the cards will be re-rendered (the <code>style</code> of the <code>card</code> component no longer uses the <code>canvasTransform.k</code>).</p>
<pre><code class="lang-javascript"> &lt;div
    ...
    className=<span class="hljs-string">"canvas"</span>
    style={{
        <span class="hljs-attr">transform</span>: <span class="hljs-string">`translateX(<span class="hljs-subst">${transform.x}</span>px) translateY(<span class="hljs-subst">${transform.y}</span>px) scale(<span class="hljs-subst">${transform.k}</span>)`</span>,
        ...
    }}&gt;
    ...
&lt;/div&gt;
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>
    <span class="hljs-attr">className</span>=<span class="hljs-string">"card"</span>
    <span class="hljs-attr">...</span>
&lt;/<span class="hljs-attr">div</span>&gt;</span></span>
</code></pre>
<p>The <code>Canvas</code> still needed to re-render whenever the pan or zoom changed, and it is possible to prevent this with <code>useRef</code>, and updating the CSS transform with direct JavaScript Dom manipulation in the <a target="_blank" href="https://d3js.org/d3-zoom">d3-zoom</a> event handler. This doesn’t make a significant improvement to the performance though, and is a definite hack, so the trade off is not worthwhile.</p>
<p>Both zooming and panning get a bit slower when the canvas is zoomed very far out and there are (a lot) more cards visible on the screen, just due to the browser having to render them all. It's still workable at 100,000 cards though. There are things you can do about this. An easy option is limiting the maximum zoom extent. This is a functional change, so potentially something that doesn’t meet requirements, but it is easy to do in d3-zoom using <a target="_blank" href="https://d3js.org/d3-zoom#zoom_scaleExtent"><code>scaleExtent</code></a>:</p>
<pre><code class="lang-javascript">zoom&lt;HTMLDivElement&gt;().scaleExtent([<span class="hljs-number">0.1</span>, <span class="hljs-number">100</span>])
</code></pre>
<p>Another option is to create a bitmap for very low zoom levels and render that as a single element. This may be difficult, but it means that there will be no change to the functionality.</p>
<h2 id="heading-how-to-optimize-dragging-cards-around-the-canvas">How to Optimize Dragging Cards Around the Canvas</h2>
<h3 id="heading-starting-a-drag"><strong>Starting a drag</strong></h3>
<p>The <code>useDraggable</code> hook from <code>DndContext</code> causes some re-renders when starting a drag operation.</p>
<p>It is possible to improve this by changing the <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/Draggable.tsx"><code>Draggable</code></a> component to just have this hook (and the things that use it) and having a <code>DraggableInner</code> component for everything else (inside a <code>memo</code>). This works well for reducing the re-renders, in that the <code>DraggableInner</code> almost never get re-rendered, and improves the speed of starting a drag operation. However, it was still fairly slow, and the time was all under the <code>DndContext</code>.</p>
<p>A better option is to create a new <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/NonDraggable.tsx"><code>NonDraggable</code></a> component, that looks exactly like the <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/Draggable.tsx"><code>Draggable</code></a> component, but does not hook up with <code>DndContext</code>. These cards are shown on the Canvas, and have an <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/56d0c8256350ef3a0d8f50cc442305bd6c9d03c1/src/NonDraggable.tsx#L10"><code>onMouseEnter</code></a> event, to swap in the <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/Draggable.tsx"><code>Draggable</code></a> component for the active card, so that dragging continued to work.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> onMouseEnter = useCallback(<span class="hljs-function">() =&gt;</span> {
    setHoverCard(card);
}, []);
</code></pre>
<p>This works well, and significantly improves the speed when starting a drag operation, but it was still quite slow with large numbers of cards. Nearly nothing was getting re-rendered, but there is still a time cost to when using <code>memo</code>, as it needs to check whether components have changed.</p>
<p>To fix this, we create an <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/AllCards.tsx"><code>AllCards</code></a> component, that contains all the cards on the canvas as <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/NonDraggable.tsx"><code>NonDraggable</code></a> components. Because it always renders all the cards, it nearly never needs to be re-rendered, and it is used with <code>memo</code>. So instead of each individual card using a <code>memo</code> (with the associated time cost), there is now just one component using a <code>memo</code>. To make it so that the dragging still works, the active <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/Draggable.tsx"><code>Draggable</code></a> component is rendered on top, obscuring the <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/NonDraggable.tsx"><code>NonDraggable</code></a> component beneath it. There is also a <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/Cover.tsx"><code>Cover</code></a> component beneath that, so that when the <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/Draggable.tsx"><code>Draggable</code></a> component is dragged away, the <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/NonDraggable.tsx"><code>NonDraggable</code></a> component underneath remains hidden.</p>
<p>Original code, where each card is a <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/Draggable.tsx"><code>Draggable</code></a> component:</p>
<pre><code class="lang-javascript">&lt;DndContext ...&gt;
    {cards.map(<span class="hljs-function">(<span class="hljs-params">card</span>) =&gt;</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Draggable</span> <span class="hljs-attr">card</span>=<span class="hljs-string">{card}</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{card.id}</span> <span class="hljs-attr">canvasTransform</span>=<span class="hljs-string">{transform}</span> /&gt;</span></span>
    ))}
&lt;/DndContext&gt;
</code></pre>
<p>Optimized code, where the <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/AllCards.tsx"><code>AllCards</code></a> component renders all the cards as <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/NonDraggable.tsx"><code>NonDraggable</code></a> components, and then a <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/Cover.tsx"><code>Cover</code></a> and a <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/Draggable.tsx"><code>Draggable</code></a> component for the active card.</p>
<pre><code class="lang-javascript">&lt;AllCards cards={cards} setHoverCard={setHoverCard} /&gt;
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">DndContext</span> <span class="hljs-attr">...</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Cover</span> <span class="hljs-attr">card</span>=<span class="hljs-string">{hoverCard}</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Draggable</span> <span class="hljs-attr">card</span>=<span class="hljs-string">{hoverCard}</span> <span class="hljs-attr">canvasTransform</span>=<span class="hljs-string">{transform}</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">DndContext</span>&gt;</span></span>
</code></pre>
<p>This works very well. With a low number of cards, the speed is about the same, but with a high numbers of cards, it’s about twenty times faster.</p>
<p>There is now a new potential performance issue with the <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/56d0c8256350ef3a0d8f50cc442305bd6c9d03c1/src/NonDraggable.tsx#L10"><code>onMouseEnter</code></a> event that swaps in the <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/Draggable.tsx"><code>Draggable</code></a> component for the active card, but this just adds two components to the Dom, and is very quick even with large numbers of cards.</p>
<h3 id="heading-finishing-a-drag"><strong>Finishing a drag</strong></h3>
<p>Finishing a drag operation is hard to optimize, as the position of a card changes, and that does need to re-render, which means that the <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/AllCards.tsx"><code>AllCards</code></a> component has to re-render as well.</p>
<p>You can see original code below. Even when using <code>memo</code> with the Draggable component, the end drag operation still takes 2500ms with 100,000 cards, mostly due to the complexity of the <code>Draggable</code> component and its integration with DndKit.</p>
<pre><code class="lang-javascript">&lt;DndContext ...&gt;
    {cards.map(<span class="hljs-function">(<span class="hljs-params">card</span>) =&gt;</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Draggable</span> <span class="hljs-attr">card</span>=<span class="hljs-string">{card}</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{card.id}</span> <span class="hljs-attr">canvasTransform</span>=<span class="hljs-string">{transform}</span> /&gt;</span></span>
    ))}
&lt;/DndContext&gt;
</code></pre>
<p>However, we now use the <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/NonDraggable.tsx"><code>NonDraggable</code></a> components, which all <code>memo</code> successfully, and only the dragged card is re-rendered. There is still a time cost using the <code>memo</code>, and this is the slowest part of the solution, but it leads to an increase in speed to 500ms with 100,000 cards.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> NonDraggable = memo(...)

<span class="hljs-keyword">const</span> AllCards = memo(<span class="hljs-function">(<span class="hljs-params">cards, setHoverCard</span>) =&gt;</span> {
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
        {cards.map((card) =&gt; {
            <span class="hljs-tag">&lt;<span class="hljs-name">NonDraggable</span> <span class="hljs-attr">card</span>=<span class="hljs-string">{card}</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{card.id}</span> <span class="hljs-attr">setHoverCard</span>=<span class="hljs-string">{setHoverCard}</span> /&gt;</span>);
        })}
    <span class="hljs-tag">&lt;/&gt;</span></span>;
});
</code></pre>
<h2 id="heading-results"><strong>Results</strong></h2>
<p>The base unoptimized version started to get slow between 1000 and 5000 cards. Standard optimizations improved this to around 10,000 cards, and the more optimization took it to about 100,000 cards. The trade off is that the code becomes significantly more complicated, which makes it harder to understand and modify, especially for people new to the codebase.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td></td><td></td><td><strong>Pan (ms)</strong></td><td><strong>Zoom (ms)</strong></td><td><strong>Start drag (ms)</strong></td><td><strong>End drag (ms)</strong></td><td><strong>Card hover (ms)</strong></td></tr>
</thead>
<tbody>
<tr>
<td>1000 cards</td><td>Base</td><td>3</td><td>4</td><td>200</td><td>50</td><td>-</td></tr>
<tr>
<td></td><td>Basic optimization</td><td>2</td><td>3</td><td>200</td><td>30</td><td>-</td></tr>
<tr>
<td></td><td>Intensive optimization</td><td>10</td><td>10</td><td>7</td><td>15</td><td>2</td></tr>
<tr>
<td>5000 cards</td><td>Base</td><td>20</td><td>150</td><td>450</td><td>200</td><td>-</td></tr>
<tr>
<td></td><td>Basic optimization</td><td>20</td><td>150</td><td>200</td><td>80</td><td>-</td></tr>
<tr>
<td></td><td>Intensive optimization</td><td>10</td><td>10</td><td>25</td><td>40</td><td>2</td></tr>
<tr>
<td>10,000 cards</td><td>Base</td><td>50</td><td>300</td><td>900</td><td>400</td><td>-</td></tr>
<tr>
<td></td><td>Basic optimization</td><td>50</td><td>300</td><td>400</td><td>180</td><td>-</td></tr>
<tr>
<td></td><td>Intensive optimization</td><td>25</td><td>25</td><td>50</td><td>50</td><td>2</td></tr>
<tr>
<td>50,000 cards</td><td>Base</td><td>1000</td><td>1500</td><td>4000</td><td>1800</td><td>-</td></tr>
<tr>
<td></td><td>Basic optimization</td><td>1000</td><td>1500</td><td>1900</td><td>900</td><td>-</td></tr>
<tr>
<td></td><td>Intensive optimization</td><td>150</td><td>150</td><td>150</td><td>250</td><td>5</td></tr>
<tr>
<td>100,000 cards</td><td>Base</td><td>-</td><td>-</td><td>-</td><td>-</td><td>-</td></tr>
<tr>
<td></td><td>Basic optimization</td><td>3000</td><td>4500</td><td>5000</td><td>2500</td><td>-</td></tr>
<tr>
<td></td><td>Intensive optimization</td><td>150</td><td>250</td><td>300</td><td>500</td><td>15</td></tr>
</tbody>
</table>
</div><h2 id="heading-summary">Summary</h2>
<p>It is unusual to display 100,000 or more items on screen in a standard React App, but in a highly graphical codebase, it becomes much more likely.</p>
<p>With these numbers, the browser rendering engine is likely to take a significant amount of time, so it is best to use the performance API to measure performance, instead of the usual React tools.</p>
<p>Standard React optimization strategies do work and improve the situation, but there is a need to go further, by finding ways to avoid renders, and even to avoid too many <code>memo</code> comparisons.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Key Metrics That Can Make or Break Your Startup ]]>
                </title>
                <description>
                    <![CDATA[ If you’ve built something worth pitching – something more than a fancy hobby with a login screen – you need to know your numbers. Not "I’ll get back to you" know them, know them like you know your co-founder's coffee order. I have seen too many found... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/key-metrics-that-can-make-or-break-your-startup/</link>
                <guid isPermaLink="false">6894eea1ba8a1138e41767a1</guid>
                
                    <category>
                        <![CDATA[ startup ]]>
                    </category>
                
                    <category>
                        <![CDATA[ finance ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Founder ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ideas ]]>
                    </category>
                
                    <category>
                        <![CDATA[ business ]]>
                    </category>
                
                    <category>
                        <![CDATA[ beginner ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Business and Finance  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ metrics ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Productivity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Product Management ]]>
                    </category>
                
                    <category>
                        <![CDATA[ performance ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #reporting ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ycombinator ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Aditya Vikram Kashyap ]]>
                </dc:creator>
                <pubDate>Thu, 07 Aug 2025 18:21:21 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1754590848364/2e68c07e-d5d8-4da7-bc41-3798c991bfbc.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you’ve built something worth pitching – something more than a fancy hobby with a login screen – you need to know your numbers. Not "I’ll get back to you" know them, know them like you know your co-founder's coffee order.</p>
<p>I have seen too many founders who are smart, legit, and ambitious get ghosted by investors simply because they couldn't walk through their unit economics.</p>
<p>It's not personal. It's math.</p>
<p>So here it is: Numbers that will either carry your pitch or quietly kill it, explained by someone who has sat through them time and time again, with examples, and no fluff.</p>
<h3 id="heading-heres-what-well-cover">Here’s what we’ll cover:</h3>
<ul>
<li><p><a class="post-section-overview" href="#heading-1-burn-rate-how-fast-are-you-lighting-your-cash-on-fire">1. Burn Rate: How Fast Are You Lighting Your Cash on Fire?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-2-cash-runway-how-long-before-you-run-out-of-cash">2. Cash Runway: How Long Before You Run Out of Cash?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-3-cac-customer-acquisition-cost-how-much-does-it-cost-to-convince-someone-to-pay-you">3. CAC (Customer Acquisition Cost): How Much Does it Cost to Convince Someone to Pay You?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-4-customer-lifetime-value-ltv-how-much-is-one-customer-worth-over-time">4. Customer Lifetime Value (LTV): How Much is One Customer Worth Over Time?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-5-gross-profit-margin-what-do-you-actually-keep-after-delivering-your-service-or-product">5. Gross Profit Margin: What Do You Actually Keep After Delivering Your Service or Product?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-6-monthly-annual-recurring-revenue-mrr-arr">6. Monthly / Annual Recurring Revenue (MRR / ARR)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-7-churn-rate-how-fast-are-your-users-leaving">7. Churn Rate: How Fast Are Your Users Leaving</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-8-payback-period-how-long-before-you-recover-your-cac">8. Payback Period: How Long Before You Recover Your CAC?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-9-earnings-before-interest-taxes-depreciation-and-amortization-ebitda">9. Earnings Before Interest, Taxes, Depreciation, and Amortization (EBITDA)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-10-valuation-whats-your-company-worth-and-what-supports-that-number">10. Valuation: What’s Your Company Worth – and What Supports that Number?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-real-talk-before-you-close-that-tab">Real Talk Before You Close That Tab</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-real-experience">Real Experience</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-this-matters">Why This Matters</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion-and-final-thoughts">Conclusion and Final Thoughts</a></p>
</li>
</ul>
<h2 id="heading-1-burn-rate-how-fast-are-you-lighting-your-cash-on-fire">1. Burn Rate: How Fast Are You Lighting Your Cash on Fire?</h2>
<p>Burn rate is the speed at which a startup is spending its cash. Basically, how fast are you consuming your venture capital to cover over overhead until you generate positive cash flow from operations? It’s a measure of negative cash flow.</p>
<p>If you’re spending $80K a month to keep the lights on (payroll, AWS, your workspace snacks, and so on), that’s how much cash you’re burning each month. But many startups calculate two different burn rates: gross burn (how much cash you’re spending, ignoring any revenue), and net burn (monthly operating experiences minus any cash you take in each month). Net burn basically measures how fast your cash is shrinking, and it’s often what investors care more about.</p>
<p>Real talk: investors want to know when the plane runs out of fuel before they board. Thats what this metric helps them understand – how fast you’re going through the money you have.</p>
<p>At some point, if a company has a high burn rate, it has to reduce structural costs by cutting expenditures on labor, rent, marketing, and/or capital equipment. The burn rate is an important metric for any company, but it's particularly important for startups that aren't yet generating revenue. It tells managers and investors how fast the company is spending its capital</p>
<p><a target="_blank" href="https://www.youtube.com/watch?v=BpS3shZI35A">Watch this video</a> to understand more about Burn Rate.</p>
<h2 id="heading-2-cash-runway-how-long-before-you-run-out-of-cash">2. Cash Runway: How Long Before You Run Out of Cash?</h2>
<p>Cash runway tells how long a startup can continue to operate at a certain burn rate until they are out of cash. For startups without revenue, you can calculate this by dividing available cash by total monthly expenses. Available cash is defined as the funds that are accessible now or can be accessed at a later time relatively quickly to pay for expenses.</p>
<p>When you’re making this calculation, it’s important to not include any anticipated fundraising and other uncertain sources of capital.</p>
<p>Actively managing cash runway is crucial for startup survival and growth. With a significant percentage of startups failing due to cash shortages, founders need to closely monitor their cash burn rate and runway.</p>
<p>The length of runway needed varies based on factors including the startup’s stage, industry, and milestones. In tighter venture capital markets, startups should plan for longer runways and consider strategies such as increasing revenue, reducing expenses, or raising additional capital. Regularly updating financial models and understanding metrics like the burn multiple can help you make informed decisions to extend your runway and align your growth ambitions with financial stability.</p>
<p>While it’s a simple calculation at face value, a cash runway analysis is nuanced and unique to every startup and can be impacted by a multitude of circumstances.</p>
<p>To calculate this, you just divide your total cash reserves by the amount you’re spending each month. Say you’ve got $250K in the bank and you’re spending $50K/month: 250/50 = 5. So you’ve got 5 months. Not 6. Not “it depends” – 5. That’s your runway.</p>
<p>Investors ask “If we don’t fund you, how long do you survive?” If you don’t know that answer, you're not fundraising – you’re freelancing with hope.</p>
<p><a target="_blank" href="https://youtu.be/vtaMwtQgFGE?si=-Hcf_h_LxKYChdBa">Here is a video</a> that explains cash runway with real world examples and the thought process behind it.</p>
<p>And <a target="_blank" href="https://www.jpmorgan.com/insights/business-planning/does-your-startup-have-enough-runway-to-survive">here’s an article</a> from JP Morgan breaking down cash runway, its importance, and what can you to to maximize it.</p>
<h3 id="heading-burn-rate-vs-runway">Burn Rate vs Runway</h3>
<p>So, let’s just make this super clear: burn rate is simply how much you spend each month to run your operation – that is, your negative cash flow. Runway is how many months there are left before your bank balance reaches zero.</p>
<p>So again, why do these numbers matter?</p>
<p>Because burn rate tells you how quickly you need to find more revenue or funding. Runway tells investors whether you are going to still be around by the time they finish their due diligence.</p>
<p>They are not just numbers. They are your survival clock.</p>
<p>Smart founders utilize these metrics to:</p>
<ul>
<li><p>Trim the fat without cutting muscle – know what to focus on and what to let go</p>
</li>
<li><p>Forecast hiring/fundraising deadlines – know the process and prep for it. Numbers don’t line but they sure can get you ghosted.</p>
</li>
<li><p>Assure investors you’re not going to come knocking again in 90 days – establishing credibility is key, make an investor realize its not just a hobby, you mean business.</p>
</li>
</ul>
<p>The goal: Extend runway without stalling momentum. Keep the plane in the air, while building a bigger engine.</p>
<h2 id="heading-3-cac-customer-acquisition-cost-how-much-does-it-cost-to-convince-someone-to-pay-you">3. CAC (Customer Acquisition Cost): How Much Does it Cost to Convince Someone to Pay You?</h2>
<p>Cost of acquisition refers to the entire cost that a business incurs to obtain a new client or asset. This includes the purchase price, shipping, installation, and marketing costs for the asset acquired. CAC takes into account the total expenditure on all marketing, advertising, and sales for the period, which you then divide by the number of new customers for the period.</p>
<p>In this case, all the upfront costs incurred to purchase a business asset, including equipment or inventory, are part of the cost of acquisition. Cost of acquisition includes:</p>
<ul>
<li><p>Purchase price of the item</p>
</li>
<li><p>Costs to ship it to its point of use</p>
</li>
<li><p>Costs to install the item</p>
</li>
<li><p>Costs to get it up and running (in the case of equipment) or ready for sale (in the case of inventory) condition</p>
</li>
<li><p>Marketing sales teams salaries</p>
</li>
<li><p>All sales and consulting marketing expenses geared to get new consumers should all be included</p>
</li>
</ul>
<p><strong>Formula:</strong><br>CAC = (Total Marketing + Sales Expenses) / Number of New Customers Acquired</p>
<p>Say you spent $10K last month across paid ads, content creation, outbound campaigns, and sales team costs. You onboarded 100 new customers, so your CAC = $100.</p>
<p>But is that good?</p>
<p>It depends on:</p>
<ul>
<li><p>Your pricing model (one-time vs. subscription)</p>
</li>
<li><p>Your margin (how much of that sale do you actually keep?)</p>
</li>
<li><p>Your customer retention (how long do they stick around?)</p>
</li>
</ul>
<p>If you’re selling a $20 product once, a $100 CAC is a non-starter. But if that customer brings in $50/month for 12 months, you’ve got a solid return.</p>
<p><strong>Watch for red flags:</strong></p>
<ul>
<li><p>CAC is rising but revenue isn’t</p>
</li>
<li><p>You’re overly reliant on paid ads (especially if organic/referral is flat)</p>
</li>
<li><p>You don’t know CAC by channel (averages hide leaks)</p>
</li>
</ul>
<p>A healthy CAC is one that pays itself back quickly and can be improved over time as you optimize funnels and messaging</p>
<p><a target="_blank" href="https://www.youtube.com/watch?v=KFJ3ip30QPM">Here is a video</a> that breaks down CAC for you.</p>
<h2 id="heading-4-customer-lifetime-value-ltv-how-much-is-one-customer-worth-over-time">4. Customer Lifetime Value (LTV): How Much is One Customer Worth Over Time?</h2>
<p>Customer Lifetime Value is the average monetary value of each customer to your business. LTV takes into account how much a unique customer is expected to spend with your business. It’s an important metric so you know how much new customers are worth to your business over their lifespan as a customer.</p>
<p>Let’s say you charge $25/month. The average customer sticks around 12 months.<br>LTV = $300.</p>
<p>In this case, if your CAC is $80? You’re in the green. But if it’s $350? You’re basically paying people to hang out (and losing money on them).</p>
<p>Now, let’s connect this to CAC.</p>
<p>Say your CAC is $80. You’re doing fine – your LTV is ~4x CAC. That’s what investors want to see.</p>
<p>Rule of thumb: you want your LTV to be at least 3x your CAC. A 1:1 ratio means you’re barely breaking even, before operational costs and the math stops working at scale. So if you can hit a 3:1 ratio, great – and based off my experience, your business will be much more appealing if it’s closer to 5:1.</p>
<p>And keep in mind that different models can have different thresholds. For example, a SaaS company with low churn can afford higher CACs, while an e-commerce platform might need faster payback. And marketplaces and freemium models may have lower LTV per user, but they can often more easily offset it with volume.</p>
<p>If you don’t know your LTV or can’t defend it with data, it becomes hard to justify spend – and easy for investors to walk.</p>
<p>If you want to know more, <a target="_blank" href="https://www.youtube.com/watch?v=vA1YX8963ts">this video</a> walks you through the basics.</p>
<p>And <a target="_blank" href="https://www.youtube.com/watch?v=773zBQVPx_Q">here’s a video</a> that beautifully explains the CAC and LTV relationship.</p>
<h2 id="heading-5-gross-profit-margin-what-do-you-actually-keep-after-delivering-your-service-or-product">5. Gross Profit Margin: What Do You Actually Keep After Delivering Your Service or Product?</h2>
<p>Gross profit margin shows the amount of money a business collects after it pays for all its expenses. It’s usually calculated as a percentage of sales. This specific metric is also referred to as the gross margin ratio.</p>
<p>Companies use gross margin as a measure of how production costs relate to revenue. If a company's gross margin falls because it is making less revenue, it may try to cut labor costs, find cheaper suppliers of materials, or increase prices to increase revenue.</p>
<p>Gross profit margins can also allow a business to measure how efficient a company is, or compare two very differently sized companies that share a common revenue stream or product</p>
<p>If you sell a subscription for $50/month and it costs you $10/month to host, maintain, and support it, your gross margin is 80%.</p>
<ul>
<li><p>Good: SaaS companies often hit 70–90%.</p>
</li>
<li><p>Bad: If you're below 30%, your "scalable" business will collapse under weight.</p>
</li>
</ul>
<p>Want to know the conceptual math behind this metric and how it differs from Profit Margin? <a target="_blank" href="https://www.youtube.com/watch?v=9xAMe0QBFhU&amp;t=45s">Here is a fantastic video</a> that easily breaks it down.</p>
<h2 id="heading-6-monthly-annual-recurring-revenue-mrr-arr">6. Monthly / Annual Recurring Revenue (MRR / ARR)</h2>
<p>Annual recurring revenue (ARR) is revenue a company expects to see from its product and service offerings, calculated over the course of a year. Companies that sell annual subscriptions like using ARR as a sales metric to track what they anticipate making in a year.</p>
<p>ARR tends to be used if companies sell a product or service in the software as a service (SaaS) space, but it can also be useful in terms of streaming services, cell phone bills, and (almost) anything else with a predictable, recurring charge.</p>
<p>ARR is calculated annually, whereas monthly recurring revenue (MRR) is calculated monthly. MRR is useful in that it shows what’s happening on a month-to-month basis. For example, if you change your price in April, you can see the immediate effects of that change in May. MRR also helps track fluctuations in revenue based on outside factors like holiday shopping seasons and economic conditions.</p>
<p>In a nutshell, Monthly / Annual Recurring Revenue = predictable income.</p>
<p>If you’re pulling $20K/month in subscriptions, that’s $240K ARR. Simple.</p>
<p>What investors care about:</p>
<ul>
<li><ul>
<li><p>Is it growing?</p>
<ul>
<li><p>How fast?</p>
</li>
<li><p>And how stable is it?</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><a target="_blank" href="https://youtu.be/qwo7WFWusO4?si=pCelTr2slb2OLuw9">Here is a founder breaking down the metric</a> and explaining the relationship between MRR/ARR.</p>
<h2 id="heading-7-churn-rate-how-fast-are-your-users-leaving">7. Churn Rate: How Fast Are Your Users Leaving</h2>
<p>The churn rate, also known as attrition rate, represents the rate at which a customer stops doing business with a company. Customer churn is typically expressed as the percentage of service subscribers that discontinue their service subscriptions within a time frame. Churn can also be expressed as the rate at which employees leave their jobs in a given time.</p>
<p>In order for a business to grow its number of clients, its growth rate (which takes into account new customers) must be higher than its churn rate.</p>
<p>The benefit of calculating a churn rate is that it can clarify how well a business is retaining its customers, which is a measure of the quality of service the business is providing and the usefulness of that service.</p>
<p>When a business can see its churn rate increasing from period to period, this suggests that a critical aspect of how it is running the business might be problematic or flawed.</p>
<p>It could be the result of:</p>
<ul>
<li><p>A faulty product(s)</p>
</li>
<li><p>Bad customer service</p>
</li>
<li><p>Costs exceed utility to customers</p>
</li>
</ul>
<p>And so on.</p>
<p>The churn rate will indicate to a business that it needs to learn why its customers are leaving, and where it needs to adjust its business. It’s more expensive to attract new customers than it is to retain them, so reducing the churn rate can save a business resources in the future.</p>
<p>Real talk: Say you had 500 users at the start of the month, and you lost 50 by the end of the month. That’s 10% churn – which is high! Annualize that and…ouch. You're not growing. You're replacing.</p>
<p>Make sure you fix this before you fundraise. Or at least explain why churn’s high and what you’re doing to plug the holes.</p>
<p><a target="_blank" href="https://www.youtube.com/watch?v=Jlg5J_Mpq7g">Here is a video</a> that beautifully explains Churn Rate.</p>
<h2 id="heading-8-payback-period-how-long-before-you-recover-your-cac">8. Payback Period: How Long Before You Recover Your CAC?</h2>
<p>The payback period is a popular tool for determining investment return. People invest money for the purpose of getting it back and generating a positive return on the money they invested. The shorter the payback period, the more beneficial the investment will be.</p>
<p>The payback period does not factor in the time value of money. You can determine it simply by counting the number of years until the principal paid in is returned.</p>
<p>This metric measures how quickly your customer pays you back for the cost of acquiring them. The payback period doesn’t take into account the total profitability of an investment. It’s just concerned with paying the investment back.</p>
<p>There are two common interpretations:</p>
<ol>
<li><p><strong>Customer-Level Payback:</strong> If your CAC is $250 and your customer pays $50/month, it’ll take 5 months to recover the acquisition cost.</p>
</li>
<li><p><strong>Investment-Level Payback:</strong> You spend $100,000 on a new sales hire, tool stack, or feature. You want to know how long it takes for that investment to generate $100,000 in profit.</p>
</li>
</ol>
<p>Both use the same principle: the shorter the payback period, the less cash you need to float your growth.</p>
<p>If you want a target, aim for 6 months for customer-level payback. Closer to 3-6 is ideal. Long payback periods mean you need deep pockets – or exceptional retention – to stay afloat.</p>
<p><a target="_blank" href="https://www.youtube.com/watch?v=KbtTk2azIjY">Here’s a video</a> where you can learn more.</p>
<h2 id="heading-9-earnings-before-interest-taxes-depreciation-and-amortization-ebitda">9. Earnings Before Interest, Taxes, Depreciation, and Amortization (EBITDA)</h2>
<p>EBITDA stands for Earning Before Interest, Taxes, Depreciation, and Amortization. You can think of this as just your company's operating profit if you wanted a very rudimentary way of referring to it.</p>
<p>It’s not flashy. It’s not fun. But it tells investors: “Here’s what we really make once the accounting fog clears.” and “Are we generating real profits from our actual operations?”</p>
<p>EBITDA is what investors look at because it is the best way of comparing apples to apples when considering startups. EBITDA can provide investors a measure of your operational health.</p>
<p>A negative EBITDA for an early stage company isn't going to raise any eyebrows. Just don’t act surprised when someone brings it up. You need to do that math before the pitch. But, if you have a growing early stage company that's moving from negative EBITDA to positive? Now your getting into grown folks business.</p>
<p><a target="_blank" href="https://www.youtube.com/watch?v=DH901SrBv9Q">Here’s a video</a> that explains the basics of EBITDA.</p>
<p>And <a target="_blank" href="https://www.youtube.com/watch?v=D58oCe_7BBM">here’s another video</a> that explains how investors look at EBITDA and its value in determining your business’s worth.</p>
<h2 id="heading-10-valuation-whats-your-company-worth-and-what-supports-that-number">10. Valuation: What’s Your Company Worth – and What Supports that Number?</h2>
<p>Valuation is a focused exercise that determines the value of an asset, investment, or company. So, how much your company’s worth. And the goal is typically to determine whether that value is a fair value.</p>
<p>Valuations can be conducted in one of two ways:</p>
<ul>
<li><p>an absolute valuation, which evaluates a company on its own merits and entirely independently of other factors/companies, or</p>
</li>
<li><p>a relative valuation, which evaluates the company relative to other similar firms, or assets, in the same sector or industry. This determines if the company, or asset, is worth that much relative to others.</p>
</li>
</ul>
<p>Depending on how the analysis and conclusions are reached, there are a variety of methods and techniques used to develop valuations. And as you’d expect, there’s often significant variability between outputs (or valuations) based on the inputs and context.</p>
<p>While valuations are predominantly quantitatively driven, there’s often a significant subjective influence that come from the assumptions and estimates made along the way. Valuations are also subject to developing situations and events outside of the analysis or the control of the analyst – for example, earnings reports or material news, or economic news – that can result in a change to a valuation stance.</p>
<p>If you’re pre-revenue and you’re saying $30M because a friend raised at that, please stop. Their experience likely has nothing to do with yours.</p>
<p>Valuation = traction + market comps + revenue + momentum + team.</p>
<p>Valuation isn’t just about what you want – it’s about what you can defend.</p>
<p>Startups are typically valued using:</p>
<ul>
<li><p><strong>Comparable Analysis (Comps):</strong> What similar companies are worth</p>
</li>
<li><p><strong>Discounted Cash Flow (DCF):</strong> Projecting future cash and discounting it back</p>
</li>
<li><p><strong>Revenue Multiples:</strong> Often 5x–10x for SaaS, but varies wildly</p>
</li>
<li><p><strong>Precedent Transactions:</strong> What investors paid in past rounds for similar startups</p>
</li>
</ul>
<p>But that’s the math.</p>
<p>Here’s the messy truth: <strong>Valuation = Traction + Team + TAM (total addressable market) + Timing + Storytelling.</strong></p>
<p>Hard factors:</p>
<ul>
<li><p>MRR/ARR</p>
</li>
<li><p>Growth rate</p>
</li>
<li><p>Churn</p>
</li>
<li><p>CAC:LTV</p>
</li>
<li><p>Gross margins</p>
</li>
</ul>
<p>Soft factors:</p>
<ul>
<li><p>Founding team’s track record</p>
</li>
<li><p>Market momentum</p>
</li>
<li><p>Hype or scarcity</p>
</li>
</ul>
<p>Don’t inflate. Don’t anchor to your friend’s raise. Know your comps. And show why <em>your</em> model is defensible, not just desirable. Inflated numbers make investors run. They don’t correct you – they just ghost you.</p>
<p>There are numerous books written on valuation and each technique could be its own PhD. But my role here is to give you a sneak peak into the metrics.</p>
<p>Here’s a <a target="_blank" href="https://www.youtube.com/watch?v=T3Ud5WQCrzQ">basic video on valuation</a> if you’re interested in a deeper dive.</p>
<p>And <a target="_blank" href="https://www.youtube.com/watch?v=znmQ7oMiQrM&amp;list=PLUkh9m2BorqnKWu0g5ZUps_CbQ-JGtbI9">here’s a more detailed video course</a> outlining different forms of valuation. Professor Damodaran from New York University is considered to be one of the aces and thought leaders when it comes to valuation. In this video course he explains stepwise and beautifully so you can understand and explore the fascinating world of valuations.</p>
<h2 id="heading-real-talk-before-you-close-that-tab">Real Talk Before You Close That Tab</h2>
<h3 id="heading-real-experience">Real Experience</h3>
<p>I met a founder once – early days, rough product, but you could tell he actually cared. He wasn't trying to look good. No buzzwords. No "disrupt" talk. Just someone trying to solve something annoying and important.</p>
<p>He walked into the room with a twinkle. Not swagger – just that gentle intensity. We were leaning in.</p>
<p>Then, in the middle of the pitch, someone asked, "So what's your monthly burn?" And I swear to you, he said, "Umm... I think my co-founder has that. I haven't looked in a while."</p>
<p>That was it.</p>
<p>No freak out. No awkward pause. Just... a cluck. Like a window closing in the background.</p>
<p>The product? Still smart. But the moment? Gone.</p>
<p>Nobody was mad. Nobody laughed. We even said thank you. But nobody followed up.</p>
<p>Why? Because it didn't feel like a business. It felt like a maybe.</p>
<h3 id="heading-why-this-matters">Why This Matters</h3>
<p>I’ve seen so many versions of that same scene play out. It’s never about charisma. It’s not even about the idea, half the time.</p>
<p>It’s about whether the person asking for money actually knows what they’re building. Not the dream, the mechanics. The guts, nuts and bolds of the business. The ugly Excel math nobody brags about on Twitter.</p>
<p>Unfortunately, no simple pitch deck will do that part for you. No co-founder can answer those questions on your behalf.</p>
<p>If it’s your vision, own the math. If it’s your company, learn the cost of keeping it alive.</p>
<p>The rest? The logos, the taglines, the “go-to-market” plans?.... All of that’s just packaging.</p>
<p>And you don’t have to be perfect either. You just have to be in it. Eyes open. Numbers in your head.<br>Because if you’re asking people to believe in what you’re building, you’d better believe in the scaffolding holding it up.</p>
<p>So yeah, know your CAC. Your LTV. Your margins. Your churn. Not to check some box on an investor’s sheet, but to prove to yourself and the investor that the thing you’re spending your life on…has legs. That it can stand. And run.</p>
<p>And maybe, someday, outlast you. Maybe!</p>
<h2 id="heading-conclusion-and-final-thoughts"><strong>Conclusion and Final Thoughts</strong></h2>
<p>I hope this was helpful to you, especially if you’re a founder or aspiring founder trying to build the next big thing. While there a many more ratios and concepts, these are the crux of them.</p>
<p>A lot of other complex ratios and valuations are either built using these metrics or refer them in some way. And each of these metrics could be an article of its own. But I wanted to give you my top 10 run down so that you could get a head start. Numbers are very much a part of the ideation stage itself, and omitting them from your strategy could prove to be a fatal mistake.</p>
<p>I’ll leave you with <a target="_blank" href="https://youtu.be/Pg72m3CjuK4?si=GtIFdvC5WzbKna79">one last video</a> on How to Start a Start Up with Michael Seibel (Reddit, YC, Twitch) that I hope you find valuable. It lays out, in a crash course format, the mindset of a founder who has been there and done that. The fun fact is that a lot of the themes he speaks of tie in to the metrics here, directly or indirectly.</p>
<p>I hope this gives you a perspective of being on the other side, evaluating your hard work and passion, and I hope it sets you up for success in your next Investor Review.</p>
<p>I look forward to your thoughts, comments, and feedback. If this was helpful, engaging, and informative, do share it – you never know who may need it, or could benefit from it. I wish you all the very best in your funding rounds.</p>
<p>Until then, keep learning, unlearning, and relearning, folks.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Sort Dates Efficiently in JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ Recently, I was working on a PowerApps Component Framework (PCF) project that required sorting an array of objects by date. The dates were in ISO 8601 format but without a time zone – for example, "20 ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-sort-dates-efficiently-in-javascript/</link>
                <guid isPermaLink="false">6839b592f8650e337982bcba</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ performance ]]>
                    </category>
                
                    <category>
                        <![CDATA[ datetime ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Brandon Wozniewicz ]]>
                </dc:creator>
                <pubDate>Fri, 30 May 2025 13:41:38 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748612402734/7124a95d-0a33-4ab6-93d2-d94fc354ae12.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Recently, I was working on a PowerApps Component Framework (PCF) project that required sorting an array of objects by date. The dates were in ISO 8601 format but without a time zone – for example, <code>"2025-05-01T15:00:00.00"</code>.</p>
<p>Without much thought, I wrote something similar to:</p>
<pre><code class="language-JavaScript">const sorted = data.sort((a, b) =&gt; {
  return new Date(a.date) - new Date(b.date);
})
</code></pre>
<p>This worked fine on small datasets. But the array I was sorting had nearly <strong>30,000 objects</strong>. On a fast development machine, the performance hit was around <strong>100–150ms</strong> – already noticeable when combined with other UI work. When I tested with <strong>4× CPU throttling</strong> in the browser, the delay increased to nearly <strong>400ms</strong>, which more accurately simulates a lower-end device. That's a reasonable approach to ensure your app still performs well for users on slower machines.</p>
<p>Results in browser:</p>
<pre><code class="language-Bash">sort_with_date_conversion: 397.955078125 ms
</code></pre>
<p>Output with performance throttled by 4x slowdown</p>
<p>In this article, you will learn how to sort dates efficiently in JavaScript. We'll walk through what makes the method above inefficient, as well as a better pattern–especially when dealing with large amounts of data.</p>
<h3 id="heading-table-of-contents">Table of Contents</h3>
<ol>
<li><p><a href="#heading-why-400ms-feels-slow">Why 400ms Feels Slow</a></p>
</li>
<li><p><a href="#heading-setting-up-our-experiment">Setting Up Our Experiment</a></p>
</li>
<li><p><a href="#heading-the-cost-of-date-conversion">The Cost of Date Conversion</a></p>
</li>
<li><p><a href="#heading-the-lexicographical-superpower-of-iso-8601">The Lexicographical Superpower of ISO 8601</a></p>
</li>
<li><p><a href="#heading-what-if-your-dates-arent-iso-format">What If Your Dates Aren't ISO Format?</a></p>
</li>
<li><p><a href="#heading-key-takeaways">Key Takeaways</a></p>
</li>
</ol>
<h2 id="heading-why-400ms-feels-slow">Why 400ms <em>Feels</em> Slow</h2>
<p>According to Jakob Nielsen's classic <em>"Usability Engineering</em>" (1993), delays under 100 milliseconds are perceived as instantaneous. Between 100ms and 1,000ms, users start to notice lag – even if it doesn't require UI feedback. In my case, 400ms felt <strong>choppy</strong>, especially since the PCF component was already handling other tasks. It wasn't going to cut it.</p>
<h2 id="heading-setting-up-our-experiment">Setting Up Our Experiment</h2>
<p>Let's simulate this with a simple experiment that stress tests our sorting. We'll create an array of 100,000 ISO-formatted dates, and <strong>we will simulate a 4x performance slowdown in the browser for all scenarios:</strong></p>
<pre><code class="language-JavaScript">// Create an array of 100,000 ISO-format dates
const isoArray = [];
let currentDate = new Date(2023, 9, 1); // October 1, 2023

for (let i = 0; i &lt; 100000; i++) {
  const year = currentDate.getFullYear();
  const month = String(currentDate.getMonth() + 1).padStart(2, '0');
  const day = String(currentDate.getDate()).padStart(2, '0');

  isoArray.push({ date: `\({year}-\){month}-${day}`, value: i });
  currentDate.setDate(currentDate.getDate() + 1); // advance by one day
}

// Shuffle the array to simulate unsorted input
function shuffle(array) {
  for (let i = array.length - 1; i &gt; 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  }
}

shuffle(isoArray);
</code></pre>
<h2 id="heading-the-cost-of-date-conversion">The Cost of Date Conversion</h2>
<p>Now, let's sort using the <code>new Date()</code> method, where each new date is instantiated directly inside the sort method.</p>
<pre><code class="language-JavaScript">console.time('sort_with_date_conversion');

// Sorting by converting each string to a Date object on every comparison
const sortedByDate = isoArray.sort((a, b) =&gt; {
  return new Date(a.date) - new Date(b.date);
});

console.timeEnd('sort_with_date_conversion');
</code></pre>
<p>Result in browser:</p>
<pre><code class="language-Bash">sort_with_date_conversion: 1629.466796875 ms
</code></pre>
<p>Sorting 100,000 dates took almost 2 seconds.</p>
<p>Almost 2 seconds. Ouch.</p>
<h2 id="heading-the-lexicographical-superpower-of-iso-8601">The Lexicographical Superpower of ISO 8601</h2>
<p>Here’s the critical realization: <strong>ISO 8601 date strings are already lexicographically sortable</strong>. That means we can skip the <code>Date</code> object entirely:</p>
<pre><code class="language-JavaScript">console.time('sort_by_iso_string');

// Compare strings directly — thanks to ISO 8601 format
const sorted = isoArray.sort((a, b) =&gt; 
  a.date &gt; b.date ? 1 : -1
);

console.timeEnd('sort_by_iso_string');
console.log(sorted.slice(0, 10));
</code></pre>
<p>Output in the console:</p>
<pre><code class="language-Bash">sort_by_iso_string: 10.549072265625 ms
[
  { date: '2023-10-01', value: 0 },
  { date: '2023-10-02', value: 1 },
  { date: '2023-10-03', value: 2 },
  { date: '2023-10-04', value: 3 },
  { date: '2023-10-05', value: 4 },
  { date: '2023-10-06', value: 5 },
  { date: '2023-10-07', value: 6 },
  { date: '2023-10-08', value: 7 },
  { date: '2023-10-09', value: 8 },
  { date: '2023-10-10', value: 9 }
]
</code></pre>
<p>From 1600ms down to ~10ms. That's a 160x speedup.</p>
<p>Why is this faster? Because using new Date() inside .sort() results in creating two new Date objects <strong>per comparison</strong>. With 100,000 items and how sort works internally, that's <strong>millions</strong> of object instantiations. On the other hand, when we sort lexicographically, we are simply sorting strings, which is far less expensive.</p>
<h2 id="heading-what-if-your-dates-arent-iso-format">What If Your Dates <em>Aren't</em> ISO Format?</h2>
<p>Let's say your dates are in <code>MM/DD/YYYY</code> format. Those strings aren't lexicographically sortable, so you'll need to transform them first.</p>
<h3 id="heading-transform-then-sort">Transform <em>then</em> Sort</h3>
<pre><code class="language-JavaScript">console.time('sort_with_iso_conversion_first');

const sortedByISO = mdyArray
  .map((item) =&gt; { // First convert to ISO format
    const [month, day, year] = item.date.split('/');
    return { date: `\({year}-\){month}-${day}`, value: item.value };
  })
  .sort((a, b) =&gt; (a.date &gt; b.date ? 1 : -1)); // then sort

console.timeEnd('sort_with_iso_conversion_first');
</code></pre>
<p>Output:</p>
<pre><code class="language-Bash">sort_with_iso_conversion_first: 58.8779296875 ms
</code></pre>
<p>Still perceived as instantaneous.</p>
<h3 id="heading-retaining-original-objects">Retaining Original Objects</h3>
<p>If you want to keep your original objects (with non-ISO dates), you can use tuples:</p>
<pre><code class="language-JavaScript">console.time('sort_and_preserve_original');

// Create tuples: [sortableDate, originalObject]
const sortedWithOriginal = mdyArray
  .map((item) =&gt; {
    const [month, day, year] = item.date.split('/');
    return [`\({year}-\){month}-${day}`, item]; // return the tuple items
  })
  .sort((a, b) =&gt; a[0] &gt; b[0] ? 1 : -1) // sort based on the first item
  .map(([, item]) =&gt; item); // Return the original object

console.timeEnd('sort_and_preserve_original');
</code></pre>
<p>Output:</p>
<pre><code class="language-Bash">sort_and_preserve_original: 73.733154296875 ms
</code></pre>
<p>Still within the boundaries of being perceived as instantaneous.</p>
<p>The original data is preserved and the performance falls well within what is perceived as instantaneous.</p>
<h2 id="heading-key-takeaways">Key Takeaways</h2>
<ul>
<li><p><strong>Avoid object creation inside .sort()</strong>, especially for large arrays.</p>
</li>
<li><p><strong>ISO 8601 strings are lexicographically sortable.</strong> Use string comparison when you can.</p>
</li>
<li><p>If your date strings aren't sortable, <strong>map them to a sortable form first</strong>, sort, and optionally map them back.</p>
</li>
<li><p>Minor tweaks in sorting can yield <strong>massive performance gains</strong> – especially in UI components or real-time visualizations.</p>
</li>
</ul>
<p>Found this helpful? I write about practical automation, productivity systems, and building smarter workflows — without the jargon. Visit me at <a href="http://brandonwoz.com">brandonwoz.com</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ The Front-End Performance Optimization Handbook – Tips and Strategies for Devs ]]>
                </title>
                <description>
                    <![CDATA[ When you’re building a website, you’ll want it to be responsive, fast, and efficient. This means making sure the site loads quickly, runs smoothly, and provides a seamless experience for your users, among other things. So as you build, you’ll want to... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/the-front-end-performance-optimization-handbook/</link>
                <guid isPermaLink="false">681b5e61b1ed0b90facd0adf</guid>
                
                    <category>
                        <![CDATA[ Frontend Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ frontend ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ performance ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gordan Tan ]]>
                </dc:creator>
                <pubDate>Wed, 07 May 2025 13:21:37 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1746468304666/ca24ac6b-1591-4abf-a544-739fbfaecf49.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When you’re building a website, you’ll want it to be responsive, fast, and efficient. This means making sure the site loads quickly, runs smoothly, and provides a seamless experience for your users, among other things.</p>
<p>So as you build, you’ll want to keep various performance optimizations in mind – like reducing file size, making fewer server requests, optimizing images in various ways, and so on.</p>
<p>But performance optimization is a double-edged sword, with both good and bad aspects. The good side is that it can improve website performance, while the bad side is that it's complicated to configure, and there are many rules to follow.</p>
<p>Also, some performance optimization rules aren't suitable for all scenarios and should be used with caution. So make sure you approach this handbook with a critical eye. In it, I’ll lay out a bunch of ways you can optimize your website’s performance, and share insights to help you chose which of these techniques to use.</p>
<p>I’ll also provide the references for these optimization suggestions after each one and at the end of the article.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-reduce-http-requests">Reduce HTTP Requests</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-http2">Use HTTP2</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-server-side-rendering">Use Server-Side Rendering</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-a-cdn-for-static-resources">Use a CDN for Static Resources</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-place-css-in-the-head-and-javascript-files-at-the-bottom">Place CSS in the Head and JavaScript Files at the Bottom</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-font-icons-iconfont-instead-of-image-icons">Use Font Icons (iconfont) Instead of Image Icons</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-make-good-use-of-caching-avoid-reloading-the-same-resources">Make Good Use of Caching, Avoid Reloading the Same Resources</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-compress-files">Compress Files</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-image-optimization">Image Optimization</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-lazy-loading-images">Lazy Loading Images</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-responsive-images">Responsive Images</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-adjust-image-size">Adjust Image Size</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-reduce-image-quality">Reduce Image Quality</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-css3-effects-instead-of-images-when-possible">Use CSS3 Effects Instead of Images When Possible</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-webp-format-images">Use webp Format Images</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-load-code-on-demand-through-webpack-extract-third-party-libraries-reduce-redundant-code-when-converting-es6-to-es5">Load Code on Demand Through Webpack, Extract Third-Party Libraries, Reduce Redundant Code When Converting ES6 to ES5</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-reduce-reflows-and-repaints">Reduce Reflows and Repaints</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-event-delegation">Use Event Delegation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-pay-attention-to-program-locality">Pay Attention to Program Locality</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-if-else-vs-switch">if-else vs switch</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-lookup-tables">Lookup Tables</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-avoid-page-stuttering">Avoid Page Stuttering</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-requestanimationframe-to-implement-visual-changes">Use requestAnimationFrame to Implement Visual Changes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-web-workers">Use Web Workers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-bitwise-operations">Use Bitwise Operations</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-dont-override-native-methods">Don't Override Native Methods</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-reduce-the-complexity-of-css-selectors">Reduce the Complexity of CSS Selectors</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-flexbox-instead-of-earlier-layout-models">Use Flexbox Instead of Earlier Layout Models</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-transform-and-opacity-properties-to-implement-animations">Use Transform and Opacity Properties to Implement Animations</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-rules-reasonably-avoid-over-optimization">Use Rules Reasonably, Avoid Over-Optimization</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-other-references">Other References</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-1-reduce-http-requests"><strong>1. Reduce HTTP Requests</strong></h2>
<p>A complete HTTP request needs to go through DNS lookup, TCP handshake, browser sending the HTTP request, server receiving the request, server processing the request and sending back a response, browser receiving the response, and other processes. Let's look at a specific example to understand how HTTP works:</p>
<p><img src="https://camo.githubusercontent.com/7988c06bb7b698dcc66ac8f2556cbe03b239ba2c8bf17ecddb29004c74b0eb36/68747470733a2f2f692d626c6f672e6373646e696d672e636e2f626c6f675f6d6967726174652f64333736643731343630633763376331316462316338353134366230343164632e706e67" alt="HTTP request waterfall showing timing breakdown" width="600" height="400" loading="lazy"></p>
<p>This is an HTTP request, and the file size is 28.4KB.</p>
<p>Terminology explained:</p>
<ul>
<li><p>Queueing: Time spent in the request queue.</p>
</li>
<li><p>Stalled: The time difference between when the TCP connection is established and when data can actually be transmitted, including proxy negotiation time.</p>
</li>
<li><p>Proxy negotiation: Time spent negotiating with the proxy server.</p>
</li>
<li><p>DNS Lookup: Time spent performing DNS lookup. Each different domain on a page requires a DNS lookup.</p>
</li>
<li><p>Initial Connection / Connecting: Time spent establishing a connection, including TCP handshake/retry and SSL negotiation.</p>
</li>
<li><p>SSL: Time spent completing the SSL handshake.</p>
</li>
<li><p>Request sent: Time spent sending the network request, usually a millisecond.</p>
</li>
<li><p>Waiting (TFFB): TFFB is the time from when the page request is made until the first byte of response data is received.</p>
</li>
<li><p>Content Download: Time spent receiving the response data.</p>
</li>
</ul>
<p>From this example, we can see that the actual data download time accounts for only <code>13.05 / 204.16 = 6.39%</code> of the total. The smaller the file, the smaller this ratio – and the larger the file, the higher the ratio. This is why it's recommended to combine multiple small files into one large file, which reduces the number of HTTP requests.</p>
<h3 id="heading-how-to-combine-multiple-files"><strong>How to combine multiple files</strong></h3>
<p>There are several techniques to reduce the number of HTTP requests by combining files:</p>
<p><strong>1. Bundle JavaScript files with Webpack</strong></p>
<pre><code class="lang-typescript"><span class="hljs-comment">// webpack.config.js</span>
<span class="hljs-built_in">module</span>.<span class="hljs-built_in">exports</span> = {
  entry: <span class="hljs-string">'./src/index.js'</span>,
  output: {
    filename: <span class="hljs-string">'bundle.js'</span>,
    path: path.resolve(__dirname, <span class="hljs-string">'dist'</span>),
  },
};
</code></pre>
<p>This will combine all JavaScript files imported in your entry point into a single bundle.</p>
<p><strong>2. Combine CSS files</strong><br>Using CSS preprocessors like Sass:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">/* main.scss */</span>
<span class="hljs-meta">@import</span> <span class="hljs-string">'reset'</span>;
<span class="hljs-meta">@import</span> <span class="hljs-string">'variables'</span>;
<span class="hljs-meta">@import</span> <span class="hljs-string">'typography'</span>;
<span class="hljs-meta">@import</span> <span class="hljs-string">'layout'</span>;
<span class="hljs-meta">@import</span> <span class="hljs-string">'components'</span>;
</code></pre>
<p>Then compile to a single CSS file:</p>
<pre><code class="lang-typescript">sass main.scss:main.css
</code></pre>
<p>Reference:</p>
<ul>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Performance_API/Resource_timing">Resource_timing</a></li>
</ul>
<h2 id="heading-2-use-http2"><strong>2. Use HTTP2</strong></h2>
<p>Compared to HTTP1.1, HTTP2 has several advantages:</p>
<h3 id="heading-faster-parsing">Faster parsing</h3>
<p>When parsing HTTP1.1 requests, the server must continuously read bytes until it encounters the CRLF delimiter. Parsing HTTP2 requests isn't as complicated because HTTP2 is a frame-based protocol, and each frame has a field indicating its length.</p>
<h3 id="heading-multiplexing">Multiplexing</h3>
<p>With HTTP1.1, if you want to make multiple requests simultaneously, you need to establish multiple TCP connections because one TCP connection can only handle one HTTP1.1 request at a time.</p>
<p>In HTTP2, multiple requests can share a single TCP connection, which is called multiplexing. Each request and response is represented by a stream with a unique stream ID to identify it.<br>Multiple requests and responses can be sent out of order within the TCP connection and then reassembled at the destination using the stream ID.</p>
<h3 id="heading-header-compression">Header compression</h3>
<p>HTTP2 provides header compression functionality.</p>
<p>For example, consider the following two requests:</p>
<pre><code class="lang-typescript">:authority: unpkg.zhimg.com
:method: GET
:path: <span class="hljs-regexp">/za-js-sdk@2.16.0/</span>dist/zap.js
:scheme: https
accept: *<span class="hljs-comment">/*
accept-encoding: gzip, deflate, br
accept-language: zh-CN,zh;q=0.9
cache-control: no-cache
pragma: no-cache
referer: https://www.zhihu.com/
sec-fetch-dest: script
sec-fetch-mode: no-cors
sec-fetch-site: cross-site
user-agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36</span>
</code></pre>
<pre><code class="lang-typescript">:authority: zz.bdstatic.com
:method: GET
:path: <span class="hljs-regexp">/linksubmit/</span>push.js
:scheme: https
accept: *<span class="hljs-comment">/*
accept-encoding: gzip, deflate, br
accept-language: zh-CN,zh;q=0.9
cache-control: no-cache
pragma: no-cache
referer: https://www.zhihu.com/
sec-fetch-dest: script
sec-fetch-mode: no-cors
sec-fetch-site: cross-site
user-agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36</span>
</code></pre>
<p>From the two requests above, you can see that a lot of data is repeated. If we could store the same headers and only send the differences between them, we could save a lot of bandwidth and speed up the request time.</p>
<p>HTTP/2 uses "header tables" on the client and server sides to track and store previously sent key-value pairs, and for identical data, it's no longer sent through each request and response.</p>
<p>Here's a simplified example. Suppose the client sends the following header requests in sequence:</p>
<pre><code class="lang-typescript">Header1:foo
Header2:bar
Header3:bat
</code></pre>
<p>When the client sends a request, it creates a table based on the header values:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Index</td><td>Header Name</td><td>Value</td></tr>
</thead>
<tbody>
<tr>
<td>62</td><td>Header1</td><td>foo</td></tr>
<tr>
<td>63</td><td>Header2</td><td>bar</td></tr>
<tr>
<td>64</td><td>Header3</td><td>bat</td></tr>
</tbody>
</table>
</div><p>If the server receives the request, it will create the same table.<br>When the client sends the next request, if the headers are the same, it can directly send a header block like this:</p>
<pre><code class="lang-typescript"><span class="hljs-number">62</span> <span class="hljs-number">63</span> <span class="hljs-number">64</span>
</code></pre>
<p>The server will look up the previously established table and restore these numbers to the complete headers they correspond to.</p>
<h3 id="heading-priority">Priority</h3>
<p>HTTP2 can set a higher priority for more urgent requests, and the server can prioritize handling them after receiving such requests.</p>
<h3 id="heading-flow-control">Flow control</h3>
<p>Since the bandwidth of a TCP connection (depending on the network bandwidth from client to server) is fixed, when there are multiple concurrent requests, if one request occupies more traffic, another request will occupy less. Flow control can precisely control the flow of different streams.</p>
<h3 id="heading-server-push">Server push</h3>
<p>A powerful new feature added in HTTP2 is that the server can send multiple responses to a single client request. In other words, in addition to responding to the initial request, the server can also push additional resources to the client without the client explicitly requesting them.</p>
<p>For example, when a browser requests a website, in addition to returning the HTML page, the server can also proactively push resources based on the URLs of resources in the HTML page.</p>
<p>Many websites have already started using HTTP2, such as Zhihu:</p>
<p><a target="_blank" href="https://camo.githubusercontent.com/17c8f78f0341150240e6719ed82ee794e5c569404861581ccad306b88d9b6f6c/68747470733a2f2f696d672d626c6f672e6373646e696d672e636e2f696d675f636f6e766572742f39636165316532313931613035393466393833373636646635636265373562352e706e67"><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%2Fb0ovwimn9pg7z7eo0qxd.png" alt="show hot to check HTTP1 and HTTP2 protocols" width="600" height="400" loading="lazy"></a></p>
<p>Where "h2" refers to the HTTP2 protocol, and "http/1.1" refers to the HTTP1.1 protocol.</p>
<p>References:</p>
<ul>
<li><p><a target="_blank" href="https://developers.google.com/web/fundamentals/performance/http2/?hl=zh-cn">HTTP2 Introduction</a></p>
</li>
<li><p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP">HTTP</a></p>
</li>
</ul>
<h2 id="heading-3-use-server-side-rendering"><strong>3. Use Server-Side Rendering</strong></h2>
<p>In client-side rendering, you get the HTML file, download JavaScript files as needed, run the files, generate the DOM, and then render.</p>
<p>And in server-side rendering, the server returns the HTML file, and the client only needs to parse the HTML.</p>
<ul>
<li><p>Pros: Faster first-screen rendering, better SEO.</p>
</li>
<li><p>Cons: Complicated configuration, increases the computational load on the server.</p>
</li>
</ul>
<p>Below, I'll use Vue SSR as an example to briefly describe the SSR process.</p>
<h3 id="heading-client-side-rendering-process">Client-side rendering process</h3>
<ol>
<li><p>Visit a client-rendered website.</p>
</li>
<li><p>The server returns an HTML file containing resource import statements and <code>&lt;div id="app"&gt;&lt;/div&gt;</code>.</p>
</li>
<li><p>The client requests resources from the server via HTTP, and when the necessary resources are loaded, it executes <code>new Vue()</code> to instantiate and render the page.</p>
</li>
</ol>
<p><strong>Example of client-side rendered app (Vue):</strong></p>
<pre><code class="lang-typescript">&lt;!-- index.html --&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
  &lt;title&gt;Client-side Rendering Example&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;!-- Initially empty container --&gt;
  &lt;div id=<span class="hljs-string">"app"</span>&gt;&lt;/div&gt;

  &lt;!-- JavaScript bundle that will render the content --&gt;
  &lt;script src=<span class="hljs-string">"/dist/bundle.js"</span>&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<pre><code class="lang-typescript"><span class="hljs-comment">// main.js (compiled into bundle.js)</span>
<span class="hljs-keyword">import</span> Vue <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">'./App.vue'</span>;

<span class="hljs-comment">// Client-side rendering happens here - after JS loads and executes</span>
<span class="hljs-keyword">new</span> Vue({
  render: <span class="hljs-function"><span class="hljs-params">h</span> =&gt;</span> h(App)
}).$mount(<span class="hljs-string">'#app'</span>);
</code></pre>
<pre><code class="lang-typescript"><span class="hljs-comment">// App.vue</span>
&lt;template&gt;
  &lt;div&gt;
    &lt;h1&gt;{{ title }}&lt;/h1&gt;
    &lt;p&gt;This content is rendered client-side.&lt;/p&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  data() {
    <span class="hljs-keyword">return</span> {
      title: <span class="hljs-string">'Hello World'</span>
    }
  },
  <span class="hljs-comment">// In client-side rendering, this lifecycle hook runs in the browser</span>
  mounted() {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Component mounted in browser'</span>);
  }
}
&lt;/script&gt;
</code></pre>
<h3 id="heading-server-side-rendering-process">Server-side rendering process</h3>
<ol>
<li><p>Visit a server-rendered website.</p>
</li>
<li><p>The server checks which resource files the current route component needs, then fills the content of these files into the HTML file. If there are AJAX requests, it will execute them for data pre-fetching and fill them into the HTML file, and finally return this HTML page.</p>
</li>
<li><p>When the client receives this HTML page, it can start rendering the page immediately. At the same time, the page also loads resources, and when the necessary resources are fully loaded, it begins to execute <code>new Vue()</code> to instantiate and take over the page.</p>
</li>
</ol>
<p><strong>Example of server-side rendered app (Vue):</strong></p>
<pre><code class="lang-typescript"><span class="hljs-comment">// server.js</span>
<span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">const</span> server = express();
<span class="hljs-keyword">const</span> { createBundleRenderer } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'vue-server-renderer'</span>);

<span class="hljs-comment">// Create a renderer based on the server bundle</span>
<span class="hljs-keyword">const</span> renderer = createBundleRenderer(<span class="hljs-string">'./dist/vue-ssr-server-bundle.json'</span>, {
  template: <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>).readFileSync(<span class="hljs-string">'./index.template.html'</span>, <span class="hljs-string">'utf-8'</span>),
  clientManifest: <span class="hljs-built_in">require</span>(<span class="hljs-string">'./dist/vue-ssr-client-manifest.json'</span>)
});

<span class="hljs-comment">// Handle all routes with the same renderer</span>
server.get(<span class="hljs-string">'*'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> context = { url: req.url };

  <span class="hljs-comment">// Render our Vue app to a string</span>
  renderer.renderToString(context, <span class="hljs-function">(<span class="hljs-params">err, html</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (err) {
      <span class="hljs-comment">// Handle error</span>
      res.status(<span class="hljs-number">500</span>).end(<span class="hljs-string">'Server Error'</span>);
      <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-comment">// Send the rendered HTML to the client</span>
    res.end(html);
  });
});

server.listen(<span class="hljs-number">8080</span>);
</code></pre>
<pre><code class="lang-typescript">&lt;!-- index.template.html --&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
  &lt;title&gt;Server-side Rendering Example&lt;/title&gt;
  &lt;!-- Resources injected by the server renderer --&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;!-- This will be replaced <span class="hljs-keyword">with</span> the app<span class="hljs-string">'s HTML --&gt;
  &lt;!--vue-ssr-outlet--&gt;
&lt;/body&gt;
&lt;/html&gt;</span>
</code></pre>
<pre><code class="lang-typescript"><span class="hljs-comment">// entry-server.js</span>
<span class="hljs-keyword">import</span> { createApp } <span class="hljs-keyword">from</span> <span class="hljs-string">'./app'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> context =&gt; {
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> { app, router } = createApp();

    <span class="hljs-comment">// Set server-side router's location</span>
    router.push(context.url);

    <span class="hljs-comment">// Wait until router has resolved possible async components and hooks</span>
    router.onReady(<span class="hljs-function">() =&gt;</span> {
      <span class="hljs-keyword">const</span> matchedComponents = router.getMatchedComponents();

      <span class="hljs-comment">// No matched routes, reject with 404</span>
      <span class="hljs-keyword">if</span> (!matchedComponents.length) {
        <span class="hljs-keyword">return</span> reject({ code: <span class="hljs-number">404</span> });
      }

      <span class="hljs-comment">// The Promise resolves to the app instance</span>
      resolve(app);
    }, reject);
  });
}
</code></pre>
<p>From the two processes above, you can see that the difference lies in the second step. A client-rendered website will directly return the HTML file, while a server-rendered website will render the page completely before returning this HTML file.</p>
<h4 id="heading-whats-the-benefit-of-doing-this-its-a-faster-time-to-content">What's the benefit of doing this? It's a faster time-to-content.</h4>
<p>Suppose your website needs to load four files (a, b, c, d) to render completely. And each file is 1 MB in size.</p>
<p>Calculating this way: a client-rendered website needs to load 4 files and an HTML file to complete the home page rendering, totaling 4MB (ignoring the HTML file size). While a server-rendered website only needs to load a fully rendered HTML file to complete the home page rendering, totaling the size of the already rendered HTML file (which isn't usually too large, generally a few hundred KB; my personal blog website (SSR) loads an HTML file of 400KB). <strong>This is why server-side rendering is faster.</strong></p>
<p>References:</p>
<ul>
<li><p><a target="_blank" href="https://github.com/woai3c/vue-ssr-demo">vue-ssr-demo</a></p>
</li>
<li><p><a target="_blank" href="https://ssr.vuejs.org/zh/">Vue.js Server-Side Rendering Guide</a></p>
</li>
</ul>
<h2 id="heading-4-use-a-cdn-for-static-resources"><strong>4. Use a CDN for Static Resources</strong></h2>
<p>A Content Delivery Network (CDN) is a set of web servers distributed across multiple geographic locations. We all know that the further the server is from the user, the higher the latency. CDNs are designed to solve this problem by deploying servers in multiple locations, bringing users closer to servers, thereby shortening request times.</p>
<h3 id="heading-cdn-principles">CDN Principles</h3>
<p>When a user visits a website without a CDN, the process is as follows:</p>
<ol>
<li><p>The browser needs to resolve the domain name into an IP address, so it makes a request to the local DNS.</p>
</li>
<li><p>The local DNS makes successive requests to the root server, top-level domain server, and authoritative server to get the IP address of the website's server.</p>
</li>
<li><p>The local DNS sends the IP address back to the browser, and the browser makes a request to the website server's IP address and receives the resources.</p>
</li>
</ol>
<p><a target="_blank" href="https://camo.githubusercontent.com/a9d8ea319521e8f560e8b68c2df8a4afaf27ed46e29e481b35bb78d013d23ca6/68747470733a2f2f6465762d746f2d75706c6f6164732e73332e616d617a6f6e6177732e636f6d2f75706c6f6164732f61727469636c65732f7a3079336a387a733733727a62617466616731342e706e67"><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%2Fz0y3j8zs73rzbatfag14.png" alt="Diagram showing request flow without CDN: browser → DNS → root servers → top-level domain → authoritative server → website server" width="600" height="400" loading="lazy"></a></p>
<p>If the user is visiting a website that has deployed a CDN, the process is as follows:</p>
<ol>
<li><p>The browser needs to resolve the domain name into an IP address, so it makes a request to the local DNS.</p>
</li>
<li><p>The local DNS makes successive requests to the root server, top-level domain server, and authoritative server to get the IP address of the Global Server Load Balancing (GSLB) system.</p>
</li>
<li><p>The local DNS then makes a request to the GSLB. The main function of the GSLB is to determine the user's location based on the local DNS's IP address, filter out the closest local Server Load Balancing (SLB) system to the user, and return the IP address of that SLB to the local DNS.</p>
</li>
<li><p>The local DNS sends the SLB's IP address back to the browser, and the browser makes a request to the SLB.</p>
</li>
<li><p>The SLB selects the optimal cache server based on the resource and address requested by the browser and sends it back to the browser.</p>
</li>
<li><p>The browser then redirects to the cache server based on the address returned by the SLB.</p>
</li>
<li><p>If the cache server has the resource the browser needs, it sends the resource back to the browser. If not, it requests the resource from the source server, sends it to the browser, and caches it locally.</p>
</li>
</ol>
<p><a target="_blank" href="https://camo.githubusercontent.com/1ade29f05689af94c1066bccedab884a119d2fb4cba44f08fd95357cd9abdef6/68747470733a2f2f6465762d746f2d75706c6f6164732e73332e616d617a6f6e6177732e636f6d2f75706c6f6164732f61727469636c65732f616f70776c68783778386f33726176766e3170322e706e67"><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%2Faopwlhx7x8o3ravvn1p2.png" alt="Diagram showing request flow with CDN: browser → DNS → root servers → GSLB → SLB → cache servers → origin server" width="600" height="400" loading="lazy"></a></p>
<p>References:</p>
<ul>
<li><p><a target="_blank" href="https://en.wikipedia.org/wiki/Content_delivery_network">Content delivery network(CDN)</a></p>
</li>
<li><p><a target="_blank" href="https://www.freecodecamp.org/news/how-cdns-improve-performance-in-front-end-projects/">How to use CDNs to improve performance</a></p>
</li>
</ul>
<h2 id="heading-5-place-css-in-the-head-and-javascript-files-at-the-bottom"><strong>5. Place CSS in the Head and JavaScript Files at the Bottom</strong></h2>
<ul>
<li><p>CSS execution blocks rendering and prevents JS execution</p>
</li>
<li><p>JS loading and execution block HTML parsing and prevent CSSOM construction</p>
</li>
</ul>
<p>If these CSS and JS tags are placed in the HEAD tag, and they take a long time to load and parse, then the page will be blank. So you should place JS files at the bottom (not blocking DOM parsing but will block rendering) so that HTML parsing is completed before loading JS files. This presents the page content to the user as early as possible.</p>
<p>So then you might be wondering – why should CSS files still be placed in the head?</p>
<p>Because loading HTML first and then loading CSS will make users see an unstyled, "ugly" page at first glance. To avoid this situation, place CSS files in the head.</p>
<p>You can also place JS files in the head as long as the script tag has the defer attribute, which means asynchronous download and delayed execution.</p>
<p><strong>Here's an example of optimal placement:</strong></p>
<pre><code class="lang-typescript">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
  &lt;meta charset=<span class="hljs-string">"UTF-8"</span>&gt;
  &lt;title&gt;Optimized Resource Loading&lt;/title&gt;

  &lt;!-- CSS <span class="hljs-keyword">in</span> the head <span class="hljs-keyword">for</span> faster rendering --&gt;
  &lt;link rel=<span class="hljs-string">"stylesheet"</span> href=<span class="hljs-string">"styles.css"</span>&gt;

  &lt;!-- Critical JS that must load early can use defer --&gt;
  &lt;script defer src=<span class="hljs-string">"critical.js"</span>&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;header&gt;
    &lt;h1&gt;My Website&lt;/h1&gt;
    &lt;!-- Page content here --&gt;
  &lt;/header&gt;

  &lt;main&gt;
    &lt;p&gt;Content that users need to see quickly...&lt;/p&gt;
  &lt;/main&gt;

  &lt;footer&gt;
    &lt;!-- Footer content --&gt;
  &lt;/footer&gt;

  &lt;!-- Non-critical JavaScript at the bottom --&gt;
  &lt;script src=<span class="hljs-string">"app.js"</span>&gt;&lt;/script&gt;
  &lt;script src=<span class="hljs-string">"analytics.js"</span>&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p><strong>Explanation of this approach:</strong></p>
<ol>
<li><p><strong>CSS in the</strong> <code>&lt;head&gt;</code>: Ensures the page is styled as soon as it renders, preventing the "flash of unstyled content" (FOUC). CSS is render-blocking, but that's actually what we want in this case.</p>
</li>
<li><p><strong>Critical JS with</strong> <code>defer</code>: The <code>defer</code> attribute tells the browser to:</p>
<ul>
<li><p>Download the script in parallel while parsing HTML</p>
</li>
<li><p>Only execute the script after HTML parsing is complete but before the <code>DOMContentLoaded</code> event</p>
</li>
<li><p>Maintain the order of execution if there are multiple deferred scripts</p>
</li>
</ul>
</li>
<li><p><strong>Non-critical JS before closing</strong> <code>&lt;/body&gt;</code>: Scripts without special attributes will:</p>
<ul>
<li><p>Block HTML parsing while they download and execute</p>
</li>
<li><p>By placing them at the bottom, we ensure that all the important content is parsed and displayed first</p>
</li>
<li><p>This improves perceived performance even if the total load time is the same</p>
</li>
</ul>
</li>
</ol>
<p>You can also use <code>async</code> for scripts that don't depend on DOM or other scripts:</p>
<pre><code class="lang-typescript">&lt;script <span class="hljs-keyword">async</span> src=<span class="hljs-string">"independent.js"</span>&gt;&lt;/script&gt;
</code></pre>
<p>The <code>async</code> attribute will download the script in parallel and execute it as soon as it's available, which may interrupt HTML parsing. Use this only for scripts that don't modify the DOM or depend on other scripts.</p>
<p>Reference:</p>
<ul>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Learn_web_development/Getting_started/Your_first_website/Adding_interactivity">Adding Interactivity with JavaScript</a></li>
</ul>
<h2 id="heading-6-use-font-icons-iconfont-instead-of-image-icons"><strong>6. Use Font Icons (iconfont) Instead of Image Icons</strong></h2>
<p>A font icon is an icon made into a font. When using it, it's just like a font, and you can set attributes such as font-size, color, and so on, which is very convenient. Font icons are also vector graphics and won't lose clarity. Another advantage is that the generated files are particularly small.</p>
<h3 id="heading-compress-font-files">Compress Font Files</h3>
<p>Use the <a target="_blank" href="https://github.com/patrickhulce/fontmin-webpack">fontmin-webpack</a> plugin to compress font files (thanks to <a target="_blank" href="https://juejin.im/user/237150239985165">Frontend Xiaowei</a> for providing this).</p>
<p><a target="_blank" href="https://camo.githubusercontent.com/8aec44850415bdf6f23aa59cae5daa0c6d06ec9414766ddfe34c294b663fcde4/68747470733a2f2f696d672d626c6f672e6373646e696d672e636e2f696d675f636f6e766572742f37376232656235653365303933323030383765333337303638366461393330302e706e67"><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%2Flmbq5m02e5myhbyz7c5d.png" alt="Showing difference between uncompressed and compressed files" width="600" height="400" loading="lazy"></a></p>
<p>References:</p>
<ul>
<li><p><a target="_blank" href="https://github.com/patrickhulce/fontmin-webpack">fontmin-webpack</a></p>
</li>
<li><p><a target="_blank" href="https://www.iconfont.cn/">Iconfont-Alibaba Vector Icon Library</a></p>
</li>
</ul>
<h2 id="heading-7-make-good-use-of-caching-avoid-reloading-the-same-resources"><strong>7. Make Good Use of Caching, Avoid Reloading the Same Resources</strong></h2>
<p>To prevent users from having to request files every time they visit a website, we can control this behavior by adding Expires or max-age. Expires sets a time, and as long as it's before this time, the browser won't request the file but will directly use the cache. Max-age is a relative time, and it's recommended to use max-age instead of Expires.</p>
<p>But this creates a problem: what happens when the file is updated? How do we notify the browser to request the file again?</p>
<p>This can be done by updating the resource link addresses referenced in the page, making the browser actively abandon the cache and load new resources.</p>
<p>The specific approach is to associate the URL modification of the resource address with the file content, which means that only when the file content changes, the corresponding URL will change. This achieves file-level precise cache control.</p>
<p>So what is related to file content? We naturally think of using <a target="_blank" href="https://www.okta.com/identity-101/md5/">digest algorithms</a> to derive digest information for the file. The digest information corresponds one-to-one with the file content, providing a basis for cache control that's precise to the granularity of individual files.</p>
<h3 id="heading-how-to-implement-caching-and-cache-busting"><strong>How to implement caching and cache-busting:</strong></h3>
<p><strong>1. Server-side cache headers (using Express.js as an example):</strong></p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Set cache control headers for static resources</span>
app.use(<span class="hljs-string">'/static'</span>, express.static(<span class="hljs-string">'public'</span>, {
  maxAge: <span class="hljs-string">'1y'</span>, <span class="hljs-comment">// Cache for 1 year</span>
  etag: <span class="hljs-literal">true</span>,   <span class="hljs-comment">// Use ETag for validation</span>
  lastModified: <span class="hljs-literal">true</span> <span class="hljs-comment">// Use Last-Modified for validation</span>
}));

<span class="hljs-comment">// For HTML files that shouldn't be cached as long</span>
app.get(<span class="hljs-string">'/*.html'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  res.set({
    <span class="hljs-string">'Cache-Control'</span>: <span class="hljs-string">'public, max-age=300'</span>, <span class="hljs-comment">// Cache for 5 minutes</span>
    <span class="hljs-string">'Expires'</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(<span class="hljs-built_in">Date</span>.now() + <span class="hljs-number">300000</span>).toUTCString()
  });
  <span class="hljs-comment">// Send HTML content</span>
});
</code></pre>
<p><strong>2. Using content hashes in filenames (Webpack configuration):</strong></p>
<pre><code class="lang-typescript"><span class="hljs-comment">// webpack.config.js</span>
<span class="hljs-built_in">module</span>.<span class="hljs-built_in">exports</span> = {
  output: {
    filename: <span class="hljs-string">'[name].[contenthash].js'</span>, <span class="hljs-comment">// Uses content hash in filename</span>
    path: path.resolve(__dirname, <span class="hljs-string">'dist'</span>),
  },
  plugins: [
    <span class="hljs-comment">// Extract CSS into separate files with content hash</span>
    <span class="hljs-keyword">new</span> MiniCssExtractPlugin({
      filename: <span class="hljs-string">'[name].[contenthash].css'</span>
    }),
    <span class="hljs-comment">// Generate HTML with correct hashed filenames</span>
    <span class="hljs-keyword">new</span> HtmlWebpackPlugin({
      template: <span class="hljs-string">'src/index.html'</span>
    })
  ]
};
</code></pre>
<p>This will produce output files like:</p>
<ul>
<li><p><code>main.8e0d62a10c151dad4f8e.js</code></p>
</li>
<li><p><code>styles.f4e3a77c616562b26ca1.css</code></p>
</li>
</ul>
<p>When you change the content of a file, its hash will change, forcing the browser to download the new file instead of using the cached version.</p>
<p><strong>3. Example of generated HTML with cache-busting:</strong></p>
<pre><code class="lang-typescript">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
  &lt;meta charset=<span class="hljs-string">"UTF-8"</span>&gt;
  &lt;title&gt;Cache Busting Example&lt;/title&gt;
  &lt;!-- Note the content hash <span class="hljs-keyword">in</span> the filename --&gt;
  &lt;link rel=<span class="hljs-string">"stylesheet"</span> href=<span class="hljs-string">"/static/styles.f4e3a77c616562b26ca1.css"</span>&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;div id=<span class="hljs-string">"app"</span>&gt;&lt;/div&gt;
  &lt;!-- Script <span class="hljs-keyword">with</span> content hash --&gt;
  &lt;script src=<span class="hljs-string">"/static/main.8e0d62a10c151dad4f8e.js"</span>&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p><strong>4. Version query parameters (simpler but less effective approach):</strong></p>
<pre><code class="lang-typescript">&lt;link rel=<span class="hljs-string">"stylesheet"</span> href=<span class="hljs-string">"styles.css?v=1.2.3"</span>&gt;
&lt;script src=<span class="hljs-string">"app.js?v=1.2.3"</span>&gt;&lt;/script&gt;
</code></pre>
<p>When updating files, manually change the version number to force a new download.</p>
<p>References:</p>
<ul>
<li><a target="_blank" href="https://webpack.js.org/guides/caching/#root">webpack-caching</a></li>
</ul>
<h2 id="heading-8-compress-files"><strong>8. Compress Files</strong></h2>
<p>Compressing files can reduce file download time, providing a better user experience.</p>
<p>Thanks to the development of Webpack and Node, file compression is now very convenient.</p>
<p>In Webpack, you can use the following plugins for compression:</p>
<ul>
<li><p>JavaScript: UglifyPlugin</p>
</li>
<li><p>CSS: MiniCssExtractPlugin</p>
</li>
<li><p>HTML: HtmlWebpackPlugin</p>
</li>
</ul>
<p>In fact, we can do even better by using gzip compression. This can be enabled by adding the gzip identifier to the Accept-Encoding header in the HTTP request header. Of course, the server must also support this feature.</p>
<p>Gzip is currently the most popular and effective compression method. For example, the app.js file generated after building a project I developed with Vue has a size of 1.4MB, but after gzip compression, it's only 573KB, reducing the volume by nearly 60%.</p>
<p>Here are the methods for configuring gzip in webpack and node.</p>
<p><strong>Download plugins</strong></p>
<pre><code class="lang-typescript">npm install compression-webpack-plugin --save-dev
npm install compression
</code></pre>
<p><strong>Webpack configuration</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> CompressionPlugin = <span class="hljs-built_in">require</span>(<span class="hljs-string">'compression-webpack-plugin'</span>);

<span class="hljs-built_in">module</span>.<span class="hljs-built_in">exports</span> = {
  plugins: [<span class="hljs-keyword">new</span> CompressionPlugin()],
}
</code></pre>
<p><strong>Node configuration</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> compression = <span class="hljs-built_in">require</span>(<span class="hljs-string">'compression'</span>)
<span class="hljs-comment">// Use before other middleware</span>
app.use(compression())
</code></pre>
<h2 id="heading-9-image-optimization"><strong>9. Image Optimization</strong></h2>
<h3 id="heading-1-lazy-loading-images"><strong>1. Lazy Loading Images</strong></h3>
<p>In a page, don't initially set the path for images – only load the actual image when it appears in the browser's viewport. This is lazy loading. For websites with many images, loading all images at once can have a significant impact on user experience, so image lazy loading is necessary.</p>
<p>First, set up the images like this, where images won't load when they're not visible in the page:</p>
<pre><code class="lang-typescript">&lt;img data-src=<span class="hljs-string">"https://avatars0.githubusercontent.com/u/22117876?s=460&amp;u=7bd8f32788df6988833da6bd155c3cfbebc68006&amp;v=4"</span>&gt;
</code></pre>
<p>When the page becomes visible, use JS to load the image:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> img = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'img'</span>)
img.src = img.dataset.src
</code></pre>
<p>This is how the image gets loaded. For the complete code, please refer to the reference materials.</p>
<p>Reference:</p>
<ul>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/Performance/Guides/Lazy_loading">Lazy loading images for the web</a></li>
</ul>
<h3 id="heading-2-responsive-images"><strong>2. Responsive Images</strong></h3>
<p>The advantage of responsive images is that browsers can automatically load appropriate images based on screen size.</p>
<p>Implementation through <code>picture</code>:</p>
<pre><code class="lang-typescript">&lt;picture&gt;
    &lt;source srcset=<span class="hljs-string">"banner_w1000.jpg"</span> media=<span class="hljs-string">"(min-width: 801px)"</span>&gt;
    &lt;source srcset=<span class="hljs-string">"banner_w800.jpg"</span> media=<span class="hljs-string">"(max-width: 800px)"</span>&gt;
    &lt;img src=<span class="hljs-string">"banner_w800.jpg"</span> alt=<span class="hljs-string">""</span>&gt;
&lt;/picture&gt;
</code></pre>
<p>Implementation through <code>@media</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-meta">@media</span> (min-width: <span class="hljs-number">769</span>px) {
    .bg {
        background-image: url(bg1080.jpg);
    }
}
<span class="hljs-meta">@media</span> (max-width: <span class="hljs-number">768</span>px) {
    .bg {
        background-image: url(bg768.jpg);
    }
}
</code></pre>
<h3 id="heading-3-adjust-image-size"><strong>3. Adjust Image Size</strong></h3>
<p>For example, if you have a 1920 * 1080 size image, you show it to users as a thumbnail, and only display the full image when users hover over it. If users never actually hover over the thumbnail, the time spent downloading the image is wasted.</p>
<p>So we can optimize this with two images. Initially, only load the thumbnail, and when users hover over the image, then load the large image. Another approach is to lazy load the large image, manually changing the src of the large image to download it after all elements have loaded.</p>
<p><strong>Example implementation of image size optimization:</strong></p>
<pre><code class="lang-typescript">&lt;!-- HTML Structure --&gt;
&lt;div <span class="hljs-keyword">class</span>=<span class="hljs-string">"image-container"</span>&gt;
  &lt;img <span class="hljs-keyword">class</span>=<span class="hljs-string">"thumbnail"</span> src=<span class="hljs-string">"thumbnail-small.jpg"</span> alt=<span class="hljs-string">"Small thumbnail"</span>&gt;
  &lt;img <span class="hljs-keyword">class</span>=<span class="hljs-string">"full-size"</span> data-src=<span class="hljs-string">"image-large.jpg"</span> alt=<span class="hljs-string">"Full-size image"</span>&gt;
&lt;/div&gt;
</code></pre>
<pre><code class="lang-typescript"><span class="hljs-comment">/* CSS for the container and images */</span>
.image-container {
  position: relative;
  width: <span class="hljs-number">200</span>px;
  height: <span class="hljs-number">150</span>px;
  overflow: hidden;
}

.thumbnail {
  width: <span class="hljs-number">100</span>%;
  height: <span class="hljs-number">100</span>%;
  <span class="hljs-built_in">object</span>-fit: cover;
  display: block;
}

.full-size {
  display: none;
  position: absolute;
  top: <span class="hljs-number">0</span>;
  left: <span class="hljs-number">0</span>;
  z-index: <span class="hljs-number">2</span>;
  max-width: <span class="hljs-number">600</span>px;
  max-height: <span class="hljs-number">400</span>px;
}

<span class="hljs-comment">/* Show full size on hover */</span>
.image-container:hover .full-size {
  display: block;
}
</code></pre>
<pre><code class="lang-typescript"><span class="hljs-comment">// JavaScript to lazy load the full-size image</span>
<span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">'DOMContentLoaded'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> containers = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">'.image-container'</span>);

  containers.forEach(<span class="hljs-function"><span class="hljs-params">container</span> =&gt;</span> {
    <span class="hljs-keyword">const</span> thumbnail = container.querySelector(<span class="hljs-string">'.thumbnail'</span>);
    <span class="hljs-keyword">const</span> fullSize = container.querySelector(<span class="hljs-string">'.full-size'</span>);

    <span class="hljs-comment">// Load the full-size image when the user hovers over the thumbnail</span>
    container.addEventListener(<span class="hljs-string">'mouseenter'</span>, <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-keyword">if</span> (!fullSize.src &amp;&amp; fullSize.dataset.src) {
        fullSize.src = fullSize.dataset.src;
      }
    });

    <span class="hljs-comment">// Alternative: Load the full-size image after the page loads completely</span>
    <span class="hljs-comment">/*
    window.addEventListener('load', () =&gt; {
      setTimeout(() =&gt; {
        if (!fullSize.src &amp;&amp; fullSize.dataset.src) {
          fullSize.src = fullSize.dataset.src;
        }
      }, 1000); // Delay loading by 1 second after window load
    });
    */</span>
  });
});
</code></pre>
<p>This implementation:</p>
<ol>
<li><p>Shows only the thumbnail initially</p>
</li>
<li><p>Loads the full-size image only when the user hovers over the thumbnail</p>
</li>
<li><p>Provides an alternative approach to load all full-size images with a delay after page load</p>
</li>
</ol>
<h3 id="heading-4-reduce-image-quality"><strong>4. Reduce Image Quality</strong></h3>
<p>For example, with JPG format images, there's usually no noticeable difference between 100% quality and 90% quality, especially when used as background images. When cutting background images in Adobe Photoshop, I often cut the image into JPG format and compress it to 60% quality, and basically can't see any difference.</p>
<p>There are two compression methods: one is through the Webpack plugin <code>image-webpack-loader</code>, and the other is through online compression websites.</p>
<p>Here's how to use the Webpack plugin <code>image-webpack-loader</code>:</p>
<pre><code class="lang-typescript">npm i -D image-webpack-loader
</code></pre>
<p>Webpack configuration:</p>
<pre><code class="lang-typescript">{
  test: <span class="hljs-regexp">/\.(png|jpe?g|gif|svg)(\?.*)?$/</span>,
  use:[
    {
    loader: <span class="hljs-string">'url-loader'</span>,
    options: {
      limit: <span class="hljs-number">10000</span>, <span class="hljs-comment">/* Images smaller than 1000 bytes will be automatically converted to base64 code references */</span>
      name: utils.assetsPath(<span class="hljs-string">'img/[name].[hash:7].[ext]'</span>)
      }
    },
    <span class="hljs-comment">/* Compress images */</span>
    {
      loader: <span class="hljs-string">'image-webpack-loader'</span>,
      options: {
        bypassOnDebug: <span class="hljs-literal">true</span>,
      }
    }
  ]
}
</code></pre>
<h3 id="heading-5-use-css3-effects-instead-of-images-when-possible"><strong>5. Use CSS3 Effects Instead of Images When Possible</strong></h3>
<p>Many images can be drawn with CSS effects (gradients, shadows, and so on). In these cases, CSS3 effects are better. This is because code size is usually a fraction or even a tenth of the image size.</p>
<p>Reference:</p>
<ul>
<li><a target="_blank" href="https://webpack.js.org/guides/asset-management/">Asset Management</a></li>
</ul>
<h3 id="heading-6-use-webp-to-format-images"><strong>6. Use WebP to Format Images</strong></h3>
<p>WebP's advantage is reflected in its better image data compression algorithm, which brings smaller image volume while maintaining image quality that's indistinguishable to the naked eye. It also has lossless and lossy compression modes, Alpha transparency, and animation features. Its conversion effects on JPEG and PNG are quite excellent, stable, and uniform.</p>
<p><strong>Example of implementing WebP with fallbacks:</strong></p>
<pre><code class="lang-typescript">&lt;!-- Using the picture element <span class="hljs-keyword">for</span> WebP <span class="hljs-keyword">with</span> fallback --&gt;
&lt;picture&gt;
  &lt;source srcset=<span class="hljs-string">"image.webp"</span> <span class="hljs-keyword">type</span>=<span class="hljs-string">"image/webp"</span>&gt;
  &lt;source srcset=<span class="hljs-string">"image.jpg"</span> <span class="hljs-keyword">type</span>=<span class="hljs-string">"image/jpeg"</span>&gt;
  &lt;img src=<span class="hljs-string">"image.jpg"</span> alt=<span class="hljs-string">"Description of the image"</span>&gt;
&lt;/picture&gt;
</code></pre>
<p><strong>Server-side WebP detection and serving:</strong></p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Express.js example</span>
app.get(<span class="hljs-string">'/images/:imageName'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> supportsWebP = req.headers.accept &amp;&amp; req.headers.accept.includes(<span class="hljs-string">'image/webp'</span>);
  <span class="hljs-keyword">const</span> imagePath = supportsWebP 
    ? <span class="hljs-string">`public/images/<span class="hljs-subst">${req.params.imageName}</span>.webp`</span> 
    : <span class="hljs-string">`public/images/<span class="hljs-subst">${req.params.imageName}</span>.jpg`</span>;

  res.sendFile(path.resolve(__dirname, imagePath));
});
</code></pre>
<p>Reference:</p>
<ul>
<li><a target="_blank" href="https://en.wikipedia.org/wiki/WebP">WebP</a></li>
</ul>
<h2 id="heading-10-load-code-on-demand-through-webpack-extract-third-party-libraries-reduce-redundant-code-when-converting-es6-to-es5"><strong>10. Load Code on Demand Through Webpack, Extract Third-Party Libraries, Reduce Redundant Code When Converting ES6 to ES5</strong></h2>
<p>The following quote from the official Webpack documentation explains the concept of lazy loading:</p>
<blockquote>
<p>"Lazy loading or on-demand loading is a great way to optimize a website or application. This approach actually separates your code at some logical breakpoints, and then immediately references or is about to reference some new code blocks after completing certain operations in some code blocks. This speeds up the initial loading of the application and lightens its overall volume because some code blocks may never be loaded." <em>Source:</em> <a target="_blank" href="http://webpack.docschina.org/guides/lazy-loading/"><em>Lazy Loading</em></a></p>
</blockquote>
<p><strong>Note:</strong> While image lazy loading (discussed in section 9.1) delays the loading of image resources until they're visible in the viewport, code lazy loading splits JavaScript bundles and loads code fragments only when they're needed for specific functionality. They both improve initial load time, but they work at different levels of resource optimization.</p>
<h3 id="heading-generate-file-names-based-on-file-content-combined-with-import-dynamic-import-of-components-to-achieve-on-demand-loading"><strong>Generate File Names Based on File Content, Combined with Import Dynamic Import of Components to Achieve On-Demand Loading</strong></h3>
<p>This requirement can be achieved by configuring the filename property of output. One of the value options in the filename property is <code>[contenthash]</code>, which creates a unique hash based on file content. When the file content changes, <code>[contenthash]</code> also changes.</p>
<pre><code class="lang-typescript">output: {
    filename: <span class="hljs-string">'[name].[contenthash].js'</span>,
    chunkFilename: <span class="hljs-string">'[name].[contenthash].js'</span>,
    path: path.resolve(__dirname, <span class="hljs-string">'../dist'</span>),
},
</code></pre>
<p><strong>Example of code lazy loading in a Vue application:</strong></p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Instead of importing synchronously like this:</span>
<span class="hljs-comment">// import UserProfile from './components/UserProfile.vue'</span>

<span class="hljs-comment">// Use dynamic import for route components:</span>
<span class="hljs-keyword">const</span> UserProfile = <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">'./components/UserProfile.vue'</span>)

<span class="hljs-comment">// Then use it in your routes</span>
<span class="hljs-keyword">const</span> router = <span class="hljs-keyword">new</span> VueRouter({
  routes: [
    { path: <span class="hljs-string">'/user/:id'</span>, component: UserProfile }
  ]
})
</code></pre>
<p>This ensures the UserProfile component is only loaded when a user navigates to that route, not on initial page load.</p>
<h3 id="heading-extract-third-party-libraries"><strong>Extract Third-Party Libraries</strong></h3>
<p>Since imported third-party libraries are generally stable and don't change frequently, extracting them separately as long-term caches is a better choice. This requires using the cacheGroups option of Webpack4's splitChunk plugin.</p>
<pre><code class="lang-typescript">optimization: {
    runtimeChunk: {
        name: <span class="hljs-string">'manifest'</span> <span class="hljs-comment">// Split webpack's runtime code into a separate chunk.</span>
    },
    splitChunks: {
        cacheGroups: {
            vendor: {
                name: <span class="hljs-string">'chunk-vendors'</span>,
                test: <span class="hljs-regexp">/[\\/]node_modules[\\/]/</span>,
                priority: <span class="hljs-number">-10</span>,
                chunks: <span class="hljs-string">'initial'</span>
            },
            common: {
                name: <span class="hljs-string">'chunk-common'</span>,
                minChunks: <span class="hljs-number">2</span>,
                priority: <span class="hljs-number">-20</span>,
                chunks: <span class="hljs-string">'initial'</span>,
                reuseExistingChunk: <span class="hljs-literal">true</span>
            }
        },
    }
},
</code></pre>
<ul>
<li><p><code>test</code>: Used to control which modules are matched by this cache group. If passed unchanged, it defaults to select all modules. Types of values that can be passed: <code>RegExp</code>, <code>String</code>, and <code>Function</code>.</p>
</li>
<li><p><code>priority</code>: Indicates extraction weight, with higher numbers indicating higher priority. Since a module might meet the conditions of multiple <code>cacheGroups</code>, extraction is determined by the highest weight.</p>
</li>
<li><p><code>reuseExistingChunk</code>: Indicates whether to use existing chunks. If true, it means that if the current chunk contains modules that have already been extracted, new ones won't be generated.</p>
</li>
<li><p><code>minChunks</code> (default is 1): The minimum number of times this code block should be referenced before splitting (note: to ensure code block reusability, the default strategy doesn't require multiple references to be split).</p>
</li>
<li><p><code>chunks</code> (default is async): initial, async, and all.</p>
</li>
<li><p><code>name</code> (name of the packaged chunks): String or function (functions can customize names based on conditions).</p>
</li>
</ul>
<h3 id="heading-reduce-redundant-code-when-converting-es6-to-es5"><strong>Reduce Redundant Code When Converting ES6 to ES5</strong></h3>
<p>To achieve the same functionality as the original code after Babel conversion, some helper functions are needed. For example this:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> Person {}
</code></pre>
<p>will be converted to this:</p>
<pre><code class="lang-typescript"><span class="hljs-meta">"use strict"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_classCallCheck</span>(<span class="hljs-params">instance, Constructor</span>) </span>{
  <span class="hljs-keyword">if</span> (!(instance <span class="hljs-keyword">instanceof</span> Constructor)) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">TypeError</span>(<span class="hljs-string">"Cannot call a class as a function"</span>);
  }
}

<span class="hljs-keyword">var</span> Person = <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Person</span>(<span class="hljs-params"></span>) </span>{
  _classCallCheck(<span class="hljs-built_in">this</span>, Person);
};
</code></pre>
<p>Here, <code>_classCallCheck</code> is a <code>helper</code> function. If classes are declared in many files, then many such <code>helper</code> functions will be generated.</p>
<p>The <code>@babel/runtime</code> package declares all the helper functions needed, and the role of <code>@babel/plugin-transform-runtime</code> is to import all files that need <code>helper</code> functions from the <code>@babel/runtime package</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-meta">"use strict"</span>;

<span class="hljs-keyword">var</span> _classCallCheck2 = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@babel/runtime/helpers/classCallCheck"</span>);

<span class="hljs-keyword">var</span> _classCallCheck3 = _interopRequireDefault(_classCallCheck2);

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_interopRequireDefault</span>(<span class="hljs-params">obj</span>) </span>{
  <span class="hljs-keyword">return</span> obj &amp;&amp; obj.__esModule ? obj : { <span class="hljs-keyword">default</span>: obj };
}

<span class="hljs-keyword">var</span> Person = <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Person</span>(<span class="hljs-params"></span>) </span>{
  (<span class="hljs-number">0</span>, _classCallCheck3.default)(<span class="hljs-built_in">this</span>, Person);
};
</code></pre>
<p>Here, the <code>helper</code> function <code>classCallCheck</code> is no longer compiled, but instead references <code>helpers/classCallCheck</code> from <code>@babel/runtime</code>.</p>
<p><strong>Installation:</strong></p>
<pre><code class="lang-typescript">npm i -D <span class="hljs-meta">@babel</span>/plugin-transform-runtime <span class="hljs-meta">@babel</span>/runtime
</code></pre>
<p><strong>Usage:</strong><br>In the <code>.babelrc</code> file,</p>
<pre><code class="lang-typescript"><span class="hljs-string">"plugins"</span>: [
        <span class="hljs-string">"@babel/plugin-transform-runtime"</span>
]
</code></pre>
<p>References:</p>
<ul>
<li><p><a target="_blank" href="https://babeljs.io/">Babel</a></p>
</li>
<li><p><a target="_blank" href="https://router.vuejs.org/guide/advanced/lazy-loading.html">Vue Route Lazy Loading</a></p>
</li>
<li><p><a target="_blank" href="https://webpack.js.org/plugins/split-chunks-plugin/">SplitChunksPlugin</a></p>
</li>
</ul>
<h2 id="heading-11-reduce-reflows-and-repaints"><strong>11. Reduce Reflows and Repaints</strong></h2>
<h3 id="heading-browser-rendering-process"><strong>Browser Rendering Process</strong></h3>
<ol>
<li><p>Parse HTML to generate DOM tree.</p>
</li>
<li><p>Parse CSS to generate CSSOM rules tree.</p>
</li>
<li><p>Combine DOM tree and CSSOM rules tree to generate rendering tree.</p>
</li>
<li><p>Traverse the rendering tree to begin layout, calculating the position and size information of each node.</p>
</li>
<li><p>Paint each node of the rendering tree to the screen.</p>
</li>
</ol>
<p><a target="_blank" href="https://camo.githubusercontent.com/b01f818aab6cf14622f77ee3d2407b961b38b4654ab88c3fa391d2b43a77c46c/68747470733a2f2f696d672d626c6f672e6373646e696d672e636e2f696d675f636f6e766572742f35363437643961643461643561353731373839313964656165353137356238332e706e67"><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%2Ft7yfvoxwqdvs7a9c6v9b.png" alt="Diagram of browser rendering process showing the steps from HTML/CSS to rendered pixels" width="600" height="400" loading="lazy"></a></p>
<h3 id="heading-reflow"><strong>Reflow</strong></h3>
<p>When the position or size of DOM elements is changed, the browser needs to regenerate the rendering tree, a process called reflow.</p>
<h3 id="heading-repaint"><strong>Repaint</strong></h3>
<p>After regenerating the rendering tree, each node of the rendering tree needs to be painted to the screen, a process called repaint. Not all actions will cause reflow – for example, changing font color will only cause repaint. Remember, reflow will cause repaint, but repaint will not cause reflow.</p>
<p>Both reflow and repaint operations are very expensive because the JavaScript engine thread and the GUI rendering thread are mutually exclusive, and only one can work at a time.</p>
<p>What operations will cause reflow?</p>
<ul>
<li><p>Adding or removing visible DOM elements</p>
</li>
<li><p>Element position changes</p>
</li>
<li><p>Element size changes</p>
</li>
<li><p>Content changes</p>
</li>
<li><p>Browser window size changes</p>
</li>
</ul>
<p>How to reduce reflows and repaints?</p>
<ul>
<li><p>When modifying styles with JavaScript, it's best not to write styles directly, but to replace classes to change styles.</p>
</li>
<li><p>If you need to perform a series of operations on a DOM element, you can take the DOM element out of the document flow, make modifications, and then bring it back to the document. It's recommended to use hidden elements (display:none) or document fragments (DocumentFragement), both of which can implement this approach well.</p>
</li>
</ul>
<p><strong>Example of causing unnecessary reflows (inefficient):</strong></p>
<pre><code class="lang-typescript"><span class="hljs-comment">// This causes multiple reflows as each style change triggers a reflow</span>
<span class="hljs-keyword">const</span> element = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'myElement'</span>);
element.style.width = <span class="hljs-string">'100px'</span>;
element.style.height = <span class="hljs-string">'200px'</span>;
element.style.margin = <span class="hljs-string">'10px'</span>;
element.style.padding = <span class="hljs-string">'20px'</span>;
element.style.borderRadius = <span class="hljs-string">'5px'</span>;
</code></pre>
<p><strong>Optimized version 1 – using CSS classes:</strong></p>
<pre><code class="lang-typescript"><span class="hljs-comment">/* style.css */</span>
.my-modified-element {
  width: <span class="hljs-number">100</span>px;
  height: <span class="hljs-number">200</span>px;
  margin: <span class="hljs-number">10</span>px;
  padding: <span class="hljs-number">20</span>px;
  border-radius: <span class="hljs-number">5</span>px;
}
</code></pre>
<pre><code class="lang-typescript"><span class="hljs-comment">// Only one reflow happens when the class is added</span>
<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'myElement'</span>).classList.add(<span class="hljs-string">'my-modified-element'</span>);
</code></pre>
<p><strong>Optimized version 2 – batching style changes:</strong></p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Batching style changes using cssText</span>
<span class="hljs-keyword">const</span> element = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'myElement'</span>);
element.style.cssText = <span class="hljs-string">'width: 100px; height: 200px; margin: 10px; padding: 20px; border-radius: 5px;'</span>;
</code></pre>
<p><strong>Optimized version 3 – using document fragments (for multiple elements):</strong></p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Instead of adding elements one by one</span>
<span class="hljs-keyword">const</span> list = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'myList'</span>);
<span class="hljs-keyword">const</span> fragment = <span class="hljs-built_in">document</span>.createDocumentFragment();

<span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">100</span>; i++) {
  <span class="hljs-keyword">const</span> item = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'li'</span>);
  item.textContent = <span class="hljs-string">`Item <span class="hljs-subst">${i}</span>`</span>;
  fragment.appendChild(item);
}

<span class="hljs-comment">// Only one reflow happens when the fragment is appended</span>
list.appendChild(fragment);
</code></pre>
<p><strong>Optimized version 4 – take element out of flow, modify, then reinsert:</strong></p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Remove from DOM, make changes, then reinsert</span>
<span class="hljs-keyword">const</span> element = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'myElement'</span>);
<span class="hljs-keyword">const</span> parent = element.parentNode;
<span class="hljs-keyword">const</span> nextSibling = element.nextSibling;

<span class="hljs-comment">// Remove (causes one reflow)</span>
parent.removeChild(element);

<span class="hljs-comment">// Make multiple changes (no reflows while detached)</span>
element.style.width = <span class="hljs-string">'100px'</span>;
element.style.height = <span class="hljs-string">'200px'</span>;
element.style.margin = <span class="hljs-string">'10px'</span>;
element.style.padding = <span class="hljs-string">'20px'</span>;
element.style.borderRadius = <span class="hljs-string">'5px'</span>;

<span class="hljs-comment">// Reinsert (causes one more reflow)</span>
<span class="hljs-keyword">if</span> (nextSibling) {
  parent.insertBefore(element, nextSibling);
} <span class="hljs-keyword">else</span> {
  parent.appendChild(element);
}
</code></pre>
<p><strong>Optimized version 5 – using display:none temporarily:</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> element = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'myElement'</span>);

<span class="hljs-comment">// Hide element (one reflow)</span>
element.style.display = <span class="hljs-string">'none'</span>;

<span class="hljs-comment">// Make multiple changes (no reflows while hidden)</span>
element.style.width = <span class="hljs-string">'100px'</span>;
element.style.height = <span class="hljs-string">'200px'</span>;
element.style.margin = <span class="hljs-string">'10px'</span>;
element.style.padding = <span class="hljs-string">'20px'</span>;
element.style.borderRadius = <span class="hljs-string">'5px'</span>;

<span class="hljs-comment">// Show element again (one more reflow)</span>
element.style.display = <span class="hljs-string">'block'</span>;
</code></pre>
<p>By using these optimization techniques, you can significantly reduce the number of reflows and repaints, leading to smoother performance, especially for animations and dynamic content updates.</p>
<h2 id="heading-12-use-event-delegation"><strong>12. Use Event Delegation</strong></h2>
<p>Event delegation takes advantage of event bubbling, allowing you to specify a single event handler to manage all events of a particular type. All events that use buttons (most mouse events and keyboard events) are suitable for the event delegation technique. Using event delegation can save memory.</p>
<pre><code class="lang-typescript">&lt;ul&gt;
  &lt;li&gt;Apple&lt;/li&gt;
  &lt;li&gt;Banana&lt;/li&gt;
  &lt;li&gt;Pineapple&lt;/li&gt;
&lt;/ul&gt;

<span class="hljs-comment">// good</span>
<span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'ul'</span>).onclick = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> target = event.target
  <span class="hljs-keyword">if</span> (target.nodeName === <span class="hljs-string">'LI'</span>) {
    <span class="hljs-built_in">console</span>.log(target.innerHTML)
  }
}

<span class="hljs-comment">// bad</span>
<span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">'li'</span>).forEach(<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  e.onclick = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">this</span>.innerHTML)
  }
})
</code></pre>
<h2 id="heading-13-pay-attention-to-program-locality"><strong>13. Pay Attention to Program Locality</strong></h2>
<p>A well-written computer program often has good locality – it tends to reference data items near recently referenced data items or the recently referenced data items themselves. This tendency is known as the principle of locality. Programs with good locality run faster than those with poor locality.</p>
<h3 id="heading-locality-usually-takes-two-different-forms"><strong>Locality usually takes two different forms:</strong></h3>
<ul>
<li><p>Temporal locality: In a program with good temporal locality, memory locations that have been referenced once are likely to be referenced multiple times in the near future.</p>
</li>
<li><p>Spatial locality: In a program with good spatial locality, if a memory location has been referenced once, the program is likely to reference a nearby memory location in the near future.</p>
</li>
</ul>
<h4 id="heading-temporal-locality-example">Temporal locality example:</h4>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sum</span>(<span class="hljs-params">arry</span>) </span>{
    <span class="hljs-keyword">let</span> i, sum = <span class="hljs-number">0</span>
    <span class="hljs-keyword">let</span> len = arry.length

    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &lt; len; i++) {
        sum += arry[i]
    }

    <span class="hljs-keyword">return</span> sum
}
</code></pre>
<p>In this example, the variable sum is referenced once in each loop iteration, so it has good temporal locality.</p>
<h4 id="heading-spatial-locality-example">Spatial locality example:</h4>
<p>Program with good spatial locality:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Two-dimensional array </span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sum1</span>(<span class="hljs-params">arry, rows, cols</span>) </span>{
    <span class="hljs-keyword">let</span> i, j, sum = <span class="hljs-number">0</span>

    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &lt; rows; i++) {
        <span class="hljs-keyword">for</span> (j = <span class="hljs-number">0</span>; j &lt; cols; j++) {
            sum += arry[i][j]
        }
    }
    <span class="hljs-keyword">return</span> sum
}
</code></pre>
<p>Program with poor spatial locality:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Two-dimensional array </span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sum2</span>(<span class="hljs-params">arry, rows, cols</span>) </span>{
    <span class="hljs-keyword">let</span> i, j, sum = <span class="hljs-number">0</span>

    <span class="hljs-keyword">for</span> (j = <span class="hljs-number">0</span>; j &lt; cols; j++) {
        <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &lt; rows; i++) {
            sum += arry[i][j]
        }
    }
    <span class="hljs-keyword">return</span> sum
}
</code></pre>
<p>Looking at the two spatial locality examples above, the method of accessing each element of the array sequentially starting from each row, as shown in the examples, is called a reference pattern with a stride of 1.</p>
<p>If in an array, every k elements are accessed, it's called a reference pattern with a stride of k. Generally, as the stride increases, spatial locality decreases.</p>
<p>What's the difference between these two examples? Well, the first example scans the array by row, scanning one row completely before moving on to the next row. The second example scans the array by column, scanning one element in a row and immediately going to scan the same column element in the next row.</p>
<p>Arrays are stored in memory in row order, resulting in the example of scanning the array row by row getting a stride-1 reference pattern with good spatial locality. The other example has a stride of rows, with extremely poor spatial locality.</p>
<h3 id="heading-performance-testing"><strong>Performance Testing</strong></h3>
<p>Running environment:</p>
<ul>
<li><p>CPU: i5-7400</p>
</li>
<li><p>Browser: Chrome 70.0.3538.110</p>
</li>
</ul>
<p>Testing spatial locality on a two-dimensional array with a length of 9000 (child array length also 9000) 10 times, taking the average time (milliseconds), the results are as follows:</p>
<p>The examples used are the two spatial locality examples mentioned above.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Stride 1</td><td>Stride 9000</td></tr>
</thead>
<tbody>
<tr>
<td>124</td><td>2316</td></tr>
</tbody>
</table>
</div><p>From the test results above, the array with a stride of 1 executes an order of magnitude faster than the array with a stride of 9000.</p>
<p>So to sum up:</p>
<ul>
<li><p>Programs that repeatedly reference the same variables have good temporal locality</p>
</li>
<li><p>For programs with a reference pattern with a stride of k, the smaller the stride, the better the spatial locality; while programs that jump around in memory with large strides will have very poor spatial locality</p>
</li>
</ul>
<p>Reference:</p>
<ul>
<li><a target="_blank" href="https://www.amazon.sg/Computer-Systems-Programmers-Perspective-3rd/dp/013409266X">Computer Systems: A Programmer's Perspective</a></li>
</ul>
<h2 id="heading-14-if-else-vs-switch"><strong>14. if-else vs switch</strong></h2>
<p>As the number of judgment conditions increases, it becomes more preferable to use switch instead of if-else.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">if</span> (color == <span class="hljs-string">'blue'</span>) {

} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (color == <span class="hljs-string">'yellow'</span>) {

} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (color == <span class="hljs-string">'white'</span>) {

} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (color == <span class="hljs-string">'black'</span>) {

} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (color == <span class="hljs-string">'green'</span>) {

} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (color == <span class="hljs-string">'orange'</span>) {

} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (color == <span class="hljs-string">'pink'</span>) {

}

<span class="hljs-keyword">switch</span> (color) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">'blue'</span>:

        <span class="hljs-keyword">break</span>
    <span class="hljs-keyword">case</span> <span class="hljs-string">'yellow'</span>:

        <span class="hljs-keyword">break</span>
    <span class="hljs-keyword">case</span> <span class="hljs-string">'white'</span>:

        <span class="hljs-keyword">break</span>
    <span class="hljs-keyword">case</span> <span class="hljs-string">'black'</span>:

        <span class="hljs-keyword">break</span>
    <span class="hljs-keyword">case</span> <span class="hljs-string">'green'</span>:

        <span class="hljs-keyword">break</span>
    <span class="hljs-keyword">case</span> <span class="hljs-string">'orange'</span>:

        <span class="hljs-keyword">break</span>
    <span class="hljs-keyword">case</span> <span class="hljs-string">'pink'</span>:

        <span class="hljs-keyword">break</span>
}
</code></pre>
<p>In situations like the one above, from a readability perspective, using switch is better (JavaScript's switch statement is not based on hash implementation but on loop judgment, so from a performance perspective, if-else and switch are the same).</p>
<h3 id="heading-why-switch-is-better-for-multiple-conditions"><strong>Why switch is better for multiple conditions:</strong></h3>
<ol>
<li><p><strong>Improved readability</strong>: Switch statements present a clearer visual structure when dealing with multiple conditions against the same variable. The case statements create a more organized, tabular format that's easier to scan and understand.</p>
</li>
<li><p><strong>Cleaner code maintenance</strong>: Adding or removing conditions in a switch statement is simpler and less error-prone. With if-else chains, it's easy to accidentally break the chain or forget an "else" keyword.</p>
</li>
<li><p><strong>Less repetition</strong>: In the if-else example, we repeat checking the same variable (<code>color</code>) multiple times, while in switch we specify it once at the top.</p>
</li>
<li><p><strong>Better for debugging</strong>: When debugging, it's easier to set breakpoints on specific cases in a switch statement than trying to identify which part of a long if-else chain you need to target.</p>
</li>
<li><p><strong>Intent signaling</strong>: Using switch communicates to other developers that you're checking multiple possible values of the same variable, rather than potentially unrelated conditions.</p>
</li>
</ol>
<p>For modern JavaScript, there's another alternative worth considering for simple value mapping – object literals:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> colorActions = {
  <span class="hljs-string">'blue'</span>: <span class="hljs-function">() =&gt;</span> { <span class="hljs-comment">/* blue action */</span> },
  <span class="hljs-string">'yellow'</span>: <span class="hljs-function">() =&gt;</span> { <span class="hljs-comment">/* yellow action */</span> },
  <span class="hljs-string">'white'</span>: <span class="hljs-function">() =&gt;</span> { <span class="hljs-comment">/* white action */</span> },
  <span class="hljs-string">'black'</span>: <span class="hljs-function">() =&gt;</span> { <span class="hljs-comment">/* black action */</span> },
  <span class="hljs-string">'green'</span>: <span class="hljs-function">() =&gt;</span> { <span class="hljs-comment">/* green action */</span> },
  <span class="hljs-string">'orange'</span>: <span class="hljs-function">() =&gt;</span> { <span class="hljs-comment">/* orange action */</span> },
  <span class="hljs-string">'pink'</span>: <span class="hljs-function">() =&gt;</span> { <span class="hljs-comment">/* pink action */</span> }
};

<span class="hljs-comment">// Execute the action if it exists</span>
<span class="hljs-keyword">if</span> (colorActions[color]) {
  colorActions[color]();
}
</code></pre>
<p>This approach provides even better performance (O(1) lookup time) compared to both if-else and switch statement approaches.</p>
<h2 id="heading-15-lookup-tables"><strong>15. Lookup Tables</strong></h2>
<p>When there are many conditional statements, using switch and if-else is not the best choice. In such cases, you might want to try lookup tables. Lookup tables can be constructed using arrays and objects.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">switch</span> (index) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">'0'</span>:
        <span class="hljs-keyword">return</span> result0
    <span class="hljs-keyword">case</span> <span class="hljs-string">'1'</span>:
        <span class="hljs-keyword">return</span> result1
    <span class="hljs-keyword">case</span> <span class="hljs-string">'2'</span>:
        <span class="hljs-keyword">return</span> result2
    <span class="hljs-keyword">case</span> <span class="hljs-string">'3'</span>:
        <span class="hljs-keyword">return</span> result3
    <span class="hljs-keyword">case</span> <span class="hljs-string">'4'</span>:
        <span class="hljs-keyword">return</span> result4
    <span class="hljs-keyword">case</span> <span class="hljs-string">'5'</span>:
        <span class="hljs-keyword">return</span> result5
    <span class="hljs-keyword">case</span> <span class="hljs-string">'6'</span>:
        <span class="hljs-keyword">return</span> result6
    <span class="hljs-keyword">case</span> <span class="hljs-string">'7'</span>:
        <span class="hljs-keyword">return</span> result7
    <span class="hljs-keyword">case</span> <span class="hljs-string">'8'</span>:
        <span class="hljs-keyword">return</span> result8
    <span class="hljs-keyword">case</span> <span class="hljs-string">'9'</span>:
        <span class="hljs-keyword">return</span> result9
    <span class="hljs-keyword">case</span> <span class="hljs-string">'10'</span>:
        <span class="hljs-keyword">return</span> result10
    <span class="hljs-keyword">case</span> <span class="hljs-string">'11'</span>:
        <span class="hljs-keyword">return</span> result11
}
</code></pre>
<p>This switch statement can be converted to a lookup table:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> results = [result0,result1,result2,result3,result4,result5,result6,result7,result8,result9,result10,result11]

<span class="hljs-keyword">return</span> results[index]
</code></pre>
<p>If the conditional statements are not numerical values but strings, you can use an object to build a lookup table:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> map = {
  red: result0,
  green: result1,
}

<span class="hljs-keyword">return</span> map[color]
</code></pre>
<h3 id="heading-why-lookup-tables-are-better-for-many-conditions"><strong>Why lookup tables are better for many conditions:</strong></h3>
<ol>
<li><p><strong>Constant time complexity (O(1))</strong>: Lookup tables provide direct access to the result based on the index/key, making the operation time constant regardless of how many options there are. In contrast, both if-else chains and switch statements have linear time complexity (O(n)) because in the worst case, they might need to check all conditions.</p>
</li>
<li><p><strong>Performance gains with many conditions</strong>: As the number of conditions increases, the performance advantage of lookup tables becomes more significant. For a small number of cases (2-5), the difference is negligible, but with dozens or hundreds of cases, lookup tables are substantially faster.</p>
</li>
<li><p><strong>Code brevity</strong>: As shown in the examples, lookup tables typically require less code, making your codebase more maintainable.</p>
</li>
<li><p><strong>Dynamic configuration</strong>: Lookup tables can be easily populated dynamically:</p>
</li>
</ol>
<pre><code class="lang-typescript">   <span class="hljs-keyword">const</span> actionMap = {};

   <span class="hljs-comment">// Dynamically populate the map</span>
   <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">registerAction</span>(<span class="hljs-params">key, handler</span>) </span>{
     actionMap[key] = handler;
   }

   <span class="hljs-comment">// Register different handlers</span>
   registerAction(<span class="hljs-string">'save'</span>, saveDocument);
   registerAction(<span class="hljs-string">'delete'</span>, deleteDocument);

   <span class="hljs-comment">// Use it</span>
   <span class="hljs-keyword">if</span> (actionMap[userAction]) {
     actionMap[userAction]();
   }
</code></pre>
<ol start="5">
<li><strong>Reduced cognitive load</strong>: When there are many conditions, lookup tables eliminate the mental overhead of following long chains of logic.</li>
</ol>
<h3 id="heading-when-to-use-each-approach"><strong>When to use each approach:</strong></h3>
<ul>
<li><p><strong>If-else</strong>: Best for a few conditions (2-3) with complex logic or different variables being checked</p>
</li>
<li><p><strong>Switch</strong>: Good for moderate number of conditions (4-10) checking against the same variable</p>
</li>
<li><p><strong>Lookup tables</strong>: Ideal for many conditions (10+) or when you need O(1) access time</p>
</li>
</ul>
<p>In real applications, lookup tables might be populated from external sources like databases or configuration files, making them flexible for scenarios where the mapping logic might change without requiring code modifications.</p>
<h2 id="heading-16-avoid-page-stuttering"><strong>16. Avoid Page Stuttering</strong></h2>
<h3 id="heading-60fps-and-device-refresh-rate"><strong>60fps and Device Refresh Rate</strong></h3>
<blockquote>
<p>Currently, most devices have a screen refresh rate of 60 times/second. Therefore, if there's an animation or gradient effect on the page, or if the user is scrolling the page, the browser needs to render animations or pages at a rate that matches the device's screen refresh rate.  </p>
<p>The budget time for each frame is just over 16 milliseconds (1 second / 60 = 16.66 milliseconds). But in reality, the browser has housekeeping work to do, so all your work needs to be completed within 10 milliseconds. If you can't meet this budget, the frame rate will drop, and content will jitter on the screen.  </p>
<p>This phenomenon is commonly known as stuttering and has a negative impact on user experience. <em>Source:</em> <a target="_blank" href="https://developers.google.com/web/fundamentals/performance/rendering"><em>Google Web Fundamentals - Rendering Performance</em></a></p>
</blockquote>
<p><a target="_blank" href="https://camo.githubusercontent.com/300b19e6e2523e1dfba3a8addba37a65797cc55de57501768ce987a81d06332f/68747470733a2f2f696d672d626c6f672e6373646e696d672e636e2f696d675f636f6e766572742f31626565666137613665323039346465643966656261336165633832303135382e706e67"><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%2Fp5pde3zqyadfrth7ypsf.png" alt="Frame budget timing diagram showing the 16ms frame budget and browser overhead" width="600" height="400" loading="lazy"></a></p>
<p>Suppose you use JavaScript to modify the DOM, trigger style changes, go through reflow and repaint, and finally paint to the screen. If any of these takes too long, it will cause the rendering time of this frame to be too long, and the average frame rate will drop. Suppose this frame took 50 ms, then the frame rate would be 1s / 50ms = 20fps, and the page would appear to stutter.</p>
<p>For some long-running JavaScript, we can use timers to split and delay execution.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>, len = arry.length; i &lt; len; i++) {
    process(arry[i])
}
</code></pre>
<p>Suppose the loop structure above takes too long due to either the high complexity of process() or too many array elements, or both, you might want to try splitting.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> todo = arry.concat()
<span class="hljs-built_in">setTimeout</span>(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
    process(todo.shift())
    <span class="hljs-keyword">if</span> (todo.length) {
        <span class="hljs-built_in">setTimeout</span>(<span class="hljs-built_in">arguments</span>.callee, <span class="hljs-number">25</span>)
    } <span class="hljs-keyword">else</span> {
        callback(arry)
    }
}, <span class="hljs-number">25</span>)
</code></pre>
<p>If you're interested in learning more, check out <a target="_blank" href="https://www.amazon.com/High-Performance-JavaScript-Application-Interfaces/dp/059680279X">High Performance JavaScript</a> Chapter 6.</p>
<p>Reference:</p>
<ul>
<li><a target="_blank" href="https://developers.google.com/web/fundamentals/performance/rendering">Rendering Performance</a></li>
</ul>
<h2 id="heading-17-use-requestanimationframe-to-implement-visual-changes"><strong>17. Use</strong> <code>requestAnimationFrame</code> <strong>to Implement Visual Changes</strong></h2>
<p>From point 16, we know that most devices have a screen refresh rate of 60 times/second, which means the average time per frame is 16.66 milliseconds. When using JavaScript to implement animation effects, the best case is that the code starts executing at the beginning of each frame. The only way to ensure JavaScript runs at the beginning of a frame is to use <code>requestAnimationFrame</code>.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">/**
 * If run as a requestAnimationFrame callback, this
 * will be run at the start of the frame.
 */</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateScreen</span>(<span class="hljs-params">time</span>) </span>{
  <span class="hljs-comment">// Make visual updates here.</span>
}

requestAnimationFrame(updateScreen);
</code></pre>
<p>If you use <code>setTimeout</code> or <code>setInterval</code> to implement animations, the callback function will run at some point in the frame, possibly right at the end, which can often cause us to miss frames, leading to stuttering.</p>
<p><a target="_blank" href="https://camo.githubusercontent.com/6921c15237df7064a3fe41fa89a174d78b43a8a0764a3b7536051c59b223ef6d/68747470733a2f2f696d672d626c6f672e6373646e696d672e636e2f696d675f636f6e766572742f32386238663463313066646333393633303135386562646162626264356432662e706e67"><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%2F72qwmqhf5972jig808qz.png" alt="show the execution time of javascript" width="600" height="400" loading="lazy"></a></p>
<p>Reference:</p>
<ul>
<li><p><a target="_blank" href="https://web.dev/articles/optimize-javascript-execution">Optimize JavaScript Execution</a></p>
</li>
<li><p><a target="_blank" href="https://www.freecodecamp.org/news/immutable-javascript-improve-application-performance/">Improve JS performance</a></p>
</li>
</ul>
<h2 id="heading-18-use-web-workers"><strong>18. Use Web Workers</strong></h2>
<p>Web Workers use other worker threads to operate independently of the main thread. They can perform tasks without interfering with the user interface. A worker can send messages to the JavaScript code that created it by sending messages to the event handler specified by that code (and vice versa).</p>
<p>Web Workers are suitable for processing pure data or long-running scripts unrelated to the browser UI.</p>
<p>Creating a new worker is simple – just specify a script URI to execute the worker thread (main.js):</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">var</span> myWorker = <span class="hljs-keyword">new</span> Worker(<span class="hljs-string">'worker.js'</span>);
<span class="hljs-comment">// You can send messages to the worker through the postMessage() method and onmessage event</span>
first.onchange = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
  myWorker.postMessage([first.value, second.value]);
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Message posted to worker'</span>);
}

second.onchange = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
  myWorker.postMessage([first.value, second.value]);
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Message posted to worker'</span>);
}
</code></pre>
<p>In the worker, after receiving the message, you can write an event handler function code as a response (worker.js):</p>
<pre><code class="lang-typescript">onmessage = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Message received from main script'</span>);
  <span class="hljs-keyword">var</span> workerResult = <span class="hljs-string">'Result: '</span> + (e.data[<span class="hljs-number">0</span>] * e.data[<span class="hljs-number">1</span>]);
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Posting message back to main script'</span>);
  postMessage(workerResult);
}
</code></pre>
<p>The <code>onmessage</code> handler function executes immediately after receiving the message, and the message itself is used as the data property of the event. Here we simply multiply the two numbers and use the <code>postMessage()</code> method again to send the result back to the main thread.</p>
<p>Back in the main thread, we use <code>onmessage</code> again to respond to the message sent back from the worker:</p>
<pre><code class="lang-typescript">myWorker.onmessage = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>) </span>{
  result.textContent = e.data;
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Message received from worker'</span>);
}
</code></pre>
<p>Here we get the data from the message event and set it as the <code>textContent</code> of result, so the user can directly see the result of the calculation.</p>
<p>Note that inside the worker, you cannot directly manipulate DOM nodes, nor can you use the default methods and properties of the window object. But you can use many things under the window object, including data storage mechanisms such as WebSockets, IndexedDB, and Firefox OS-specific Data Store API.</p>
<p>Reference:</p>
<ul>
<li><p><a target="_blank" href="https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API/Using_web_workers">Web Workers</a></p>
</li>
<li><p><a target="_blank" href="https://www.freecodecamp.org/news/how-webworkers-work-in-javascript-with-example/">How web workers work in JS</a></p>
</li>
</ul>
<h2 id="heading-19-use-bitwise-operations"><strong>19. Use Bitwise Operations</strong></h2>
<p>Numbers in JavaScript are stored in 64-bit format using the IEEE-754 standard. But in bitwise operations, numbers are converted to 32-bit signed format. Even with the conversion, bitwise operations are much faster than other mathematical and boolean operations.</p>
<h3 id="heading-modulo"><strong>Modulo</strong></h3>
<p>Since the lowest bit of even numbers is 0 and odd numbers is 1, modulo operations can be replaced with bitwise operations.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">if</span> (value % <span class="hljs-number">2</span>) {
    <span class="hljs-comment">// Odd number</span>
} <span class="hljs-keyword">else</span> {
    <span class="hljs-comment">// Even number </span>
}
<span class="hljs-comment">// Bitwise operation</span>
<span class="hljs-keyword">if</span> (value &amp; <span class="hljs-number">1</span>) {
    <span class="hljs-comment">// Odd number</span>
} <span class="hljs-keyword">else</span> {
    <span class="hljs-comment">// Even number</span>
}
</code></pre>
<p><strong>How it works:</strong> The <code>&amp;</code> (bitwise AND) operator compares each bit of the first operand to the corresponding bit of the second operand. If both bits are 1, the corresponding result bit is set to 1; otherwise, it's set to 0.</p>
<p>When we do <code>value &amp; 1</code>, we're only checking the last bit of the number:</p>
<ul>
<li><p>For even numbers (for example, 4 = <code>100</code> in binary), the last bit is 0: <code>100 &amp; 001 = 000</code> (0)</p>
</li>
<li><p>For odd numbers (for example, 5 = <code>101</code> in binary), the last bit is 1: <code>101 &amp; 001 = 001</code> (1)</p>
</li>
</ul>
<h3 id="heading-floor"><strong>Floor</strong></h3>
<pre><code class="lang-typescript">~~<span class="hljs-number">10.12</span> <span class="hljs-comment">// 10</span>
~~<span class="hljs-number">10</span> <span class="hljs-comment">// 10</span>
~~<span class="hljs-string">'1.5'</span> <span class="hljs-comment">// 1</span>
~~<span class="hljs-literal">undefined</span> <span class="hljs-comment">// 0</span>
~~<span class="hljs-literal">null</span> <span class="hljs-comment">// 0</span>
</code></pre>
<p><strong>How it works:</strong> The <code>~</code> (bitwise NOT) operator inverts all bits in the operand. For a number <code>n</code>, <code>~n</code> equals <code>-(n+1)</code>. When applied twice (<code>~~n</code>), it effectively truncates the decimal part of a number, similar to <code>Math.floor()</code> for positive numbers and <code>Math.ceil()</code> for negative numbers.</p>
<p>The process:</p>
<ol>
<li><p>First <code>~</code>: Converts the number to a 32-bit integer and inverts all bits</p>
</li>
<li><p>Second <code>~</code>: Inverts all bits again, resulting in the original number but with decimal part removed</p>
</li>
</ol>
<p>For example:</p>
<pre><code class="lang-typescript">~<span class="hljs-number">10.12</span> → ~<span class="hljs-number">10</span> → -(<span class="hljs-number">10</span>+<span class="hljs-number">1</span>) → <span class="hljs-number">-11</span>
~(<span class="hljs-number">-11</span>) → -(<span class="hljs-number">-11</span>+<span class="hljs-number">1</span>) → -(<span class="hljs-number">-10</span>) → <span class="hljs-number">10</span>
</code></pre>
<h3 id="heading-bitmask"><strong>Bitmask</strong></h3>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> a = <span class="hljs-number">1</span>
<span class="hljs-keyword">const</span> b = <span class="hljs-number">2</span>
<span class="hljs-keyword">const</span> c = <span class="hljs-number">4</span>
<span class="hljs-keyword">const</span> options = a | b | c
</code></pre>
<p>By defining these options, you can use the bitwise AND operation to determine if a/b/c is in the options.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Is option b in the options?</span>
<span class="hljs-keyword">if</span> (b &amp; options) {
    ...
}
</code></pre>
<p><strong>How it works:</strong> In bitmasks, each bit represents a boolean flag. The values are typically powers of 2 so each has exactly one bit set.</p>
<ol>
<li><p><code>a = 1</code>: Binary <code>001</code></p>
</li>
<li><p><code>b = 2</code>: Binary <code>010</code></p>
</li>
<li><p><code>c = 4</code>: Binary <code>100</code></p>
</li>
<li><p><code>options = a | b | c</code>: The <code>|</code> (bitwise OR) combines them: <code>001 | 010 | 100 = 111</code> (binary) = 7 (decimal)</p>
</li>
</ol>
<p>When checking if a flag is set with <code>if (b &amp; options)</code>:</p>
<ul>
<li><p><code>b &amp; options</code> = <code>010 &amp; 111</code> = <code>010</code> = 2 (decimal)</p>
</li>
<li><p>Since this is non-zero, the condition evaluates to true</p>
</li>
</ul>
<p>This technique is extremely efficient for storing and checking multiple boolean values in a single number, and is commonly used in systems programming, graphics programming, and permission systems.</p>
<h2 id="heading-20-dont-override-native-methods"><strong>20. Don't Override Native Methods</strong></h2>
<p>No matter how optimized your JavaScript code is, it can't match native methods. This is because native methods are written in low-level languages (C/C++) and compiled into machine code, becoming part of the browser. When native methods are available, try to use them, especially for mathematical operations and DOM manipulations.</p>
<h3 id="heading-example-string-replacement-native-vs-custom"><strong>Example: String Replacement (Native vs. Custom)</strong></h3>
<p>A common pitfall is rewriting native string methods like <code>replaceAll()</code>. Below is an inefficient custom implementation versus the native method, with performance benchmarks:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Inefficient custom global replacement (manual loop)  </span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">customReplaceAll</span>(<span class="hljs-params">str, oldSubstr, newSubstr</span>) </span>{  
  <span class="hljs-keyword">let</span> result = <span class="hljs-string">''</span>;  
  <span class="hljs-keyword">let</span> index = <span class="hljs-number">0</span>;  
  <span class="hljs-keyword">while</span> (index &lt; str.length) {  
    <span class="hljs-keyword">if</span> (str.slice(index, index + oldSubstr.length) === oldSubstr) {  
      result += newSubstr;  
      index += oldSubstr.length;  
    } <span class="hljs-keyword">else</span> {  
      result += str[index];  
      index++;  
    }  
  }  
  <span class="hljs-keyword">return</span> result;  
}  

<span class="hljs-comment">// Efficient native method (browser-optimized)  </span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">nativeReplaceAll</span>(<span class="hljs-params">str, oldSubstr, newSubstr</span>) </span>{  
  <span class="hljs-keyword">return</span> str.replaceAll(oldSubstr, newSubstr);  
}  

<span class="hljs-comment">// Test with a large string (100,000 repetitions of "abc ")  </span>
<span class="hljs-keyword">const</span> largeString = <span class="hljs-string">'abc '</span>.repeat(<span class="hljs-number">100000</span>);  

<span class="hljs-comment">// Benchmark: Custom implementation  </span>
<span class="hljs-built_in">console</span>.time(<span class="hljs-string">'customReplaceAll'</span>);  
customReplaceAll(largeString, <span class="hljs-string">'abc'</span>, <span class="hljs-string">'xyz'</span>);  
<span class="hljs-built_in">console</span>.timeEnd(<span class="hljs-string">'customReplaceAll'</span>); <span class="hljs-comment">// Output: ~5ms (varies by browser)  </span>

<span class="hljs-comment">// Benchmark: Native method  </span>
<span class="hljs-built_in">console</span>.time(<span class="hljs-string">'nativeReplaceAll'</span>);  
nativeReplaceAll(largeString, <span class="hljs-string">'abc'</span>, <span class="hljs-string">'xyz'</span>);  
<span class="hljs-built_in">console</span>.timeEnd(<span class="hljs-string">'nativeReplaceAll'</span>); <span class="hljs-comment">// Output: ~2ms (typically 2-3x faster)</span>
</code></pre>
<p>Key takeaways:</p>
<ul>
<li><p><strong>Performance</strong>: Native methods like <code>replaceAll()</code> are optimized at the browser level, often outperforming handwritten code (as shown in the benchmark above).</p>
</li>
<li><p><strong>Maintainability</strong>: Native methods are standardized, well-documented, and less error-prone than custom logic (for example, handling edge cases like overlapping substrings).</p>
</li>
<li><p><strong>Ecosystem compatibility</strong>: Using native methods ensures consistency with libraries and tools that rely on JavaScript’s built-in behavior.</p>
</li>
</ul>
<h3 id="heading-when-to-use-custom-code"><strong>When to Use Custom Code</strong></h3>
<p>While native methods are usually superior, there are rare cases where you might need custom logic:</p>
<ul>
<li><p>When the native method doesn’t exist (for example, polyfilling for older browsers).</p>
</li>
<li><p>For highly specialized edge cases not covered by native APIs.</p>
</li>
<li><p>When you need to avoid function call overhead in extremely performance-critical loops (for example, tight numerical computations).</p>
</li>
</ul>
<p><strong>Remember</strong>: Browser vendors spend millions of hours optimizing native methods. By leveraging them, you gain free performance boosts and reduce the risk of reinventing flawed solutions.</p>
<h2 id="heading-21-reduce-the-complexity-of-css-selectors"><strong>21. Reduce the Complexity of CSS Selectors</strong></h2>
<h3 id="heading-1-when-browsers-read-selectors-they-follow-the-principle-of-reading-from-right-to-left"><strong>1. When browsers read selectors, they follow the principle of reading from right to left.</strong></h3>
<p>Let's look at an example:</p>
<pre><code class="lang-typescript">#block .text p {
    color: red;
}
</code></pre>
<ol>
<li><p>Find all P elements.</p>
</li>
<li><p>Check if the elements found in result 1 have parent elements with class name "text"</p>
</li>
<li><p>Check if the elements found in result 2 have parent elements with ID "block"</p>
</li>
</ol>
<p><strong>Why is this inefficient?</strong> This right-to-left evaluation process can be very expensive in complex documents. Take the selector <code>#block .text p</code> as an example:</p>
<ol>
<li><p>The browser first finds <strong>all</strong> <code>p</code> elements in the document (potentially hundreds)</p>
</li>
<li><p>For each of those paragraph elements, it must check if any of their ancestors have the class <code>text</code></p>
</li>
<li><p>For those that pass step 2, it must check if any of their ancestors have the ID <code>block</code></p>
</li>
</ol>
<p>This creates a significant performance bottleneck because:</p>
<ul>
<li><p>The initial selection (<code>p</code>) is very broad</p>
</li>
<li><p>Each subsequent step requires checking multiple ancestors in the DOM tree</p>
</li>
<li><p>This process repeats for every paragraph element</p>
</li>
</ul>
<p>A more efficient alternative would be:</p>
<pre><code class="lang-typescript">#block p.specific-text {
    color: red;
}
</code></pre>
<p>This is more efficient because it directly targets only paragraphs with a specific class, avoiding checking all paragraphs</p>
<h3 id="heading-2-css-selector-priority"><strong>2. CSS selector priority</strong></h3>
<pre><code class="lang-typescript">Inline &gt; ID selector &gt; Class selector &gt; Tag selector
</code></pre>
<p>Based on the above two pieces of information, we can draw conclusions:</p>
<ol>
<li><p>The shorter the selector, the better.</p>
</li>
<li><p>Try to use high-priority selectors, such as ID and class selectors.</p>
</li>
<li><p>Avoid using the universal selector *.</p>
</li>
</ol>
<p><strong>Practical advice for optimal CSS selectors:</strong></p>
<pre><code class="lang-typescript"><span class="hljs-comment">/* ❌ Inefficient: Too deep, starts with a tag selector */</span>
body div.container ul li a.link {
    color: blue;
}

<span class="hljs-comment">/* ✅ Better: Shorter, starts with a class selector */</span>
.container .link {
    color: blue;
}

<span class="hljs-comment">/* ✅ Best: Direct, single class selector */</span>
.nav-link {
    color: blue;
}
</code></pre>
<p>Finally, I should say that according to the materials I've found, there's no need to optimize CSS selectors because the performance difference between the slowest and fastest selectors is very small.</p>
<p>Reference:</p>
<ul>
<li><a target="_blank" href="https://www.sitepoint.com/optimizing-css-id-selectors-and-other-myths/">Optimizing CSS: ID Selectors and Other Myths</a></li>
</ul>
<h2 id="heading-22-use-flexbox-instead-of-earlier-layout-models"><strong>22. Use Flexbox Instead of Earlier Layout Models</strong></h2>
<p>In early CSS layout methods, we could position elements absolutely, relatively, or using floats. Now, we have a new layout method called <a target="_blank" href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox">Flexbox</a>, which has an advantage over earlier layout methods: better performance.</p>
<p>The screenshot below shows the layout cost of using floats on 1300 boxes:</p>
<p><a target="_blank" href="https://camo.githubusercontent.com/ff6a96a175ccd6a4a55e0a0ea2932833cae4f639ddfda73c330f056eb2311efa/68747470733a2f2f696d672d626c6f672e6373646e696d672e636e2f696d675f636f6e766572742f37343264613262643539656537613331396239363036643461393539323234392e706e67"><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%2Fnwsgqx7lcp8q0bizrtmb.png" alt="layout timeline in dev tool" width="600" height="400" loading="lazy"></a></p>
<p>Then we recreate this example using Flexbox:</p>
<p><a target="_blank" href="https://camo.githubusercontent.com/18ad08d69431cc0ef0d60b3aa748aa1e0220329cb6043046eeb744ad3ec64abe/68747470733a2f2f696d672d626c6f672e6373646e696d672e636e2f696d675f636f6e766572742f63633831663131613634643232613863656334643935616638633136376537362e706e67"><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%2Fo9pmmihyz1ql8761ae2k.png" alt="layout timeline in dev tool" width="600" height="400" loading="lazy"></a></p>
<p>Now, for the same number of elements and the same visual appearance, the layout time is much less (3.5 milliseconds versus 14 milliseconds in this example).</p>
<p>But Flexbox compatibility is still an issue, as not all browsers support it, so use it with caution.</p>
<p>Browser compatibility:</p>
<ul>
<li><p>Chrome 29+</p>
</li>
<li><p>Firefox 28+</p>
</li>
<li><p>Internet Explorer 11</p>
</li>
<li><p>Opera 17+</p>
</li>
<li><p>Safari 6.1+ (prefixed with -webkit-)</p>
</li>
<li><p>Android 4.4+</p>
</li>
<li><p>iOS 7.1+ (prefixed with -webkit-)</p>
</li>
</ul>
<p>Reference:</p>
<ul>
<li><a target="_blank" href="https://web.dev/articles/avoid-large-complex-layouts-and-layout-thrashing">Use flexbox instead of earlier layout models</a></li>
</ul>
<h2 id="heading-23-use-transform-and-opacity-properties-to-implement-animations"><strong>23. Use Transform and Opacity Properties to Implement Animations</strong></h2>
<p>In CSS, transforms and opacity property changes don't trigger reflow and repaint. They’re properties that can be processed by the compositor alone.</p>
<p><a target="_blank" href="https://camo.githubusercontent.com/00e5e11d0b2837e91e8118284520b5969ae69670c8607d791e2053599fee0b4e/68747470733a2f2f696d672d626c6f672e6373646e696d672e636e2f696d675f636f6e766572742f66626436333931363533376336623531373733633266623134343263663130632e706e67"><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%2F3ae64ihgp1781wrtfci8.png" alt="Diagram showing how transform and opacity properties bypass layout and paint processes" width="600" height="400" loading="lazy"></a></p>
<h3 id="heading-example-inefficient-vs-efficient-animation"><strong>Example: Inefficient vs. Efficient Animation</strong></h3>
<p>❌ Inefficient animation using properties that trigger reflow and repaint:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">/* CSS */</span>
.box-inefficient {
  position: absolute;
  left: <span class="hljs-number">0</span>;
  top: <span class="hljs-number">0</span>;
  width: <span class="hljs-number">100</span>px;
  height: <span class="hljs-number">100</span>px;
  background-color: #<span class="hljs-number">3498</span>db;
  animation: move-inefficient <span class="hljs-number">2</span>s infinite alternate;
}

<span class="hljs-meta">@keyframes</span> move-inefficient {
  to {
    left: <span class="hljs-number">300</span>px;
    top: <span class="hljs-number">200</span>px;
    width: <span class="hljs-number">150</span>px;
    height: <span class="hljs-number">150</span>px;
  }
}
</code></pre>
<p>This animation constantly triggers layout recalculations (reflow) because it animates position (<code>left</code>/<code>top</code>) and size (<code>width</code>/<code>height</code>) properties.</p>
<p>✅ Efficient animation using transform and opacity:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">/* CSS */</span>
.box-efficient {
  position: absolute;
  width: <span class="hljs-number">100</span>px;
  height: <span class="hljs-number">100</span>px;
  background-color: #<span class="hljs-number">3498</span>db;
  animation: move-efficient <span class="hljs-number">2</span>s infinite alternate;
}

<span class="hljs-meta">@keyframes</span> move-efficient {
  to {
    transform: translate(<span class="hljs-number">300</span>px, <span class="hljs-number">200</span>px) scale(<span class="hljs-number">1.5</span>);
    opacity: <span class="hljs-number">0.7</span>;
  }
}
</code></pre>
<p><strong>Why this is better:</strong></p>
<ol>
<li><p><code>transform: translate(300px, 200px)</code> replaces <code>left: 300px; top: 200px</code></p>
</li>
<li><p><code>transform: scale(1.5)</code> replaces <code>width: 150px; height: 150px</code></p>
</li>
<li><p>These transform operations and opacity changes can be handled directly by the GPU without triggering layout or paint operations</p>
</li>
</ol>
<p><strong>Performance comparison:</strong></p>
<ol>
<li><p>The inefficient version may drop frames on lower-end devices because each frame requires:</p>
<ul>
<li>JavaScript → Style calculations → Layout → Paint → Composite</li>
</ul>
</li>
<li><p>The efficient version typically maintains 60fps because it only requires:</p>
<ul>
<li>JavaScript → Style calculations → Composite</li>
</ul>
</li>
</ol>
<p><strong>HTML implementation:</strong></p>
<pre><code class="lang-typescript">&lt;div <span class="hljs-keyword">class</span>=<span class="hljs-string">"box-inefficient"</span>&gt;Inefficient&lt;/div&gt;
&lt;div <span class="hljs-keyword">class</span>=<span class="hljs-string">"box-efficient"</span>&gt;Efficient&lt;/div&gt;
</code></pre>
<p>For complex animations, you can use the Chrome DevTools Performance panel to visualize the difference. The inefficient animation will show many more layout and paint events compared to the efficient one.</p>
<p>Reference:</p>
<ul>
<li><a target="_blank" href="https://web.dev/articles/stick-to-compositor-only-properties-and-manage-layer-count">Use transform and opacity property changes to implement animations</a></li>
</ul>
<h2 id="heading-24-use-rules-reasonably-avoid-over-optimization"><strong>24. Use Rules Reasonably, Avoid Over-Optimization</strong></h2>
<p>Performance optimization is mainly divided into two categories:</p>
<ol>
<li><p>Load-time optimization</p>
</li>
<li><p>Runtime optimization</p>
</li>
</ol>
<p>Of the 23 suggestions above, the first 10 belong to load-time optimization, and the last 13 belong to runtime optimization. Usually, there's no need to apply all 23 performance optimization rules. It's best to make targeted adjustments based on the website's user group, saving effort and time.</p>
<p>Before solving a problem, you need to identify the problem first, otherwise you won't know where to start. So before doing performance optimization, it's best to investigate the website's loading and running performance.</p>
<h3 id="heading-check-loading-performance"><strong>Check Loading Performance</strong></h3>
<p>A website's loading performance mainly depends on white screen time and first screen time.</p>
<ul>
<li><p>White screen time: The time from entering the URL to when the page starts displaying content.</p>
</li>
<li><p>First screen time: The time from entering the URL to when the page is completely rendered.</p>
</li>
</ul>
<p>You can get the white screen time by placing the following script before <code>&lt;/head&gt;</code>.</p>
<pre><code class="lang-typescript">&lt;script&gt;
  <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>() - performance.timing.navigationStart
  <span class="hljs-comment">// You can also use domLoading and navigationStart</span>
  performance.timing.domLoading - performance.timing.navigationStart
&lt;/script&gt;
</code></pre>
<p>You can get the first screen time by executing <code>new Date() - performance.timing.navigationStart</code> in the <code>window.onload</code> event.</p>
<h3 id="heading-check-runtime-performance"><strong>Check Runtime Performance</strong></h3>
<p>With Chrome's developer tools, we can check the website's performance during runtime.</p>
<p>Open the website, press F12 and select performance, click the gray dot in the upper left corner, it turns red to indicate it has started recording. At this point, you can simulate users using the website, and after you're done, click stop, then you'll see the website's performance report during the runtime.</p>
<p>If there are red blocks, it means there are frame drops. If it's green, it means the FPS is good. For detailed usage of performance, you can search using a search engine, as the scope is limited.</p>
<p>By checking the loading and runtime performance, I believe you already have a general understanding of the website's performance. So what you need to do now is to use the 23 suggestions above to optimize your website. Go for it!</p>
<p>References:</p>
<ul>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/PerformanceTiming/navigationStart">performance.timing.navigationStart</a></li>
</ul>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Performance optimization is a critical aspect of modern web development that directly impacts user experience, engagement, and ultimately, business outcomes. Throughout this article, we've explored 24 diverse techniques spanning various layers of web applications – from network optimization to rendering performance and JavaScript execution.</p>
<h3 id="heading-key-takeaways"><strong>Key Takeaways</strong></h3>
<ol>
<li><p><strong>Start with measurement, not optimization</strong>. As discussed in point #24, always identify your specific performance bottlenecks before applying optimization techniques. Tools like Chrome DevTools Performance panel, Lighthouse, and WebPageTest can help pinpoint exactly where your application is struggling.</p>
</li>
<li><p><strong>Focus on the critical rendering path</strong>. Many of our techniques (placing CSS in the head, JavaScript at the bottom, reducing HTTP requests, server-side rendering) are centered around speeding up the time to first meaningful paint – the moment when users see and can interact with your content.</p>
</li>
<li><p><strong>Understand the browser rendering process</strong>. Knowledge of how browsers parse HTML, execute JavaScript, and render pixels to the screen is essential for making informed optimization decisions, especially when dealing with animations and dynamic content.</p>
</li>
<li><p><strong>Balance implementation cost vs. performance gain</strong>. Not all optimization techniques are worth implementing for every project. For instance, server-side rendering adds complexity that might not be justified for simple applications, and bitwise operations provide performance gains only in specific heavy computation scenarios.</p>
</li>
<li><p><strong>Consider the device and network conditions of your users</strong>. If you're building for users in regions with slower internet connections or less powerful devices, techniques like image optimization, code splitting, and reducing JavaScript payloads become even more important.</p>
</li>
</ol>
<h3 id="heading-practical-implementation-strategy"><strong>Practical Implementation Strategy</strong></h3>
<p>Instead of trying to implement all 24 techniques at once, consider taking a phased approach:</p>
<ol>
<li><p><strong>First pass</strong>: Implement the easy wins with high impact</p>
<ul>
<li><p>Proper image optimization</p>
</li>
<li><p>HTTP/2</p>
</li>
<li><p>Basic caching</p>
</li>
<li><p>CSS/JS placement</p>
</li>
</ul>
</li>
<li><p><strong>Second pass</strong>: Address specific measured bottlenecks</p>
<ul>
<li><p>Use performance profiling to identify problem areas</p>
</li>
<li><p>Apply targeted optimizations based on findings</p>
</li>
</ul>
</li>
<li><p><strong>Ongoing maintenance</strong>: Make performance part of your development workflow</p>
<ul>
<li><p>Set performance budgets</p>
</li>
<li><p>Implement automated performance testing</p>
</li>
<li><p>Review new feature additions for performance impact</p>
</li>
</ul>
</li>
</ol>
<p>By treating performance as an essential feature rather than an afterthought, you'll create web applications that not only look good and function well but also provide the speed and responsiveness that modern users expect.</p>
<p>Remember that web performance is a continuous journey, not a destination. Browsers evolve, best practices change, and user expectations increase. The techniques in this article provide a strong foundation, but staying current with web performance trends will ensure your applications remain fast and effective for years to come.</p>
<h3 id="heading-other-references"><strong>Other References</strong></h3>
<ul>
<li><p><a target="_blank" href="https://web.dev/learn/performance/why-speed-matters">Why Performance Matters</a></p>
</li>
<li><p><a target="_blank" href="https://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309">High-Performance Website Construction Guide</a></p>
</li>
<li><p><a target="_blank" href="https://hpbn.co/">High Performance Browser Networking</a></p>
</li>
<li><p><a target="_blank" href="https://www.amazon.com/High-Performance-JavaScript-Application-Interfaces/dp/059680279X">High-Performance JavaScript</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Why Your Code is Slow: Common  Performance Mistakes Beginners Make ]]>
                </title>
                <description>
                    <![CDATA[ Maybe you’ve experienced something like this before: you’ve written code that works, but when you hit “run,” it takes forever. You stare at the spinner, wondering if it’s faster to just solve the problem by hand. But you end up looking something like... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/why-your-code-is-slow-common-performance-mistakes-beginners-make/</link>
                <guid isPermaLink="false">67e6c26a4c687a76c3f3934d</guid>
                
                    <category>
                        <![CDATA[ #codenewbies ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Developer ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Programming Blogs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ performance ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rahul ]]>
                </dc:creator>
                <pubDate>Fri, 28 Mar 2025 15:38:18 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1743176201295/448f0407-8a15-4b59-a91f-8a197bc07578.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Maybe you’ve experienced something like this before: you’ve written code that works, but when you hit “run,” it takes forever. You stare at the spinner, wondering if it’s faster to just solve the problem by hand.</p>
<p>But you end up looking something like this… 😭⬇️⬇️</p>
<p><img src="https://img.ifunny.co/images/9eeae78f1bc92e6dc422c5e6af2b5a768913d2e4fa9df2d0df499c1202dfe539_1.jpg" alt="6 year old me thinking the game would load faster if i act like don't care  ORIGINALWOLFF - iFunny" width="1080" height="1100" loading="lazy"></p>
<p>Here’s the truth: slow code doesn’t have to be the end of the world. And it’s a rite of passage if you’re a developer.</p>
<p>When you’re learning to code, you’re focused on making things <em>work</em>—not making them fast. But eventually, you’ll hit a wall: your app freezes, your data script takes hours, or your game lags like a PowerPoint slideshow.</p>
<p>The difference between working code and blazing-fast code often comes down to avoiding a few common mistakes. Mistakes that are easy to make when you’re starting out, like using the wrong tool for the job, writing unnecessary code, or accidentally torturing your computer with hidden inefficiencies.</p>
<p>I’ve been there. I once wrote a “quick” script to analyze data. It ran for 3 hours. Turns out, changing one line of code cut it to 10 seconds. Yes I was dumb when I was learning – but I don’t want you to be, too.</p>
<p>That’s the power of understanding performance.</p>
<p>In this guide, I’ll break down seven common mistakes that can really tank your code’s speed—and how to fix them.</p>
<h3 id="heading-table-of-contents">Table of Contents</h3>
<ol>
<li><p><a class="post-section-overview" href="#heading-mistake-1-logging-everything-in-production-without-realizing-it">Mistake #1: Logging Everything in Production (Without Realizing It)</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-how-to-fix-it">How to Fix It</a></li>
</ul>
</li>
</ol>
<ol start="2">
<li><p><a class="post-section-overview" href="#heading-mistake-2-using-the-wrong-loops-when-theres-a-faster-alternative">Mistake #2: Using the Wrong Loops (When There’s a Faster Alternative)</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-why-this-is-a-problem-1">Why This is a Problem</a></li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-mistake-3-writing-database-queries-inside-loops-killer-of-speed">Mistake #3: Writing Database Queries Inside Loops (Killer of Speed)</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-why-this-is-a-problem-2">Why This is a Problem</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-fix-it-use-bulk-queries">How to Fix It: Use Bulk Queries</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-a-more-scalable-approach">A More Scalable Approach</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-mistake-4-not-knowing-your-hardwares-dirty-secrets">Mistake #4: Not Knowing Your Hardware’s Dirty Secrets</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-problem-1-the-cpus-crystal-ball-is-broken-memory-prefetching">Problem 1: The CPU’s Crystal Ball is Broken (Memory Prefetching)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-fix-use-contiguous-data-structures">The Fix: Use Contiguous Data Structures</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-problem-2-the-invisible-tax-of-memory-pages-tlb-thrashing">Problem 2: The Invisible Tax of Memory Pages (TLB Thrashing)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-fix-process-data-in-chunks">The Fix: Process Data in Chunks</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-problem-3-your-code-is-a-tourist-in-the-wrong-cpu-neighborhood-numa">Problem 3: Your Code is a Tourist in the Wrong CPU Neighborhood (NUMA)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-fix-pin-processes-to-numa-aware-memory">The Fix: Pin Processes to NUMA-Aware Memory</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-problem-4-the-cpu-is-a-drama-queen-speculative-execution">Problem 4: The CPU is a Drama Queen (Speculative Execution)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-fix-make-branches-predictable">The Fix: Make Branches Predictable</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-fight-back">How to Fight Back</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-mistake-5-memory-fragmentation">Mistake #5: Memory Fragmentation</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-whats-happening-under-the-hood">What’s Happening Under the Hood</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-problem-2-the-autoboxing-trap-java-c-and-so-on">Problem 2: The Autoboxing Trap (Java, C#, and so on)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-fix-use-primitive-collections">The Fix: Use Primitive Collections</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-fix-for-c">The Fix for C#</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-mistake-6-the-cache-catch">Mistake #6: The Cache (catch)</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-row-major-vs-column-major-access">Row-Major vs. Column-Major Access</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-plot-twist-your-programming-language-is-gaslighting-you">The Plot Twist: Your Programming Language is Gaslighting You</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-multidimensional-illusion-3d-arrays">The Multidimensional Illusion: 3D+ Arrays</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-nuclear-option-cache-aware-algorithms">The Nuclear Option: Cache-Aware Algorithms</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-mistake-7-the-copy-paste-trap">Mistake #7: The Copy-Paste Trap</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-problem-1-the-ghost-copies-in-harmless-operations">Problem 1: The Ghost Copies in “Harmless” Operations</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-problem-2-the-hidden-cost-of-functional-code">Problem 2: The Hidden Cost of “Functional” Code</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-problem-3-the-ill-just-modify-a-copy-mistake">Problem 3: The “I’ll Just Modify a Copy” Mistake</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-escape-the-copy-paste-hell">How to Escape the Copy-Paste hell?</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-do-pro-developers-write-faster-code">How Do Pro Developers Write Faster Code?</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-1-they-profile-their-code-instead-of-guessing">1. They Profile Their Code Instead of Guessing</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-2-they-avoid-premature-optimization">2. They Avoid Premature Optimization</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-3-they-pick-the-right-data-structures-not-just-whats-familiar">3. They Pick the Right Data Structures (Not Just What’s Familiar)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-4-they-automate-performance-checks">4. They Automate Performance Checks</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-5-they-think-about-performance-from-day-one">5. They Think About Performance From Day One</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-final-thoughts-lessons-learned-the-hard-way">Final Thoughts: Lessons Learned the Hard Way</a></p>
</li>
</ol>
<h2 id="heading-mistake-1-logging-everything-in-production-without-realizing-it"><strong>Mistake #1: Logging Everything in Production (Without Realizing It)</strong></h2>
<p>Logging is supposed to help you understand what’s happening in your code—but if you’re logging everything, you’re actually slowing it down. A common beginner mistake is leaving <code>print()</code> statements everywhere or enabling verbose logging even in production, where performance matters most.</p>
<p>Instead of logging only what’s useful, they log every function call, every input, every output, and sometimes even entire request bodies or database queries. This might seem harmless, but in a live application handling thousands of operations per second, excessive logging can cause major slowdowns.</p>
<h3 id="heading-why-this-is-a-problem">Why This is a Problem</h3>
<p>Logging isn’t free. Every log message, whether printed to the console or written to a file, adds extra processing time. If logging is done synchronously (which it often is by default), your application can pause execution while waiting for the log to be recorded.</p>
<p>It also wastes disk space. If every request gets logged in detail, log files can grow rapidly, eating up storage and making it harder to find useful information when debugging.</p>
<p>Here’s an example:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_data</span>(<span class="hljs-params">data</span>):</span>
    print(<span class="hljs-string">f"Processing data: <span class="hljs-subst">{data}</span>"</span>)  <span class="hljs-comment"># Logging every input</span>
    result = data * <span class="hljs-number">2</span>  
    print(<span class="hljs-string">f"Result: <span class="hljs-subst">{result}</span>"</span>)  <span class="hljs-comment"># Logging every result</span>
    <span class="hljs-keyword">return</span> result
</code></pre>
<p>If this function is running inside a loop handling 10,000+ operations, those print statements are slowing things down massively.</p>
<h3 id="heading-how-to-fix-it">How to Fix It</h3>
<p>Instead of logging everything, focus on logging only what actually matters. Good logging helps you diagnose real issues without cluttering your logs or slowing down your app.</p>
<p>For example, let’s say you're processing user transactions. You don’t need to log every step of the calculation, but logging when a transaction starts, succeeds, or fails is valuable.</p>
<pre><code class="lang-python">// ✅ Bad logging

logging.info(<span class="hljs-string">f"Received input: <span class="hljs-subst">{data}</span>"</span>)  
logging.info(<span class="hljs-string">f"Processing transaction for user <span class="hljs-subst">{user_id}</span>"</span>)  
logging.info(<span class="hljs-string">f"Transaction intermediate step 1 result: <span class="hljs-subst">{some_var}</span>"</span>)  
logging.info(<span class="hljs-string">f"Transaction intermediate step 2 result: <span class="hljs-subst">{another_var}</span>"</span>)  
logging.info(<span class="hljs-string">f"Transaction completed: <span class="hljs-subst">{final_result}</span>"</span>)  

// ✅ Better logging

logging.info(<span class="hljs-string">f"Processing transaction for user <span class="hljs-subst">{user_id}</span>"</span>)  
logging.info(<span class="hljs-string">f"Transaction successful. Amount: $<span class="hljs-subst">{amount}</span>"</span>)
</code></pre>
<p>Next, make sure debugging logs are turned off in production. Debug logs (<code>logging.debug()</code>) are great while developing because they show detailed information, but they shouldn’t be running on live servers.</p>
<p>You can control this by setting the logging level to <code>INFO</code> or higher:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> logging

logging.basicConfig(level=logging.INFO)  <span class="hljs-comment"># Only logs INFO, WARNING, ERROR, CRITICAL messages</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_data</span>(<span class="hljs-params">data</span>):</span>
    logging.debug(<span class="hljs-string">f"Processing data: <span class="hljs-subst">{data}</span>"</span>)  <span class="hljs-comment"># Won't show up in production</span>
    <span class="hljs-keyword">return</span> data * <span class="hljs-number">2</span>
</code></pre>
<p>Finally, for high-performance applications, consider using asynchronous logging. By default, logging operations can block execution, meaning your program waits until the log message is written before continuing. This can be a bottleneck, especially if you're logging to a file or a remote logging service.</p>
<p>Asynchronous logging solves this by handling logs in the background. Here’s how you can set it up with Python’s <code>QueueHandler</code>:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> logging
<span class="hljs-keyword">import</span> logging.handlers
<span class="hljs-keyword">import</span> queue

log_queue = queue.Queue()
queue_handler = logging.handlers.QueueHandler(log_queue)
logger = logging.getLogger()
logger.addHandler(queue_handler)
logger.setLevel(logging.INFO)

logger.info(<span class="hljs-string">"This log is handled asynchronously!"</span>)
</code></pre>
<h2 id="heading-mistake-2-using-the-wrong-loops-when-theres-a-faster-alternative"><strong>Mistake #2: Using the Wrong Loops (When There’s a Faster Alternative)</strong></h2>
<h3 id="heading-why-this-is-a-problem-1">Why This is a Problem</h3>
<p>Loops are one of the first things you learn in programming, and for loops feel natural—they give you control, they’re easy to understand, and they work everywhere. That’s why beginners tend to reach for them automatically.</p>
<p>But just because something works doesn’t mean it’s the best way. In Python, for loops can be slow—especially when there’s a built-in alternative that does the same job faster and more efficiently.</p>
<p>This isn’t just a Python thing. Most programming languages have optimized ways to handle loops under the hood—whether it's vectorized operations in NumPy, functional programming in JavaScript, or stream processing in Java. Knowing when to use them is key to writing fast, clean code.</p>
<h4 id="heading-example">Example</h4>
<p>Let’s say you want to square a list of numbers. A beginner might write this:</p>
<pre><code class="lang-python">numbers = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>]
squared = []

<span class="hljs-keyword">for</span> num <span class="hljs-keyword">in</span> numbers:
    squared.append(num ** <span class="hljs-number">2</span>)
</code></pre>
<p>Looks fine, right? But there are two inefficiencies here:</p>
<ol>
<li><p>You're manually looping when Python has a better, built-in way to handle this.</p>
</li>
<li><p>You're making repeated <code>.append()</code> calls, which add unnecessary overhead.</p>
</li>
</ol>
<p>In small cases, you won’t notice a difference. But when processing large datasets, these inefficiencies add up fast.</p>
<h3 id="heading-the-better-faster-way">The Better, Faster Way</h3>
<p>Python has built-in optimizations that make loops run faster. One of them is list comprehensions, which are optimized in C and run significantly faster than manual loops. Here’s how you can rewrite the example:</p>
<pre><code class="lang-python">pythonCopyEdit<span class="hljs-comment"># Much faster and cleaner</span>
squared = [num ** <span class="hljs-number">2</span> <span class="hljs-keyword">for</span> num <span class="hljs-keyword">in</span> numbers]
</code></pre>
<h4 id="heading-why-this-is-better">Why this is better:</h4>
<ol>
<li><p><strong>It’s faster.</strong> List comprehensions run in C under the hood, meaning they don’t have the overhead of Python function calls like <code>.append()</code>.</p>
</li>
<li><p><strong>It eliminates extra work.</strong> Instead of growing a list dynamically (which requires resizing in memory), Python pre-allocates space for the entire list. This makes the operation much more efficient.</p>
</li>
<li><p><strong>It’s more readable.</strong> The intent is clear: "I’m creating a list by squaring each number"—no need to scan through multiple lines of code.</p>
</li>
<li><p><strong>It’s less error-prone.</strong> Since everything happens in a single expression, there’s less chance of accidentally modifying the list incorrectly (for example, forgetting to <code>.append()</code>).</p>
</li>
</ol>
<h3 id="heading-when-to-use-for-loops-vs-list-comprehensions">When to Use For Loops vs. List Comprehensions</h3>
<p>For loops still have their place. Use them when:</p>
<ul>
<li><p>You need complex logic inside the loop (for example, multiple operations per iteration).</p>
</li>
<li><p>You need to modify existing data in place rather than create a new list.</p>
</li>
<li><p>The operation involves side effects, like logging, file writing, or network requests.</p>
</li>
</ul>
<p>Otherwise, list comprehensions should be your default choice for simple transformations. They’re faster, cleaner, and make your Python code more efficient.</p>
<h2 id="heading-mistake-3-writing-database-queries-inside-loops-killer-of-speed"><strong>Mistake #3: Writing Database Queries Inside Loops (Killer of Speed)</strong></h2>
<h3 id="heading-why-this-is-a-problem-2"><strong>Why This is a Problem</strong></h3>
<p>This is one of the biggest slow-code mistakes beginners (and even intermediates) make. It happens because loops feel natural, and database queries feel straightforward. But mix the two together, and you’ve got a performance disaster.</p>
<p>Every time you call a database inside a loop, you're making repeated trips to the database. Each query adds network latency, processing overhead, and unnecessary load on your system.</p>
<h4 id="heading-example-1">Example:</h4>
<p>Imagine you’re fetching user details for a list of <code>user_ids</code> like this:</p>
<pre><code class="lang-python">pythonCopyEdituser_ids = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>]

<span class="hljs-keyword">for</span> user_id <span class="hljs-keyword">in</span> user_ids:
    user = db.query(<span class="hljs-string">f"SELECT * FROM users WHERE id = <span class="hljs-subst">{user_id}</span>"</span>)
    print(user)  <span class="hljs-comment"># Do something with the user</span>
</code></pre>
<p><strong>What's wrong here?</strong></p>
<ul>
<li><p>You're hitting the database multiple times instead of once.</p>
</li>
<li><p>Each call has network overhead (database queries aren’t instant).</p>
</li>
<li><p>Performance tanks when user_ids gets large.</p>
</li>
</ul>
<h3 id="heading-how-to-fix-it-use-bulk-queries"><strong>How to Fix It: Use Bulk Queries</strong></h3>
<p>Instead of making 5 separate queries, make one:</p>
<pre><code class="lang-python">pythonCopyEdituser_ids = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>]

users = db.query(<span class="hljs-string">f"SELECT * FROM users WHERE id IN (<span class="hljs-subst">{<span class="hljs-string">','</span>.join(map(str, user_ids))}</span>)"</span>)

<span class="hljs-keyword">for</span> user <span class="hljs-keyword">in</span> users:
    print(user)  <span class="hljs-comment"># Process users efficiently</span>
</code></pre>
<p><strong>Why this is better:</strong></p>
<ul>
<li><p>In the above code, we just have one database call instead of many. This results in faster performance.</p>
</li>
<li><p>There’s also less network overhead which makes your app feel snappier.</p>
</li>
<li><p>And this works even if <code>user_ids</code> has 10,000+ entries.</p>
</li>
</ul>
<h3 id="heading-a-more-scalable-approach"><strong>A More Scalable Approach</strong></h3>
<p>If you're using an ORM (like SQLAlchemy in Python or Sequelize in JavaScript), use batch fetching instead of looping:</p>
<pre><code class="lang-python">pythonCopyEditusers = db.query(User).filter(User.id.in_(user_ids)).all()
</code></pre>
<h2 id="heading-mistake-4-not-knowing-your-hardwares-dirty-secrets"><strong>Mistake #4: Not Knowing Your Hardware’s Dirty Secrets</strong></h2>
<p>Your code doesn’t run in a magical fairyland—it runs on real hardware. CPUs, memory, and caches have quirks that can turn “logically fast” code into a sluggish mess. Here’s what most tutorials won’t tell you:</p>
<h3 id="heading-problem-1-the-cpus-crystal-ball-is-broken-memory-prefetching"><strong>Problem 1: The CPU’s Crystal Ball is Broken (Memory Prefetching)</strong></h3>
<h4 id="heading-what-you-think-happens">What you think happens:</h4>
<p><em>“I’m looping through data sequentially. The CPU should predict what I need next!”</em></p>
<h4 id="heading-what-actually-happens">What actually happens:</h4>
<p>Modern CPUs have a memory prefetcher—a smart assistant that tries to guess which memory locations you’ll need next and loads them in advance.</p>
<p>But here’s the catch: If your access pattern is too random, the prefetcher gives up. Instead of smoothly fetching data ahead of time, the CPU is left waiting, like someone stuck refreshing Google Maps on a broken internet connection or blind date.</p>
<p>This happens a lot with linked lists and hash tables, where memory jumps around unpredictably.</p>
<h4 id="heading-example-2">Example:</h4>
<pre><code class="lang-python"><span class="hljs-comment"># Linked list traversal (random memory jumps)  </span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Node</span>:</span>  
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, val</span>):</span>  
        self.val = val  
        self.next = <span class="hljs-literal">None</span>  

head = Node(<span class="hljs-number">0</span>)  
current = head  
<span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> range(<span class="hljs-number">100000</span>):  <span class="hljs-comment"># Each 'next' points to a random memory location  </span>
    current.next = Node(<span class="hljs-number">0</span>)  
    current = current.next  

<span class="hljs-comment"># Walking this list = 100,000 cache misses</span>
</code></pre>
<h4 id="heading-why-this-hurts">Why this hurts:</h4>
<p>Each time the CPU needs the next <code>Node</code>, it has to fetch it from a random memory location, making prefetching useless and causing frequent cache misses.</p>
<h3 id="heading-the-fix-use-contiguous-data-structures"><strong>The Fix: Use Contiguous Data Structures</strong></h3>
<p>Instead of using a linked list, store your data in a contiguous memory block (like an array or NumPy array). This way, the CPU can easily prefetch the next elements in sequence, speeding things up.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Array traversal (prefetcher-friendly)  </span>
data = [<span class="hljs-number">0</span>] * <span class="hljs-number">100000</span>  <span class="hljs-comment"># Contiguous memory  </span>
<span class="hljs-keyword">for</span> item <span class="hljs-keyword">in</span> data:  
    <span class="hljs-keyword">pass</span>  <span class="hljs-comment"># CPU prefetches next elements seamlessly</span>
</code></pre>
<p><strong>Why this is better:</strong></p>
<ul>
<li><p>The CPU efficiently prefetches upcoming values instead of waiting.</p>
</li>
<li><p>Fewer cache misses = way faster execution.</p>
</li>
<li><p>Hot loops (loops that run millions of times) get a huge performance boost.</p>
</li>
</ul>
<p>📌 <strong>Hot loops</strong> are loops that execute a massive number of times, like those in data processing, AI models, and game engines. Even a small speedup in a hot loop can dramatically improve overall performance.</p>
<h3 id="heading-problem-2-the-invisible-tax-of-memory-pages-tlb-thrashing"><strong>Problem 2: The Invisible Tax of Memory Pages (TLB Thrashing)</strong></h3>
<h4 id="heading-what-you-think-happens-1">What you think happens:</h4>
<p><em>“My 10GB dataset is just… there. Accessing it is free, right?”</em></p>
<h4 id="heading-what-actually-happens-1">What actually happens:</h4>
<p>Your OS splits memory into 4KB pages. Every time your program accesses a new memory page, the CPU consults a Translation Lookaside Buffer (TLB)—a “phonebook” for fast page lookups.</p>
<p>If your program jumps between too many pages, you get TLB misses, and the CPU wastes cycles waiting for the OS to fetch memory mappings.</p>
<h4 id="heading-example-3">Example:</h4>
<pre><code class="lang-python"><span class="hljs-comment"># Iterating a giant list with random access  </span>
data = [x <span class="hljs-keyword">for</span> x <span class="hljs-keyword">in</span> range(<span class="hljs-number">10</span>_000_000)]  
total = <span class="hljs-number">0</span>  
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> random_indexes:  <span class="hljs-comment"># 1,000,000 random jumps  </span>
    total += data[i]  <span class="hljs-comment"># Each jump likely hits a new page</span>
</code></pre>
<h4 id="heading-why-this-hurts-1">Why this hurts:</h4>
<ul>
<li><p>TLB misses can add 10-100 CPU cycles per access.</p>
</li>
<li><p>If you have millions of random accesses, that’s billions of wasted cycles.</p>
</li>
</ul>
<h3 id="heading-the-fix-process-data-in-chunks"><strong>The Fix: Process Data in Chunks</strong></h3>
<p>To reduce TLB misses:</p>
<ul>
<li><p><strong>Process data in chunks</strong> (for example, 4096 elements at a time) instead of randomly jumping around.</p>
</li>
<li><p><strong>Use huge pages</strong> (2MB instead of 4KB) so that more data fits in each memory page.</p>
</li>
</ul>
<h3 id="heading-problem-3-your-code-is-a-tourist-in-the-wrong-cpu-neighborhood-numa"><strong>Problem 3: Your Code is a Tourist in the Wrong CPU Neighborhood (NUMA)</strong></h3>
<h4 id="heading-what-you-think-happens-2">What you think happens:</h4>
<p><em>“My 64-core server is a speed paradise!”</em></p>
<h4 id="heading-what-actually-happens-2">What actually happens:</h4>
<p>On multi-socket servers, memory is divided into NUMA (Non-Uniform Memory Access) zones. Each CPU socket has its own local memory, and accessing memory from another socket is slow—like ordering Uber Eats from another city.</p>
<h4 id="heading-example-4">Example:</h4>
<pre><code class="lang-python"><span class="hljs-comment"># Running this on a 2-socket server:  </span>
<span class="hljs-keyword">from</span> multiprocessing <span class="hljs-keyword">import</span> Pool  
<span class="hljs-keyword">import</span> numpy <span class="hljs-keyword">as</span> np  

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process</span>(<span class="hljs-params">chunk</span>):</span>  
    data = np.load(<span class="hljs-string">"giant_array.npy"</span>)  <span class="hljs-comment"># Allocated on Socket 1's RAM  </span>
    <span class="hljs-keyword">return</span> chunk * data  <span class="hljs-comment"># If process runs on Socket 2's CPU... ouch  </span>

<span class="hljs-keyword">with</span> Pool(<span class="hljs-number">64</span>) <span class="hljs-keyword">as</span> p:  
    p.map(process, big_data)  <span class="hljs-comment"># 64 cores fighting over remote RAM</span>
</code></pre>
<h4 id="heading-why-this-hurts-2">Why this hurts:</h4>
<ul>
<li><p>Accessing memory from another NUMA zone can be 2-4x slower.</p>
</li>
<li><p>Your 64 cores end up waiting for memory instead of actually computing.</p>
</li>
</ul>
<h3 id="heading-the-fix-pin-processes-to-numa-aware-memory"><strong>The Fix: Pin Processes to NUMA-Aware Memory</strong></h3>
<p>Instead of letting your processes randomly access memory, you can pin them to the correct NUMA node.</p>
<ul>
<li><p>Use <code>numactl</code> on Linux to allocate memory near the CPU that will use it.</p>
</li>
<li><p>Use <code>numba</code>-aware libraries in NumPy to ensure data is allocated optimally.</p>
</li>
</ul>
<h3 id="heading-problem-4-the-cpu-is-a-drama-queen-speculative-execution"><strong>Problem 4: The CPU is a Drama Queen (Speculative Execution)</strong></h3>
<h4 id="heading-what-you-think-happens-3">What you think happens:</h4>
<p><em>“My code runs in the order I wrote it!”</em></p>
<h4 id="heading-what-actually-happens-3">What actually happens:</h4>
<p>CPUs speculatively execute code ahead of time. If they guess wrong, they have to rollback everything and restart, which slows things down.</p>
<h4 id="heading-example-5"><strong>Example:</strong></h4>
<pre><code class="lang-cpp"><span class="hljs-comment">// Unpredictable branches = CPU's worst nightmare  </span>
<span class="hljs-keyword">if</span> (rare_condition) {  <span class="hljs-comment">// 99% of the time, this is false  </span>
    do_work();  
}
</code></pre>
<h4 id="heading-why-this-hurts-3">Why this hurts:</h4>
<p>A branch misprediction wastes 15-20 cycles. In hot loops, this can really hurt performance.</p>
<h3 id="heading-the-fix-make-branches-predictable"><strong>The Fix: Make Branches Predictable</strong></h3>
<p>Sort data to help the CPU make better predictions:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Process all 'valid' items first, then 'invalid' ones  </span>
sorted_data = sorted(data, key=<span class="hljs-keyword">lambda</span> x: x.is_valid, reverse=<span class="hljs-literal">True</span>)  
<span class="hljs-keyword">for</span> item <span class="hljs-keyword">in</span> sorted_data:  
    <span class="hljs-keyword">if</span> item.is_valid:  <span class="hljs-comment"># CPU learns the pattern → accurate predictions  </span>
        process(item)
</code></pre>
<p><strong>Why This Works:</strong></p>
<ul>
<li><p>Branching becomes predictable—the CPU stops guessing wrong.</p>
</li>
<li><p>Sorting ahead of time reduces rollbacks and wasted cycles.</p>
</li>
</ul>
<h3 id="heading-how-to-fight-back"><strong>How to Fight Back</strong></h3>
<p>Here’s how you can stop your CPU from sabotaging your code:</p>
<ol>
<li>Treat Memory Like a Highway: Cache lines matter. Keep data contiguous so the CPU doesn’t have to search for it.</li>
</ol>
<ol start="2">
<li><p>Profile with <code>perf</code>: Use Linux’s <code>perf</code> tool to spot cache misses, page faults, and TLB thrashing:</p>
<pre><code class="lang-bash"> perf <span class="hljs-built_in">stat</span> -e cache-misses,page-faults ./your_code
</code></pre>
</li>
</ol>
<ol start="3">
<li>Assume Nothing. Benchmark Everything: CPUs have a thousand undocumented behaviors. Test different data layouts, loop structures, and memory allocations to see what’s fastest.</li>
</ol>
<h2 id="heading-mistake-5-memory-fragmentation"><strong>Mistake #5: Memory Fragmentation</strong></h2>
<p>You’ve optimized your algorithms. You’ve nailed Big O. Yet your app still crashes with “out of memory” errors or slows to a crawl over time. The culprit? Memory fragmentation—a ghost in the machine that most developers ignore until it’s too late.</p>
<h4 id="heading-whats-happening-under-the-hood">What’s Happening Under the Hood</h4>
<p>When your code allocates and frees memory blocks of varying sizes, it leaves behind a patchwork of free and used spaces. Over time, this creates a Swiss cheese effect in your RAM: plenty of total free memory, but no contiguous blocks for new allocations.</p>
<p><strong>Example:</strong><br>Imagine a C++ server that handles requests by allocating buffers of random sizes:</p>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">process_request</span><span class="hljs-params">()</span> </span>{  
    <span class="hljs-comment">// Allocate a buffer of random size between 1–1024 bytes  </span>
    <span class="hljs-keyword">char</span>* buffer = <span class="hljs-keyword">new</span> <span class="hljs-keyword">char</span>[rand() % <span class="hljs-number">1024</span> + <span class="hljs-number">1</span>];  
    <span class="hljs-comment">// ... process ...  </span>
    <span class="hljs-keyword">delete</span>[] buffer;  
}
</code></pre>
<p>After millions of requests, your memory looks like this:</p>
<p><code>[USED][FREE][USED][FREE][USED][FREE]...</code></p>
<p>Now, when you try to allocate a 2KB buffer, it fails—not because there’s no space, but because no single free block is large enough.</p>
<h4 id="heading-how-to-fix-it-1">How to Fix it:</h4>
<p>Use a memory pool to allocate fixed-size blocks:</p>
<pre><code class="lang-cpp"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MemoryPool</span> {</span>  
<span class="hljs-keyword">public</span>:  
    MemoryPool(<span class="hljs-keyword">size_t</span> block_size) : block_size_(block_size) {}  
    <span class="hljs-function"><span class="hljs-keyword">void</span>* <span class="hljs-title">allocate</span><span class="hljs-params">()</span> </span>{ <span class="hljs-comment">/* get a pre-allocated block */</span> }  
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">deallocate</span><span class="hljs-params">(<span class="hljs-keyword">void</span>* ptr)</span> </span>{ <span class="hljs-comment">/* return block to pool */</span> }  
};  

<span class="hljs-comment">// All requests use buffers of fixed size (1024 bytes)  </span>
<span class="hljs-function">MemoryPool <span class="hljs-title">pool</span><span class="hljs-params">(<span class="hljs-number">1024</span>)</span></span>;  
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">process_request</span><span class="hljs-params">()</span> </span>{  
    <span class="hljs-keyword">char</span>* buffer = <span class="hljs-keyword">static_cast</span>&lt;<span class="hljs-keyword">char</span>*&gt;(pool.allocate());  
    <span class="hljs-comment">// ... process ...  </span>
    pool.deallocate(buffer);  
}
</code></pre>
<p>By standardizing block sizes, you eliminate fragmentation.</p>
<h3 id="heading-the-autoboxing-trap-java-c-and-so-on">The Autoboxing Trap (Java, C#, and so on)</h3>
<h4 id="heading-whats-happening">What’s Happening?</h4>
<p>In languages that mix primitives (like <code>int</code>, <code>float</code>) and objects (like <code>Integer</code>, <code>Double</code>), converting a primitive to its object wrapper is called <strong>autoboxing</strong>. It feels harmless, but in hot loops, it’s a performance disaster.</p>
<p><strong>Example:</strong></p>
<pre><code class="lang-java"><span class="hljs-comment">// Slow: Creates 1,000,000 Integer objects (and garbage!)</span>
List&lt;Integer&gt; list = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;();
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">1_000_000</span>; i++) {  
    list.add(i);  <span class="hljs-comment">// Autoboxing 'i' to Integer  </span>
}
</code></pre>
<h4 id="heading-why-this-hurts-performance">Why this hurts performance:</h4>
<ul>
<li><p><strong>Memory overhead:</strong> Each <code>Integer</code> object adds 16–24 bytes of extra memory (object headers, pointers). With 1,000,000 numbers, that’s an extra 16–24MB wasted just on overhead.</p>
</li>
<li><p><strong>Garbage collection (GC) pressure:</strong> Since objects are allocated on the heap, the GC constantly cleans up old <code>Integer</code> objects, leading to latency spikes.</p>
</li>
<li><p><strong>CPU cache inefficiency:</strong> Primitives like <code>int</code> are tightly packed in memory, but <code>Integer</code> objects are scattered across the heap with extra indirection, wrecking cache locality.</p>
</li>
</ul>
<h4 id="heading-the-fix-use-primitive-collections">The Fix: Use Primitive Collections</h4>
<p>To avoid autoboxing, use data structures that store raw primitives instead of objects. In Java, Eclipse Collections provides primitive-friendly lists like <code>IntList</code> that store raw <code>int</code> values directly.</p>
<p><strong>Example: The Faster Version (Primitive Collections)</strong></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Import primitive-friendly collection</span>
<span class="hljs-keyword">import</span> org.eclipse.collections.api.list.primitive.IntList;
<span class="hljs-keyword">import</span> org.eclipse.collections.impl.list.mutable.primitive.IntArrayList;  

<span class="hljs-comment">// Use IntArrayList to store raw ints</span>
IntList list = <span class="hljs-keyword">new</span> IntArrayList();  
<span class="hljs-keyword">for</span> (int i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">1</span>_000_000; i++) {  
    list.add(i);  <span class="hljs-comment">// No autoboxing! Stores raw 'int'  </span>
}
</code></pre>
<h4 id="heading-how-this-fix-works">How this fix works:</h4>
<ul>
<li><p>Stores raw <code>int</code> values instead of <code>Integer</code> objects, eliminating memory overhead.</p>
</li>
<li><p>Avoids heap allocations, so the garbage collector doesn’t get involved.</p>
</li>
<li><p>Keeps numbers tightly packed in memory, improving CPU cache efficiency.</p>
</li>
</ul>
<h4 id="heading-the-fix-for-c">The Fix for C</h4>
<p>In C#, you can avoid unnecessary heap allocations by using <code>struct</code>s and <code>Span&lt;T&gt;</code>, which keep data on the stack or in contiguous memory rather than the heap.</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Span&lt;T&gt; avoids heap allocations  </span>
Span&lt;<span class="hljs-keyword">int</span>&gt; numbers = <span class="hljs-keyword">stackalloc</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">1</span>_000_000];  
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; numbers.Length; i++) {  
    numbers[i] = i;  <span class="hljs-comment">// No boxing, no heap allocation  </span>
}
</code></pre>
<p>No object wrappers. No GC pressure. Just performance.</p>
<h2 id="heading-mistake-6-the-cache-catch"><strong>Mistake #6: The Cache (catch)</strong></h2>
<p>You’ve heard “cache matters,” but here’s the twist: your loops are lying to your CPU. The way you traverse multi-dimensional arrays can turn a 10x speed difference into a mystery that leaves you questioning reality.</p>
<h3 id="heading-row-major-vs-column-major-access"><strong>Row-Major vs. Column-Major Access</strong></h3>
<p><strong>What you think happens</strong>:<br><em>“Iterating over a 2D array is the same whether I go row-by-row or column-by-column. Right?”</em></p>
<p><strong>What actually happens</strong>:<br>Memory is laid out linearly, but CPUs prefetch data in chunks (cache lines). Traversing against the grain forces the CPU to fetch new cache lines <em>every single step</em>.</p>
<p><strong>Example in C</strong>:</p>
<pre><code class="lang-c"><span class="hljs-comment">// A "tiny" 1024x1024 matrix  </span>
<span class="hljs-keyword">int</span> matrix[<span class="hljs-number">1024</span>][<span class="hljs-number">1024</span>];  

<span class="hljs-comment">// Fast: Row-major traversal (cache-friendly)  </span>
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">1024</span>; i++) {  
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> j = <span class="hljs-number">0</span>; j &lt; <span class="hljs-number">1024</span>; j++) {  
        matrix[i][j] = i + j;  
    }  
}  

<span class="hljs-comment">// Slow: Column-major traversal (cache-hostile)  </span>
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> j = <span class="hljs-number">0</span>; j &lt; <span class="hljs-number">1024</span>; j++) {  
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">1024</span>; i++) {  
        matrix[i][j] = i + j;  
    }  
}
</code></pre>
<p><strong>The result</strong>:</p>
<ul>
<li><p>Row-major: ~5ms (data flows like a river).</p>
</li>
<li><p>Column-major: ~50ms (CPU drowns in cache misses).</p>
</li>
</ul>
<p><strong>Why it’s worse than you think</strong>:<br>In C/C++, arrays are row-major. But in Fortran, MATLAB, or Julia, they’re column-major. Use the wrong traversal order in these languages, and you’ll get the same penalty.</p>
<h3 id="heading-the-plot-twist-your-programming-language-is-gaslighting-you"><strong>The Pl**</strong>ot Twist: Your Programming Language is Gaslighting You**</h3>
<p>In C and Python (NumPy default), arrays use row-major order. But in Fortran, MATLAB, and Julia, arrays are column-major. If you assume the wrong layout, your loops will be slow for no apparent reason.</p>
<p><strong>Python Example</strong>:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> numpy <span class="hljs-keyword">as</span> np  

<span class="hljs-comment"># Row-major (C-style) → Fast for row-wise loops  </span>
row_major = np.zeros((<span class="hljs-number">1024</span>, <span class="hljs-number">1024</span>), order=<span class="hljs-string">'C'</span>)  

<span class="hljs-comment"># Column-major (Fortran-style) → Fast for column-wise loops  </span>
col_major = np.zeros((<span class="hljs-number">1024</span>, <span class="hljs-number">1024</span>), order=<span class="hljs-string">'F'</span>)  

<span class="hljs-comment"># ❌ Slow: Column-wise access on a row-major array  </span>
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">1024</span>):  
    <span class="hljs-keyword">for</span> j <span class="hljs-keyword">in</span> range(<span class="hljs-number">1024</span>):  
        col_major[i][j] = i + j  <span class="hljs-comment"># Cache-miss chaos!</span>
</code></pre>
<h4 id="heading-why-this-is-a-problem-3">Why this is a problem:</h4>
<ul>
<li><p>Row-major (default in NumPy) expects row-wise access, but the loop accesses it column-wise, causing cache misses.</p>
</li>
<li><p>Fortran-style arrays are stored column-first, so row-wise loops will be slow instead.</p>
</li>
</ul>
<h4 id="heading-the-fix">The Fix:</h4>
<ul>
<li><p>Match the array order to your access pattern using <code>order='C'</code> (row-major) or <code>order='F'</code> (column-major).</p>
</li>
<li><p>Convert data layout with <code>np.asarray()</code> if needed.</p>
</li>
</ul>
<h3 id="heading-the-multidimensional-illusion-3d-arrays"><strong>The Multidimensional Illusion**</strong>: 3D+ Arrays**</h3>
<p><strong>What you think happens</strong>:<br><em>“3D arrays are just 2D arrays with extra steps. No big deal.”</em></p>
<p><strong>What actually happens</strong>:<br>Each dimension adds a layer of indirection. A 3D array in C is an array of arrays of arrays. Traversing the “wrong” dimension forces the CPU to dereference pointers repeatedly, killing locality.</p>
<p><strong>Example</strong>: 3D Array in Traversal in C</p>
<pre><code class="lang-c"><span class="hljs-comment">// ✅ Fast: Iterate in Row-Major Order (Innermost Dimension Last)</span>

<span class="hljs-keyword">int</span> space[<span class="hljs-number">256</span>][<span class="hljs-number">256</span>][<span class="hljs-number">256</span>];  

<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> x = <span class="hljs-number">0</span>; x &lt; <span class="hljs-number">256</span>; x++) {  
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> y = <span class="hljs-number">0</span>; y &lt; <span class="hljs-number">256</span>; y++) {  
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> z = <span class="hljs-number">0</span>; z &lt; <span class="hljs-number">256</span>; z++) {  
            space[x][y][z] = x + y + z;  <span class="hljs-comment">// Smooth memory access  </span>
        }  
    }  
}
</code></pre>
<p>So what happens is that the innermost loop moves through contiguous memory, making full use of cache lines.</p>
<pre><code class="lang-c"><span class="hljs-comment">// ❌ Slow: Iterate in the Wrong Order (Innermost Dimension First)</span>

<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> z = <span class="hljs-number">0</span>; z &lt; <span class="hljs-number">256</span>; z++) {  
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> y = <span class="hljs-number">0</span>; y &lt; <span class="hljs-number">256</span>; y++) {  
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> x = <span class="hljs-number">0</span>; x &lt; <span class="hljs-number">256</span>; x++) {  
            space[x][y][z] = x + y + z;  <span class="hljs-comment">// Constant cache misses  </span>
        }  
    }  
}
</code></pre>
<p><strong>Why this is bad</strong>:</p>
<ul>
<li><p>This loop jumps across memory every time <code>x</code> changes.</p>
</li>
<li><p>Instead of accessing contiguous memory, it dereferences pointers constantly.</p>
</li>
<li><p>Penalty: Up to 100x slower for large 3D arrays!</p>
</li>
</ul>
<h3 id="heading-the-nuclear-option-cache-aware-algorithms"><strong>The Nuclear Option: Cache-Aware Algorithms</strong></h3>
<p>For extreme performance (game engines, HPC), you need to design for cache lines:</p>
<ol>
<li><p><strong>Tiling</strong>: Split arrays into small blocks that fit in L1/L2 cache.</p>
<pre><code class="lang-python"> // Process <span class="hljs-number">8</span>x8 tiles to exploit <span class="hljs-number">64</span>-byte cache lines  
 <span class="hljs-keyword">for</span> (int i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">1024</span>; i += <span class="hljs-number">8</span>) {  
     <span class="hljs-keyword">for</span> (int j = <span class="hljs-number">0</span>; j &lt; <span class="hljs-number">1024</span>; j += <span class="hljs-number">8</span>) {  
         // Process tile[i:i+<span class="hljs-number">8</span>][j:j+<span class="hljs-number">8</span>]  
     }  
 }
</code></pre>
</li>
<li><p><strong>SoA vs. AoS</strong>: Prefer Structure of Arrays (SoA) over Array of Structures for SIMD.</p>
<pre><code class="lang-python"> // Slow: Array of Structures (AoS)  
 struct Particle { float x, y, z; };  
 Particle particles[<span class="hljs-number">1000000</span>];  

 // Fast: Structure of Arrays (SoA)  
 struct Particles {  
     float x[<span class="hljs-number">1000000</span>];  
     float y[<span class="hljs-number">1000000</span>];  
     float z[<span class="hljs-number">1000000</span>];  
 };
</code></pre>
</li>
</ol>
<h2 id="heading-mistake-7-the-copy-paste-trap"><strong>Mistake #7: The Copy-Paste Trap</strong></h2>
<p>You’d never download 10 copies of the same movie. But in code? You’re probably cloning data <em>all the time</em> without realizing it. Here’s how invisible copies turn your app into a bloated, slow mess—and how to fix it.</p>
<h3 id="heading-problem-1-the-ghost-copies-in-harmless-operations"><strong>Problem 1: The Ghost Copies in “Harmless” Operations</strong></h3>
<p><strong>What you think happens</strong>:<br><em>“I sliced a list—it’s just a reference, right?”</em></p>
<p><strong>What actually happens</strong>:<br>In many languages, slicing creates a full copy of the data. Do this with large datasets, and you’re silently doubling memory usage and CPU work.</p>
<p><strong>Python Example</strong>:</p>
<pre><code class="lang-python"><span class="hljs-comment"># A 1GB list of data  </span>
big_data = [ ... ]  <span class="hljs-comment"># 1,000,000 elements  </span>

<span class="hljs-comment"># Accidentally cloning the entire list  </span>
snippet = big_data[:<span class="hljs-number">1000</span>]  <span class="hljs-comment"># Creates a copy (harmless, right?)  </span>

<span class="hljs-comment"># Better: Use a view (if possible)  </span>
<span class="hljs-keyword">import</span> numpy <span class="hljs-keyword">as</span> np  
big_array = np.array(big_data)  
snippet = big_array[:<span class="hljs-number">1000</span>]  <span class="hljs-comment"># A view, not a copy (0MB added)</span>
</code></pre>
<h4 id="heading-why-this-hurts-4">Why this hurts:</h4>
<ul>
<li><p>Copying 1GB → 2GB of RAM used.</p>
</li>
<li><p>If this happens in a loop, your program could crash with <code>MemoryError</code>.</p>
</li>
</ul>
<h4 id="heading-the-fix-1">The Fix:</h4>
<ul>
<li><p>Use memory views (<code>numpy</code>, <code>memoryview</code> in Python) or lazy slicing (Pandas <code>.iloc</code>).</p>
</li>
<li><p>In JavaScript, <code>slice()</code> copies arrays—replace with <code>TypedArray.subarray</code> for buffers.</p>
</li>
</ul>
<h3 id="heading-problem-2-the-hidden-cost-of-functional-code"><strong>Problem 2: The Hidden Cost of “Functional” Code</strong></h3>
<h4 id="heading-what-you-think-happens-4">What you think happens:</h4>
<p><em>“I’ll chain array methods for clean, readable code!”</em></p>
<h4 id="heading-what-actually-happens-4">What actually happens:</h4>
<p>Every <code>map</code>, <code>filter</code>, or <code>slice</code> creates a new array. Chain three operations? You’ve cloned your data three times.</p>
<p><strong>JavaScript Example</strong>:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// A 10,000-element array  </span>
<span class="hljs-keyword">const</span> data = [ ... ];  

<span class="hljs-comment">// Slow: Creates 3 copies (original → filtered → mapped → sliced)  </span>
<span class="hljs-keyword">const</span> result = data  
  .filter(<span class="hljs-function"><span class="hljs-params">x</span> =&gt;</span> x.active)  
  .map(<span class="hljs-function"><span class="hljs-params">x</span> =&gt;</span> x.value * <span class="hljs-number">2</span>)  
  .slice(<span class="hljs-number">0</span>, <span class="hljs-number">100</span>);  

<span class="hljs-comment">// Faster: Do it in one pass  </span>
<span class="hljs-keyword">const</span> result = [];  
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; data.length; i++) {  
  <span class="hljs-keyword">if</span> (data[i].active) {  
    result.push(data[i].value * <span class="hljs-number">2</span>);  
    <span class="hljs-keyword">if</span> (result.length === <span class="hljs-number">100</span>) <span class="hljs-keyword">break</span>;  
  }  
}
</code></pre>
<p><strong>Why this hurts</strong>:</p>
<ul>
<li><p>10,000 elements → 30,000 operations + 3x memory.</p>
</li>
<li><p>Functional programming is <em>elegant</em> but can be <em>expensive</em>.</p>
</li>
</ul>
<h4 id="heading-the-fix-2">The Fix:</h4>
<ul>
<li><p>Use generators (Python <code>yield</code>, JS <code>function*</code>) for lazy processing.</p>
</li>
<li><p>Replace method chains with single-pass loops in hot paths.</p>
</li>
</ul>
<h3 id="heading-problem-3-the-ill-just-modify-a-copy-mistake"><strong>Problem 3: The “I’ll Just Modify a Copy” Mistake</strong></h3>
<p><strong>What you think happens</strong>:<br><em>“I need to tweak this object. I’ll duplicate it to avoid side effects.”</em></p>
<p><strong>What actually happens</strong>:<br>Deep cloning complex objects (especially in loops) is like photocopying a dictionary every time you edit a word.</p>
<p><strong>Python Example</strong>:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> copy  

config = {<span class="hljs-string">"theme"</span>: <span class="hljs-string">"dark"</span>, <span class="hljs-string">"settings"</span>: { ... }}  <span class="hljs-comment"># Nested data  </span>

<span class="hljs-comment"># Slow: Deep-copying before every edit  </span>
<span class="hljs-keyword">for</span> user <span class="hljs-keyword">in</span> users:  
    user_config = copy.deepcopy(config)  <span class="hljs-comment"># Copies entire nested structure  </span>
    user_config[<span class="hljs-string">"theme"</span>] = user.preference  
    <span class="hljs-comment"># ...  </span>

<span class="hljs-comment"># Faster: Reuse the base config, overlay changes  </span>
<span class="hljs-keyword">for</span> user <span class="hljs-keyword">in</span> users:  
    user_config = {<span class="hljs-string">"theme"</span>: user.preference, **config}  <span class="hljs-comment"># Shallow merge  </span>
    <span class="hljs-comment"># ...</span>
</code></pre>
<p><strong>Why this hurts</strong>:</p>
<ul>
<li><p><code>deepcopy</code> is 10-100x slower than shallow copies.</p>
</li>
<li><p>Multiplied by 1,000 users, you’re wasting minutes.</p>
</li>
</ul>
<h4 id="heading-the-fix-3">The Fix:</h4>
<ul>
<li><p>Use immutable patterns: Create new objects by merging instead of cloning.</p>
</li>
<li><p>For big data, use structural sharing (libraries like <code>immutables</code> in Python).</p>
</li>
</ul>
<h3 id="heading-how-to-escape-the-copy-paste-hell"><strong>How to Escape the Copy-Paste hell?</strong></h3>
<ol>
<li><p><strong>Ask: “Do I need a copy?”</strong>: 90% of the time, you don’t. Use views, generators, or in-place edits.</p>
</li>
<li><p><strong>Profile memory usage</strong>: Tools like <code>memory_profiler</code> (Python) or Chrome DevTools (JS) show copy overhead.</p>
</li>
<li><p><strong>Learn your language’s quirks</strong>:</p>
<ul>
<li><p>Python: Slicing lists copies, slicing NumPy arrays doesn’t.</p>
</li>
<li><p>JavaScript: <code>[...array]</code> clones, <code>array.subarray</code> (TypedArray) doesn’t.</p>
</li>
</ul>
</li>
</ol>
<h2 id="heading-how-do-pro-developers-write-faster-code">How Do Pro Developers Write Faster Code?</h2>
<p>Most beginners think "fast code" just means writing cleaner syntax or using a different framework. But in reality, performance isn't just about what language or framework you use—it's about how you think.</p>
<p>Pro developers don’t just write code. They measure, test, and optimize it<strong>.</strong> Here’s how they do it.</p>
<h3 id="heading-1-they-profile-their-code-instead-of-guessing"><strong>1. They Profile Their Code Instead of Guessing</strong></h3>
<p>🔥 Beginners: “This function feels slow… maybe I should rewrite it?”<br>💡 Pros: “Let’s profile it and see what’s actually slow.”</p>
<p>Instead of randomly rewriting code, pro developers measure first using <a target="_blank" href="https://www.freecodecamp.org/news/how-to-use-pythons-built-in-profiling-tools-examples-and-best-practices/">profiling tools</a>.</p>
<p><strong>Example:</strong> In Python, you can use <code>cProfile</code> to analyze where your code is spending the most time:</p>
<pre><code class="lang-javascript">pythonCopyEditimport cProfile

def slow_function():
    total = <span class="hljs-number">0</span>
    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">10</span>**<span class="hljs-number">6</span>):
        total += i
    <span class="hljs-keyword">return</span> total

cProfile.run(<span class="hljs-string">'slow_function()'</span>)
</code></pre>
<p>👀 <strong>What this tells you:</strong></p>
<ul>
<li><p>Which function takes the longest</p>
</li>
<li><p>How many times is a function being called</p>
</li>
<li><p>Where is the actual bottleneck</p>
</li>
</ul>
<p>✅ <strong>Takeaway:</strong> Before optimizing, always profile your code. You can’t fix what you don’t measure.</p>
<p>Other useful tools:</p>
<ul>
<li><p><strong>Python:</strong> <code>cProfile</code>, <code>line_profiler</code></p>
</li>
<li><p><strong>JavaScript:</strong> Chrome DevTools Performance Tab</p>
</li>
<li><p><strong>Java:</strong> JProfiler</p>
</li>
<li><p><strong>General:</strong> <code>perf</code>, <code>Valgrind</code></p>
</li>
</ul>
<h3 id="heading-2-they-avoid-premature-optimization"><strong>2. They Avoid Premature Optimization</strong></h3>
<p>🔥 Beginners: “I’ll spend hours optimizing this loop before testing it.”<br>💡 Pros: “I’ll make it work first, then optimize only what matters.”</p>
<p>Donald Knuth famously said, <em>"Premature optimization is the root of all evil."</em> Many beginners waste time optimizing things that aren’t actually slow.</p>
<p><strong>Example:</strong> A beginner might spend hours optimizing a loop that runs in 0.001 seconds, while the real slowdown is an extra database query that takes 500ms.</p>
<p>✅ <strong>Takeaway:</strong></p>
<ul>
<li><p>First, make your code work.</p>
</li>
<li><p>Then, profile and optimize only what’s slow.</p>
</li>
</ul>
<h3 id="heading-3-they-pick-the-right-data-structures-not-just-whats-familiar"><strong>3. They Pick the Right Data Structures (Not Just What’s Familiar)</strong></h3>
<p>🔥 Beginners: “I’ll just use a list.”<br>💡 Pros: “Which data structure is optimal for this task?”</p>
<p>Most slowdowns happen because of bad data structure choices. Pro developers pick the right tool instead of just going with the default.</p>
<p><strong>Example: Fast lookups</strong><br>❌ <strong>Slow (List - O(n))</strong></p>
<pre><code class="lang-javascript">pythonCopyEditusers = [<span class="hljs-string">"alice"</span>, <span class="hljs-string">"bob"</span>, <span class="hljs-string">"charlie"</span>]
<span class="hljs-keyword">if</span> <span class="hljs-string">"bob"</span> <span class="hljs-keyword">in</span> users:  # Searches the entire list
    print(<span class="hljs-string">"Found"</span>)
</code></pre>
<p>✅ <strong>Fast (Set - O(1))</strong></p>
<pre><code class="lang-javascript">pythonCopyEditusers = {<span class="hljs-string">"alice"</span>, <span class="hljs-string">"bob"</span>, <span class="hljs-string">"charlie"</span>}
<span class="hljs-keyword">if</span> <span class="hljs-string">"bob"</span> <span class="hljs-keyword">in</span> users:  # Uses a hash table <span class="hljs-keyword">for</span> instant lookup
    print(<span class="hljs-string">"Found"</span>)
</code></pre>
<p>✅ <strong>Takeaway:</strong> When performance matters, choose the right data structure, not just the most familiar one.</p>
<h3 id="heading-4-they-automate-performance-checks"><strong>4. They Automate Performance Checks</strong></h3>
<p>🔥 Beginners: “I’ll check for performance issues when I feel like it.”<br>💡 Pros: “I’ll use tools to automatically catch performance bottlenecks.”</p>
<p>Instead of manually looking for slow code, pro developers rely on automated tools that flag inefficiencies.</p>
<p><strong>Example:</strong></p>
<ul>
<li><p><strong>Python:</strong> <code>py-spy</code> (lightweight sampling profiler)</p>
</li>
<li><p><strong>JavaScript:</strong> Chrome DevTools Performance Monitoring</p>
</li>
<li><p><strong>Java:</strong> JMH (Java Microbenchmark Harness)</p>
</li>
<li><p><strong>AI-assisted code reviews:</strong> There are tools like <a target="_blank" href="http://codeant.ai">CodeAnt</a> that analyze and auto fix your code automatically when you push on GitHub(or anywhere) and suggest performance improvements.</p>
</li>
</ul>
<p>✅ <strong>Takeaway:</strong> Set up automated checks so you catch performance issues early—before they hit production.</p>
<h3 id="heading-5-they-think-about-performance-from-day-one"><strong>5. They Think About Performance From Day One</strong></h3>
<p>🔥 Beginners: “I’ll optimize later.”<br>💡 Pros: “I’ll write efficient code from the start.”</p>
<p>While premature optimization is bad, writing slow code from the start is worse. Pro developers avoid common pitfalls before they become real problems.</p>
<p><strong>Example: Writing efficient loops from the start</strong><br>❌ <strong>Slow (Unnecessary</strong> <code>.append()</code>)</p>
<pre><code class="lang-javascript">pythonCopyEditresult = []
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">10</span>**<span class="hljs-number">6</span>):
    result.append(i * <span class="hljs-number">2</span>)  # This is slow
</code></pre>
<p>✅ <strong>Fast (List Comprehension - Optimized from the Start)</strong></p>
<pre><code class="lang-javascript">pythonCopyEditresult = [i * <span class="hljs-number">2</span> <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">10</span>**<span class="hljs-number">6</span>)]  # Faster, more efficient
</code></pre>
<p>✅ <strong>Takeaway:</strong> Small choices add up. Think about performance as you write, rather than fixing it later.</p>
<h3 id="heading-final-thoughts-lessons-learned-the-hard-way"><strong>🚀 Final Thoughts: Lessons Learned the Hard Way</strong></h3>
<p>Thanks for reading! These are some of the tips I’ve personally bookmarked for myself—things I’ve learned the hard way while coding, talking to dev friends, and working on real projects.</p>
<p>When I first started, I used to guess why my code was slow instead of measuring. I’d optimize random parts of my code and still wonder why things weren’t getting faster. Over time, I realized that pro developers don’t just “write fast code” by instinct—they use tools, measure, and optimize what actually matters.</p>
<p>I wrote this to save you from making the same mistakes I did. Hopefully, now you have a clearer roadmap to writing faster, more efficient code—without the frustration I went through! 🚀</p>
<p>If you found this helpful, bookmark it for later, and feel free to share it with a fellow dev who might be struggling with slow code too.</p>
<p>Happy coding! 😊</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Python's Built-in Profiling Tools: Examples and Best Practices ]]>
                </title>
                <description>
                    <![CDATA[ Python is known for its simplicity and readability, making it a favorite among developers. But this simplicity sometimes comes at the cost of performance. When your Python application grows or needs to handle larger workloads, understanding what's ha... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-pythons-built-in-profiling-tools-examples-and-best-practices/</link>
                <guid isPermaLink="false">67e2d5a9436e3bed610a8a95</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ performance ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Performance Optimization ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python advanced ]]>
                    </category>
                
                    <category>
                        <![CDATA[ code analysis ]]>
                    </category>
                
                    <category>
                        <![CDATA[ code profiling ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Vivek Sahu ]]>
                </dc:creator>
                <pubDate>Tue, 25 Mar 2025 16:11:21 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742917060232/7ea623ac-4c4d-4bb9-9edf-f9041a8bc9ae.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p><code>Python</code> is known for its simplicity and readability, making it a favorite among developers. But this simplicity sometimes comes at the cost of performance. When your Python application grows or needs to handle larger workloads, understanding what's happening under the hood becomes crucial.</p>
<p>While many developers reach for third-party profiling tools, Python's standard library already comes packed with powerful profiling capabilities that are often overlooked or underutilized.</p>
<p>In this article, you'll learn how to use these built-in profiling tools beyond their basic usage. You'll discover how to combine and leverage them to gain deep insights into your code's performance without installing additional packages.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">Prerequi</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">site</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">s</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">The Bui</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-built-in-profiling-arsenal">l</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">t-in</a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">Profiling A</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-built-in-profiling-arsenal">r</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">senal</a></p>
<ul>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">The</a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-built-in-profiling-arsenal"><code>timeit</code> M</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">odule</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">The</a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-built-in-profiling-arsenal"></a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites"><code>cProfile</code></a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">Module</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">T</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-timeit-module">h</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">e <code>pstats</code> M</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">odule</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">The</a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-built-in-profiling-arsenal"></a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites"><code>profile</code></a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">Module</a></p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-built-in-profiling-arsenal">P</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">ractical Ex</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">periments</a></p>
<ul>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">Setup</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">Exp</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">eriment 1: Basic vs A</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">dvanced <code>timeit</code></a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-timeit-module">Usage</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-built-in-profiling-arsenal">Experiment 2</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">: Effective</a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-timeit-module"><code>c</code></a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites"><code>Profile</code> Analy</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-timeit-module">sis</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-cprofile-module">Experiment</a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-built-in-profiling-arsenal">3</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">: Combining</a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-built-in-profiling-arsenal">T</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">ools for Real</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-timeit-module">-world</a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-profile-module">P</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-cprofile-module">rofiling</a></p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-built-in-profiling-arsenal">Best Practic</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">es</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">Conclu</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-timeit-module">s</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">ion</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">References</a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-cprofile-module"></a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">a</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">nd Further R</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-built-in-profiling-arsenal">e</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">ading</a></p>
</li>
</ul>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>Before diving into the profiling techniques, make sure you have:</p>
<ol>
<li><p><strong>Python 3.6+</strong>: All examples in this article are compatible with Python 3.6 and newer versions.</p>
</li>
<li><p><strong>Basic Python Knowledge</strong>: You should be comfortable with Python fundamentals like functions, modules, and basic data structures.</p>
</li>
<li><p><strong>A Test Environment</strong>: Either a local Python environment or a virtual environment where you can run the code examples.</p>
</li>
</ol>
<p>No external libraries are required for this tutorial as we'll be focusing exclusively on Python's built-in profiling tools. You can verify your Python version this way:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Verify your Python version</span>
<span class="hljs-keyword">import</span> sys
print(<span class="hljs-string">f"Python version: <span class="hljs-subst">{sys.version}</span>"</span>)
</code></pre>
<h2 id="heading-the-built-in-profiling-arsenal"><strong>The Built-in Profiling Arsenal</strong></h2>
<p>Python ships with several profiling tools in its standard library. Let's explore each one and understand their various strengths.</p>
<h3 id="heading-the-timeit-module"><strong>The</strong> <code>timeit</code> Module</h3>
<p>Most Python developers are familiar with the basic <code>timeit</code> usage:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> timeit

<span class="hljs-comment"># Basic usage</span>
execution_time = timeit.timeit(<span class="hljs-string">'"-".join(str(n) for n in range(100))'</span>, number=<span class="hljs-number">1000</span>)
print(<span class="hljs-string">f"Execution time: <span class="hljs-subst">{execution_time}</span> seconds"</span>)

<span class="hljs-comment"># Sample output:</span>
<span class="hljs-comment"># Execution time: 0.006027 seconds</span>
</code></pre>
<p>This basic example measures how long it takes to join 100 numbers into a string with hyphens. The <code>number=1000</code> parameter tells Python to run this operation 1,000 times and return the total execution time, which helps average out any random fluctuations.</p>
<p>However, <code>timeit</code> offers much more flexibility than most developers realize. Let's explore some powerful ways to use it:</p>
<p><strong>Setup Code Separation</strong>:</p>
<pre><code class="lang-python">setup_code = <span class="hljs-string">"""
data = [i for i in range(1000)]
"""</span>

test_code = <span class="hljs-string">"""
result = [x * 2 for x in data]
"""</span>

execution_time = timeit.timeit(stmt=test_code, setup=setup_code, number=<span class="hljs-number">100</span>)
print(<span class="hljs-string">f"Execution time: <span class="hljs-subst">{execution_time}</span> seconds"</span>)

<span class="hljs-comment"># Sample output:</span>
<span class="hljs-comment"># Execution time: 0.001420 seconds</span>
</code></pre>
<p>In this example, we separate the setup code from the code being timed. This is extremely useful when:</p>
<ul>
<li><p>You need to create test data but don't want that time included in your measurement</p>
</li>
<li><p>You're timing a function that relies on imports or variable definitions</p>
</li>
<li><p>You want to reuse the same setup for multiple timing tests</p>
</li>
</ul>
<p>The advantage is that only the code in <code>test_code</code> is timed, while the setup runs just once before the timing begins.</p>
<p><strong>Comparing Functions</strong>:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">approach_1</span>(<span class="hljs-params">data</span>):</span>
    <span class="hljs-keyword">return</span> [x * <span class="hljs-number">2</span> <span class="hljs-keyword">for</span> x <span class="hljs-keyword">in</span> data]

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">approach_2</span>(<span class="hljs-params">data</span>):</span>
    <span class="hljs-keyword">return</span> list(map(<span class="hljs-keyword">lambda</span> x: x * <span class="hljs-number">2</span>, data))

data = list(range(<span class="hljs-number">1000</span>))

time1 = timeit.timeit(<span class="hljs-keyword">lambda</span>: approach_1(data), number=<span class="hljs-number">100</span>)
time2 = timeit.timeit(<span class="hljs-keyword">lambda</span>: approach_2(data), number=<span class="hljs-number">100</span>)

print(<span class="hljs-string">f"Approach 1: <span class="hljs-subst">{time1}</span> seconds"</span>)
print(<span class="hljs-string">f"Approach 2: <span class="hljs-subst">{time2}</span> seconds"</span>)
print(<span class="hljs-string">f"Ratio: <span class="hljs-subst">{time2/time1:<span class="hljs-number">.2</span>f}</span>x"</span>)

<span class="hljs-comment"># Sample output:</span>
<span class="hljs-comment"># Approach 1: 0.001406 seconds</span>
<span class="hljs-comment"># Approach 2: 0.003049 seconds</span>
<span class="hljs-comment"># Ratio: 2.17x</span>
</code></pre>
<p>This example demonstrates how to compare two different implementations of the same functionality. Here we're comparing:</p>
<ol>
<li><p>A list comprehension approach</p>
</li>
<li><p>A <code>map()</code> with lambda approach</p>
</li>
</ol>
<p>By using lambda functions, we can pass existing data to our functions when timing them. This directly measures real-world scenarios where your functions are working with existing data. The ratio calculation makes it easy to understand exactly how much faster one approach is than the other.</p>
<p>In this case, we can see the list comprehension is about 2.17 times faster than the map approach for this specific operation.</p>
<h3 id="heading-the-cprofile-module"><strong>The</strong> <code>cProfile</code> Module</h3>
<p><code>cProfile</code> is Python's C-based profiler that provides detailed statistics about function calls. Many developers use it with its default settings:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> cProfile

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">my_function</span>():</span>
    total = <span class="hljs-number">0</span>
    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">100000</span>):  <span class="hljs-comment"># Reduced for faster execution</span>
        total += i
    <span class="hljs-keyword">return</span> total

cProfile.run(<span class="hljs-string">'my_function()'</span>)

<span class="hljs-comment"># Sample output:</span>
<span class="hljs-comment">#          4 function calls in 0.002 seconds</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#    Ordered by: standard name</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)</span>
<span class="hljs-comment">#         1    0.000    0.000    0.002    0.002 &lt;string&gt;:1(&lt;module&gt;)</span>
<span class="hljs-comment">#         1    0.002    0.002    0.002    0.002 &lt;stdin&gt;:1(my_function)</span>
<span class="hljs-comment">#         1    0.000    0.000    0.002    0.002 {built-in method builtins.exec}</span>
<span class="hljs-comment">#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}</span>
</code></pre>
<p>This basic example runs the profiler on a simple function that sums the numbers from 0 to 99,999. The output provides several key pieces of information:</p>
<ul>
<li><p><code>ncalls</code>: How many times each function was called</p>
</li>
<li><p><code>tottime</code>: The total time spent in the function (excluding time in subfunctions)</p>
</li>
<li><p><code>percall</code>: Time per call (<code>tottime</code> divided by <code>ncalls</code>)</p>
</li>
<li><p><code>cumtime</code>: Cumulative time spent in this function and all subfunctions</p>
</li>
<li><p><code>filename:lineno(function)</code>: Where the function is defined</p>
</li>
</ul>
<p>This gives you a comprehensive view of where time is being spent in your code, but there's much more you can do with <code>cProfile</code>.</p>
<p>The real power comes from advanced usage techniques:</p>
<p><strong>Sorting Results</strong>:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> cProfile
<span class="hljs-keyword">import</span> pstats

<span class="hljs-comment"># Profile the function</span>
profiler = cProfile.Profile()
profiler.enable()
my_function()
profiler.disable()

<span class="hljs-comment"># Create stats object</span>
stats = pstats.Stats(profiler)

<span class="hljs-comment"># Sort by different metrics</span>
stats.sort_stats(<span class="hljs-string">'cumulative'</span>).print_stats(<span class="hljs-number">10</span>)  <span class="hljs-comment"># Top 10 functions by cumulative time</span>
stats.sort_stats(<span class="hljs-string">'calls'</span>).print_stats(<span class="hljs-number">10</span>)       <span class="hljs-comment"># Top 10 functions by call count</span>
stats.sort_stats(<span class="hljs-string">'time'</span>).print_stats(<span class="hljs-number">10</span>)        <span class="hljs-comment"># Top 10 functions by time</span>

<span class="hljs-comment"># Sample output for cumulative sorting:</span>
<span class="hljs-comment">#          2 function calls in 0.002 seconds</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#    Ordered by: cumulative time</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)</span>
<span class="hljs-comment">#         1    0.002    0.002    0.002    0.002 &lt;stdin&gt;:1(my_function)</span>
<span class="hljs-comment">#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}</span>
</code></pre>
<p>This example demonstrates how to control the profiling process and sort the results in different ways. The advantages are:</p>
<ol>
<li><p>You can enable/disable profiling around specific sections of code</p>
</li>
<li><p>You can sort results by different metrics to identify different types of bottlenecks:</p>
<ul>
<li><p><code>cumulative</code>: Find functions that consume the most time overall (including subfunctions)</p>
</li>
<li><p><code>calls</code>: Find functions called most frequently</p>
</li>
<li><p><code>time</code>: Find functions with the highest self-time (excluding subfunctions)</p>
</li>
</ul>
</li>
<li><p>Limit output to only the top N results with <code>print_stats(N)</code></p>
</li>
</ol>
<p>This flexibility lets you focus on specific performance aspects of your code.</p>
<p><strong>Filtering Results</strong>:</p>
<pre><code class="lang-python">stats.strip_dirs().print_stats()  <span class="hljs-comment"># Remove directory paths for cleaner output</span>
stats.print_stats(<span class="hljs-string">'my_module'</span>)   <span class="hljs-comment"># Only show results from my_module</span>

<span class="hljs-comment"># Sample output with strip_dirs():</span>
<span class="hljs-comment">#          2 function calls in 0.002 seconds</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#    Random listing order was used</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)</span>
<span class="hljs-comment">#         1    0.002    0.002    0.002    0.002 &lt;stdin&gt;:1(my_function)</span>
<span class="hljs-comment">#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}</span>
</code></pre>
<p>These filtering techniques are invaluable when working with larger applications:</p>
<ul>
<li><p><code>strip_dirs()</code> removes directory paths, making the output much more readable</p>
</li>
<li><p><code>print_stats('my_module')</code> filters results to only show functions from a specific module, letting you focus on your code rather than library code</p>
</li>
</ul>
<p>This is particularly useful when profiling large applications where the full output might include hundreds or thousands of function calls.</p>
<h3 id="heading-the-pstats-module"><strong>The</strong> <code>pstats</code> Module</h3>
<p>The <code>pstats</code> module is often overlooked but provides powerful ways to analyze profiling data:</p>
<p><strong>Saving and Loading Profile Data</strong>:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> cProfile
<span class="hljs-keyword">import</span> pstats

<span class="hljs-comment"># Save profile data to a file</span>
cProfile.run(<span class="hljs-string">'my_function()'</span>, <span class="hljs-string">'my_profile.stats'</span>)

<span class="hljs-comment"># Load and analyze later</span>
stats = pstats.Stats(<span class="hljs-string">'my_profile.stats'</span>)
stats.strip_dirs().sort_stats(<span class="hljs-string">'cumulative'</span>).print_stats(<span class="hljs-number">10</span>)

<span class="hljs-comment"># Sample output:</span>
<span class="hljs-comment"># Wed Mar 20 14:30:00 2024    my_profile.stats</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#          4 function calls in 0.002 seconds</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#    Ordered by: cumulative time</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)</span>
<span class="hljs-comment">#         1    0.000    0.000    0.002    0.002 {built-in method builtins.exec}</span>
<span class="hljs-comment">#         1    0.000    0.000    0.002    0.002 &lt;string&gt;:1(&lt;module&gt;)</span>
<span class="hljs-comment">#         1    0.002    0.002    0.002    0.002 &lt;stdin&gt;:1(my_function)</span>
<span class="hljs-comment">#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}</span>
</code></pre>
<p>This example shows how to save profiling data to a file and load it later for analysis. The key advantages are:</p>
<ol>
<li><p>You can collect profiling data in one session or environment and analyze it in another</p>
</li>
<li><p>You can share profiling data with team members without them needing to run the code</p>
</li>
<li><p>You can save profiling data from production environments where interactive analysis might not be possible</p>
</li>
<li><p>You can compare different runs over time to track performance improvements</p>
</li>
</ol>
<p>This approach separates data collection from analysis, making it more flexible for real-world applications.</p>
<p><strong>Combining Multiple Profiles</strong>:</p>
<pre><code class="lang-python">stats = pstats.Stats(<span class="hljs-string">'profile1.stats'</span>)
stats.add(<span class="hljs-string">'profile2.stats'</span>)
stats.add(<span class="hljs-string">'profile3.stats'</span>)
stats.sort_stats(<span class="hljs-string">'time'</span>).print_stats()

<span class="hljs-comment"># This allows you to combine results from multiple profiling runs,</span>
<span class="hljs-comment"># useful for aggregating data from different test cases or scenarios</span>
</code></pre>
<p>This powerful feature lets you combine results from multiple profiling runs. This is useful for:</p>
<ol>
<li><p>Comparing performance across different inputs</p>
</li>
<li><p>Aggregating data from multiple test scenarios</p>
</li>
<li><p>Combining data from different parts of your application</p>
</li>
<li><p>Building a more comprehensive performance picture across multiple runs</p>
</li>
</ol>
<p>By combining stats from multiple runs, you can identify patterns that might not be apparent from a single profiling session.</p>
<h3 id="heading-the-profile-module"><strong>The</strong> <code>profile</code> Module</h3>
<p>The <code>profile</code> module is a pure Python implementation of the profiler interface. While it's slower than <code>cProfile</code>, it can be more flexible for specific cases:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> profile

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">my_function</span>():</span>
    total = <span class="hljs-number">0</span>
    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">100000</span>):  <span class="hljs-comment"># Using 100000 for faster execution</span>
        total += i
    <span class="hljs-keyword">return</span> total

profile.run(<span class="hljs-string">'my_function()'</span>)

<span class="hljs-comment"># Sample output:</span>
<span class="hljs-comment">#          5 function calls in 0.011 seconds</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#    Ordered by: standard name</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)</span>
<span class="hljs-comment">#         1    0.000    0.000    0.002    0.002 :0(exec)</span>
<span class="hljs-comment">#         1    0.009    0.009    0.009    0.009 :0(setprofile)</span>
<span class="hljs-comment">#         1    0.000    0.000    0.002    0.002 &lt;string&gt;:1(&lt;module&gt;)</span>
<span class="hljs-comment">#         1    0.000    0.000    0.011    0.011 profile:0(my_function())</span>
<span class="hljs-comment">#         0    0.000             0.000          profile:0(profiler)</span>
<span class="hljs-comment">#         1    0.002    0.002    0.002    0.002 &lt;stdin&gt;:1(my_function)</span>
</code></pre>
<p>The <code>profile</code> module works similarly to <code>cProfile</code> but offers advantages in specific scenarios:</p>
<ol>
<li><p>It's implemented in pure Python, making it easier to modify if you need custom profiling behavior</p>
</li>
<li><p>You can subclass and extend it to implement custom profiling logic</p>
</li>
<li><p>It allows more fine-grained control over the profiling process</p>
</li>
<li><p>It's useful for profiling in environments where the C extension might not be available</p>
</li>
</ol>
<p>While it's slower than <code>cProfile</code> (because it's implemented in Python rather than C), its flexibility makes it valuable for specialized profiling needs.</p>
<p>The <code>profile</code> module follows the same API as <code>cProfile</code>, so you can use all the same techniques for analyzing results.</p>
<h2 id="heading-practical-experiments"><strong>Practical Experiments</strong></h2>
<p>Let's put these tools to practical use with some experiments.</p>
<h3 id="heading-setup"><strong>Setup</strong></h3>
<p>First, create a simple Python module with various functions to profile:</p>
<pre><code class="lang-python"><span class="hljs-comment"># profiling_example.py</span>

<span class="hljs-keyword">import</span> time
<span class="hljs-keyword">import</span> random

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_data</span>(<span class="hljs-params">data</span>):</span>
    result = []
    <span class="hljs-keyword">for</span> item <span class="hljs-keyword">in</span> data:
        result.append(process_item(item))
    <span class="hljs-keyword">return</span> result

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_item</span>(<span class="hljs-params">item</span>):</span>
    <span class="hljs-comment"># Simulate processing time</span>
    time.sleep(<span class="hljs-number">0.0001</span>)  <span class="hljs-comment"># Small delay for demonstration purposes</span>
    <span class="hljs-keyword">return</span> item * <span class="hljs-number">2</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">generate_data</span>(<span class="hljs-params">size</span>):</span>
    <span class="hljs-keyword">return</span> [random.randint(<span class="hljs-number">1</span>, <span class="hljs-number">100</span>) <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> range(size)]

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_data_optimized</span>(<span class="hljs-params">data</span>):</span>
    <span class="hljs-keyword">return</span> [process_item(item) <span class="hljs-keyword">for</span> item <span class="hljs-keyword">in</span> data]

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    data = generate_data(<span class="hljs-number">50</span>)
    result1 = process_data(data)
    result2 = process_data_optimized(data)
    <span class="hljs-keyword">assert</span> result1 == result2
    <span class="hljs-keyword">return</span> result1

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    main()
</code></pre>
<h3 id="heading-experiment-1-basic-vs-advanced-timeit-usage"><strong>Experiment 1: Basic vs Advanced</strong> <code>timeit</code> Usage</h3>
<p>Let's compare different ways of timing our functions:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> timeit
<span class="hljs-keyword">from</span> profiling_example <span class="hljs-keyword">import</span> generate_data, process_data, process_data_optimized

<span class="hljs-comment"># Method 1: Basic string evaluation (limited but simple)</span>
setup1 = <span class="hljs-string">"""
from profiling_example import generate_data, process_data
data = generate_data(5)  # Using a small size for demonstration
"""</span>
basic_time = timeit.timeit(<span class="hljs-string">'process_data(data)'</span>, setup=setup1, number=<span class="hljs-number">5</span>)
print(<span class="hljs-string">f"Basic timing: <span class="hljs-subst">{basic_time:<span class="hljs-number">.4</span>f}</span> seconds"</span>)

<span class="hljs-comment"># Method 2: Using lambda for better control</span>
data = generate_data(<span class="hljs-number">5</span>)
advanced_time = timeit.timeit(<span class="hljs-keyword">lambda</span>: process_data(data), number=<span class="hljs-number">5</span>)
print(<span class="hljs-string">f"Advanced timing: <span class="hljs-subst">{advanced_time:<span class="hljs-number">.4</span>f}</span> seconds"</span>)

<span class="hljs-comment"># Method 3: Comparing implementations</span>
data = generate_data(<span class="hljs-number">5</span>)
original_time = timeit.timeit(<span class="hljs-keyword">lambda</span>: process_data(data), number=<span class="hljs-number">5</span>)
optimized_time = timeit.timeit(<span class="hljs-keyword">lambda</span>: process_data_optimized(data), number=<span class="hljs-number">5</span>)
print(<span class="hljs-string">f"Original implementation: <span class="hljs-subst">{original_time:<span class="hljs-number">.4</span>f}</span> seconds"</span>)
print(<span class="hljs-string">f"Optimized implementation: <span class="hljs-subst">{optimized_time:<span class="hljs-number">.4</span>f}</span> seconds"</span>)
print(<span class="hljs-string">f"Improvement ratio: <span class="hljs-subst">{original_time/optimized_time:<span class="hljs-number">.2</span>f}</span>x"</span>)

<span class="hljs-comment"># Sample output:</span>
<span class="hljs-comment"># Basic timing: 0.0032 seconds</span>
<span class="hljs-comment"># Advanced timing: 0.0034 seconds</span>
<span class="hljs-comment"># Original implementation: 0.0033 seconds</span>
<span class="hljs-comment"># Optimized implementation: 0.0034 seconds</span>
<span class="hljs-comment"># Improvement ratio: 0.98x</span>
</code></pre>
<p>This experiment demonstrates three different approaches to timing code with the <code>timeit</code> module:</p>
<p><strong>Method 1: Basic string evaluation</strong> – This approach evaluates a string of code after running the setup code. The advantages include:</p>
<ul>
<li><p>Simple syntax for basic timing needs</p>
</li>
<li><p>Setup code runs only once, not during each timing run</p>
</li>
<li><p>Good for timing simple expressions</p>
</li>
</ul>
<p><strong>Method 2: Lambda functions</strong> – This more advanced approach uses lambda functions to call our functions directly. Benefits include:</p>
<ul>
<li><p>Direct access to functions and variables in the current scope</p>
</li>
<li><p>No need to import functions in setup code</p>
</li>
<li><p>Better for timing functions that take arguments</p>
</li>
<li><p>More intuitive for complex timing scenarios</p>
</li>
</ul>
<p><strong>Method 3: Implementation comparison</strong> – This practical approach compares two different implementations of the same functionality. This is valuable when:</p>
<ul>
<li><p>Deciding between alternative implementations</p>
</li>
<li><p>Measuring the impact of optimizations</p>
</li>
<li><p>Quantifying performance differences with a ratio</p>
</li>
</ul>
<p>In this example, the list comprehension isn't significantly faster because the dominant cost is the <code>time.sleep()</code> call in both implementations. In real-world cases with actual computation instead of sleep, the difference is often more pronounced.</p>
<h3 id="heading-experiment-2-effective-cprofile-analysis"><strong>Experiment 2: Effective</strong> <code>cProfile</code> Analysis</h3>
<p>Now let's use <code>cProfile</code> to identify bottlenecks:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> cProfile
<span class="hljs-keyword">import</span> pstats
<span class="hljs-keyword">import</span> io
<span class="hljs-keyword">from</span> profiling_example <span class="hljs-keyword">import</span> main

<span class="hljs-comment"># Method 1: Basic profiling</span>
cProfile.run(<span class="hljs-string">'main()'</span>)

<span class="hljs-comment"># Sample output snippet:</span>
<span class="hljs-comment">#         679 function calls in 0.014 seconds</span>
<span class="hljs-comment">#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)</span>
<span class="hljs-comment">#      100    0.000    0.000    0.014    0.000 profiling_example.py:10(process_item)</span>
<span class="hljs-comment">#      100    0.014    0.000    0.014    0.000 {built-in method time.sleep}</span>
<span class="hljs-comment">#        1    0.000    0.000    0.007    0.007 profiling_example.py:4(process_data)</span>
<span class="hljs-comment">#        1    0.000    0.000    0.007    0.007 profiling_example.py:18(process_data_optimized)</span>

<span class="hljs-comment"># Method 2: Capturing and analyzing results</span>
profiler = cProfile.Profile()
profiler.enable()
main()
profiler.disable()

<span class="hljs-comment"># Redirect output to string for analysis</span>
s = io.StringIO()
stats = pstats.Stats(profiler, stream=s).sort_stats(<span class="hljs-string">'cumulative'</span>)
stats.print_stats(<span class="hljs-number">10</span>)  <span class="hljs-comment"># Print top 10 functions by cumulative time</span>
print(s.getvalue())

<span class="hljs-comment"># Method 3: Focus on specific functions</span>
s = io.StringIO()
stats = pstats.Stats(profiler, stream=s).sort_stats(<span class="hljs-string">'cumulative'</span>)
stats.print_callers(<span class="hljs-string">'process_item'</span>)  <span class="hljs-comment"># Show what's calling this function</span>
print(s.getvalue())

<span class="hljs-comment"># Sample output for the callers analysis:</span>
<span class="hljs-comment"># Function was called by...</span>
<span class="hljs-comment">#       ncalls  tottime  cumtime</span>
<span class="hljs-comment"># profiling_example.py:10(process_item)  &lt;-  </span>
<span class="hljs-comment">#     50    0.000    0.007  profiling_example.py:4(process_data)</span>
<span class="hljs-comment">#     50    0.000    0.007  profiling_example.py:18(process_data_optimized)</span>
</code></pre>
<p>This experiment demonstrates three powerful <code>cProfile</code> techniques for identifying bottlenecks:</p>
<p><strong>Method 1: Basic profiling</strong> – Using <code>cProfile.run()</code> to profile a function call provides an immediate overview of performance. This technique:</p>
<ul>
<li><p>Gives you a quick snapshot of all function calls</p>
</li>
<li><p>Shows precisely where time is being spent</p>
</li>
<li><p>Is easy to use with minimal setup</p>
</li>
<li><p>Identifies the most time-consuming operations</p>
</li>
</ul>
<p>In our example, we can immediately see that <code>time.sleep()</code> is consuming most of the execution time.</p>
<p><strong>Method 2: Programmatic profiling and analysis</strong> – This approach gives you more control:</p>
<ul>
<li><p>You can enable/disable profiling for specific sections of code</p>
</li>
<li><p>You can save the results to a variable for further analysis</p>
</li>
<li><p>You can customize how results are sorted and displayed</p>
</li>
<li><p>You can redirect output to a string or file for post-processing</p>
</li>
</ul>
<p>This method is particularly useful for profiling specific parts of a larger application.</p>
<p><strong>Method 3: Caller analysis</strong> – The <code>print_callers()</code> method is extremely valuable because it:</p>
<ul>
<li><p>Shows which functions are calling your bottleneck functions</p>
</li>
<li><p>Helps identify which code paths are contributing to performance issues</p>
</li>
<li><p>Reveals how many times each caller invokes a particular function</p>
</li>
<li><p>Provides context that's crucial for understanding performance patterns</p>
</li>
</ul>
<p>In our example, we can see that both <code>process_data</code> and <code>process_data_optimized</code> are calling <code>process_item</code> 50 times each, confirming they're contributing equally to the bottleneck.</p>
<p>This immediate shows us that <code>process_item</code> is the bottleneck, specifically the <code>time.sleep()</code> call inside it, and it's being called equally by both implementations.</p>
<h3 id="heading-experiment-3-combining-tools-for-real-world-profiling"><strong>Experiment 3: Combining Tools for Real-world Profiling</strong></h3>
<p>In real-world scenarios, combining profiling tools gives the most complete picture:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> cProfile
<span class="hljs-keyword">import</span> pstats
<span class="hljs-keyword">import</span> timeit
<span class="hljs-keyword">from</span> profiling_example <span class="hljs-keyword">import</span> main, process_data, process_data_optimized, generate_data

<span class="hljs-comment"># First, use timeit to get baseline performance of main components</span>
data = generate_data(<span class="hljs-number">50</span>)
time_process = timeit.timeit(<span class="hljs-keyword">lambda</span>: process_data(data), number=<span class="hljs-number">3</span>)
time_process_opt = timeit.timeit(<span class="hljs-keyword">lambda</span>: process_data_optimized(data), number=<span class="hljs-number">3</span>)
print(<span class="hljs-string">f"Process data: <span class="hljs-subst">{time_process:<span class="hljs-number">.4</span>f}</span>s"</span>)
print(<span class="hljs-string">f"Process data optimized: <span class="hljs-subst">{time_process_opt:<span class="hljs-number">.4</span>f}</span>s"</span>)

<span class="hljs-comment"># Sample output:</span>
<span class="hljs-comment"># Process data: 0.0196s</span>
<span class="hljs-comment"># Process data optimized: 0.0194s</span>

<span class="hljs-comment"># Then, use cProfile for deeper insights</span>
profiler = cProfile.Profile()
profiler.enable()
main()
profiler.disable()

<span class="hljs-comment"># Save stats for later analysis</span>
profiler.dump_stats(<span class="hljs-string">'profile_results.stats'</span>)

<span class="hljs-comment"># Load and analyze</span>
stats = pstats.Stats(<span class="hljs-string">'profile_results.stats'</span>)
stats.strip_dirs().sort_stats(<span class="hljs-string">'cumulative'</span>).print_stats(<span class="hljs-number">10</span>)

<span class="hljs-comment"># Sample output:</span>
<span class="hljs-comment"># Wed Mar 20 14:30:00 2024    profile_results.stats</span>
<span class="hljs-comment">#          659 function calls in 0.013 seconds</span>
<span class="hljs-comment">#    Ordered by: cumulative time</span>
<span class="hljs-comment">#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)</span>
<span class="hljs-comment">#         1    0.000    0.000    0.013    0.013 profiling_example.py:21(main)</span>
<span class="hljs-comment">#       100    0.000    0.000    0.013    0.000 profiling_example.py:10(process_item)</span>
<span class="hljs-comment">#       100    0.013    0.000    0.013    0.000 {built-in method time.sleep}</span>
</code></pre>
<p>This experiment demonstrates a comprehensive real-world profiling strategy that combines multiple tools:</p>
<p><strong>First step: High-level timing with</strong> <code>timeit</code> – We start with <code>timeit</code> to:</p>
<ul>
<li><p>Get baseline performance metrics for specific functions</p>
</li>
<li><p>Compare different implementations directly</p>
</li>
<li><p>Measure overall execution time</p>
</li>
<li><p>Identify which high-level components might need optimization</p>
</li>
</ul>
<p>This gives us a quick overview that both implementations take about the same time, confirming our earlier findings.</p>
<p><strong>Second step: Detailed profiling with</strong> <code>cProfile</code> – Next, we use <code>cProfile</code> to:</p>
<ul>
<li><p>Get a function-by-function breakdown of execution time</p>
</li>
<li><p>Identify specific bottlenecks at a granular level</p>
</li>
<li><p>See the number of calls to each function</p>
</li>
<li><p>Understand the call hierarchy</p>
</li>
</ul>
<p><strong>Third step: Saving and analyzing with</strong> <code>pstats</code> – Finally, we:</p>
<ul>
<li><p>Save profiling data to a file for persistence</p>
</li>
<li><p>Load the data and apply filtering/sorting</p>
</li>
<li><p>Focus on the most time-consuming functions</p>
</li>
<li><p>Get a clean, readable output</p>
</li>
</ul>
<p>This multi-tool approach provides several advantages:</p>
<ol>
<li><p>You get both high-level and detailed insights</p>
</li>
<li><p>You can save profiling data for later comparison</p>
</li>
<li><p>You can share results with team members</p>
</li>
<li><p>You can track performance changes over time</p>
</li>
</ol>
<p>In our example, we confirm that our main bottleneck is the <code>time.sleep()</code> call inside <code>process_item</code>, which accounts for most of the execution time. Without this combined approach, we might have missed important details or wasted time optimizing the wrong parts of our code.</p>
<p>This approach gives you both high-level timing information and detailed profiling data, allowing for a comprehensive performance analysis.</p>
<h2 id="heading-best-practices"><strong>Best Practices</strong></h2>
<p>Based on our experiments, here are some best practices for effective profiling:</p>
<ol>
<li><p><strong>Start with the right tool for the job</strong>:</p>
<ul>
<li><p>Use <code>timeit</code> for quick, targeted measurements of specific functions or code blocks</p>
</li>
<li><p>Use <code>cProfile</code> for comprehensive program analysis when you need to understand how all parts of your code interact</p>
</li>
<li><p>Use <code>pstats</code> for in-depth analysis of profiling data when you need to filter, sort, and interpret complex profiling results</p>
</li>
</ul>
</li>
</ol>
<p>    For example, if you're just trying to decide between two implementations of a sorting algorithm, <code>timeit</code> is sufficient. But if you're trying to understand why your entire web application is slow, start with <code>cProfile</code>.</p>
<ol start="2">
<li><p><strong>Profile realistic workloads</strong>:</p>
<ul>
<li><p>Synthetic benchmarks often mislead because they don't reflect real-world usage patterns</p>
</li>
<li><p>Use production-like data sizes to see how your code scales with realistic inputs</p>
</li>
<li><p>Run multiple iterations to account for variance and ensure your results are reliable</p>
</li>
</ul>
</li>
</ol>
<p>    A function that's fast with 10 items might be painfully slow with 10,000. Always test with data sizes that match your production needs.</p>
<ol start="3">
<li><p><strong>Focus on the right metrics</strong>:</p>
<ul>
<li><p><code>cumulative</code> time shows the total time spent in a function and all its calls. It’s useful for finding the overall most expensive operations.</p>
</li>
<li><p><code>tottime</code> shows time spent only in the function itself. It’s useful for finding inefficient implementations.</p>
</li>
<li><p><code>ncalls</code> helps identify functions called excessively. It’s useful for finding redundant operations.</p>
</li>
</ul>
</li>
</ol>
<p>    For example, a function with a small <code>tottime</code> but large <code>cumulative</code> time might be efficient itself but is calling expensive subfunctions.</p>
<ol start="4">
<li><p><strong>Save profiling data for comparison</strong>:</p>
<ul>
<li><p>Use <code>profiler.dump_stats()</code> to save data from different versions of your code</p>
</li>
<li><p>Compare before and after optimization to quantify improvements</p>
</li>
<li><p>Track performance over time to catch regressions early</p>
</li>
</ul>
</li>
</ol>
<p>    This practice helps you prove that your optimizations are actually working and prevents performance from degrading over time.</p>
<ol start="5">
<li><p><strong>Look for the 80/20 rule</strong>:</p>
<ul>
<li><p>80% of time is often spent in 20% of the code. Focus optimization efforts on these "hot spots"</p>
</li>
<li><p>Focus optimization efforts on the functions with the highest cumulative time.</p>
</li>
<li><p>Don't optimize what isn't slow – premature optimization wastes time and can make code more complex.</p>
</li>
</ul>
</li>
</ol>
<p>    For example, in our experiments, the <code>time.sleep()</code> call was the clear bottleneck. Optimizing anything else would be pointless until that's addressed.</p>
<p>By following these practices, you'll make the most efficient use of your profiling tools and focus your optimization efforts where they'll have the greatest impact.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Python's built-in profiling tools offer a powerful arsenal for identifying and resolving performance bottlenecks in your code. By leveraging the <code>timeit</code>, <code>cProfile</code>, and <code>pstats</code> modules effectively, you can get deep insights into your application's performance without relying on third-party tools.</p>
<p>Each tool serves a specific purpose:</p>
<ul>
<li><p><code>timeit</code> helps you measure execution time of specific code snippets</p>
</li>
<li><p><code>cProfile</code> gives you a comprehensive view of function calls and execution time</p>
</li>
<li><p><code>pstats</code> lets you analyze, filter, and interpret profiling data</p>
</li>
<li><p><code>profile</code> provides a customizable profiling interface for special cases</p>
</li>
</ul>
<p>The greatest power comes from combining these tools, as we demonstrated in our practical experiments. This allows you to approach performance optimization systematically:</p>
<ol>
<li><p>Identify high-level performance concerns with <code>timeit</code></p>
</li>
<li><p>Drill down into specific bottlenecks with <code>cProfile</code></p>
</li>
<li><p>Analyze and interpret results with <code>pstats</code></p>
</li>
<li><p>Make targeted optimizations based on data, not guesswork</p>
</li>
</ol>
<p>Remember that profiling is as much an art as it is a science. The goal isn't just to make code faster, but to understand why it's slow in the first place. With the techniques demonstrated in this article, you're well-equipped to tackle performance challenges in your Python applications.</p>
<p>Apply these profiling techniques to your own code, and you'll be surprised at what you discover. Often, the bottlenecks aren't where you expect them to be!</p>
<h2 id="heading-references-and-further-reading"><strong>References and Further Reading</strong></h2>
<ul>
<li><p><a target="_blank" href="https://docs.python.org/3/library/timeit.html">Python <code>timeit</code> documentation</a>: The official documentation for the <code>timeit</code> module, with detailed explanations of all parameters and functions.</p>
</li>
<li><p><a target="_blank" href="https://docs.python.org/3/library/profile.html">Python <code>cProfile</code> documentation</a>: Comprehensive guide to profiling modules, including both <code>cProfile</code> and <code>profile</code>.</p>
</li>
<li><p><a target="_blank" href="https://docs.python.org/3/library/profile.html#pstats.Stats">Python <code>pstats</code> documentation</a>: Detailed reference for the <code>pstats</code> module, explaining all methods for analyzing profiling data.</p>
</li>
<li><p><a target="_blank" href="https://docs.python.org/3/library/profile.html">Python Profilers - Official Documentation</a>: The complete official documentation on Python's profiling capabilities.</p>
</li>
<li><p><a target="_blank" href="https://wiki.python.org/moin/PythonSpeed/PerformanceTips">Python Speed - Performance Tips</a>: A collection of practical tips for optimizing Python code once you've identified bottlenecks.</p>
</li>
<li><p><a target="_blank" href="https://docs.python.org/3/library/profile.html#module-cProfile">The Python Profilers</a>: In-depth explanation of profiling in Python, including details on overhead and accuracy.</p>
</li>
<li><p><a target="_blank" href="https://wewake.dev/posts/practical-experiments-for-django-orm-query-optimizations/">Django Optimization Techniques</a>: Practical advice on optimizing Django ORM code in real-world applications.</p>
</li>
<li><p><a target="_blank" href="https://www.freecodecamp.org/news/python-magic-methods-practical-guide/">How Python Magic Methods Work: A Practical Guide</a>: My previous article on FreeCodeCamp covering practical deep-dive on Python Magic methods.</p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Speed Up Website Loading by Removing Extra Bits and Bytes ]]>
                </title>
                <description>
                    <![CDATA[ Let’s start with an interesting fact: according to research done by Akamai, a 1-second delay in loading a website’s page can decrease the conversion rate by 7%. We are currently living in a fast-paced world, where time is money for everyone. People e... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/speed-up-website-loading/</link>
                <guid isPermaLink="false">67bca6f616cf4617f656a33f</guid>
                
                    <category>
                        <![CDATA[ code optimization ]]>
                    </category>
                
                    <category>
                        <![CDATA[ server ]]>
                    </category>
                
                    <category>
                        <![CDATA[ hosting ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Google PageSpeed ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cloudflare ]]>
                    </category>
                
                    <category>
                        <![CDATA[ performance ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Alex Tray ]]>
                </dc:creator>
                <pubDate>Mon, 24 Feb 2025 17:05:58 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1740094347867/d1097d7b-776f-4228-8088-7726b827271f.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Let’s start with an interesting fact: according to research done by <a target="_blank" href="https://www.akamai.com/newsroom/press-release/akamai-releases-spring-2017-state-of-online-retail-performance-report">Akamai</a>, a 1-second delay in loading a website’s page can decrease the conversion rate by 7%.</p>
<p>We are currently living in a fast-paced world, where time is money for everyone. People expect their favorite websites to load lightning-fast. A slow loading speed will not only make them go to the competitor but will also hurt the <a target="_blank" href="https://www.freecodecamp.org/news/how-to-use-on-page-seo-techniques-to-rank-on-the-first-page/">website's ranking</a> in the SERP.</p>
<p>But the main question is, who’s the culprit? Those extra bits and bytes that almost every site contains. These are unnecessary code files, unoptimized images, and many more. But by following the right approach, you can easily strip away these inefficiencies and achieve excellent loading speed.</p>
<p>In this article, I will be discussing that approach in detail, so stick around with me till the very end.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-why-does-loading-speed-matter">Why Does Loading Speed Matter?</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-google-ranking-factor">Google Ranking Factor</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-impact-on-user-experience">Impact on User Experience</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-negative-brand-perception">Negative Brand Perception</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-retaining-mobile-users">Retaining Mobile Users</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-remove-extra-bits-amp-bytes-from-the-website-different-strategies">How to Remove Extra Bits &amp; Bytes from the Website – Different Strategies</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-perform-code-optimization">Perform Code Optimization</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-image-amp-media-optimization">Image &amp; Media Optimization</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-manage-plugins-amp-scripts">Manage Plugins &amp; Scripts</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-server-amp-hosting-upgrades">Server &amp; Hosting Upgrades</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-tools-that-you-can-use-to-streamline-the-process">Tools That You Can Use to Streamline the Process</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-minifierhttpswwwminifierorg">Minifier</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-tinypnghttpstinypngcom">TinyPNG</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-png-to-webp-converterhttpscloudconvertcompng-to-webp">PNG to WebP Converter</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-google-pagespeed-insighthttpspagespeedwebdev">Google PageSpeed Insight</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-cloudflarehttpswwwcloudflarecom">Cloudflare</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></p>
</li>
</ul>
<h2 id="heading-why-does-loading-speed-matter">Why Does Loading Speed Matter?</h2>
<p>There are several reasons why the loading speed of a website is considered essential. Here are some of the major ones.</p>
<ol>
<li><h3 id="heading-google-ranking-factor">Google Ranking Factor:</h3>
</li>
</ol>
<p>Website loading speed is a confirmed ranking factor. This means that search engines like Google definitely consider the loading time when evaluating a website’s quality. Usually, the <a target="_blank" href="https://sematext.com/glossary/page-load-time/">ideal loading speed</a> is between 0 and 2 seconds. However, 3 seconds is also sometimes acceptable.</p>
<p>In case your site does not fulfill this criteria, then there is a high probability that it may receive a penalty from Google. This will result in lower rankings in the targeted niche – which no webmaster or business wants.</p>
<ol start="2">
<li><h3 id="heading-impact-on-user-experience">Impact on User Experience:</h3>
</li>
</ol>
<p>A slow loading speed is capable of single-handedly destroying the entire user experience. When the website does not load quickly in front of the visitor, they may close it and move on to another site to find the required information, product, or service.</p>
<p>This will decrease the number of user engagements and increase the overall bounce rate of the site. And a high bounce rate increases the chances of facing a penalty from Google.</p>
<ol start="3">
<li><h3 id="heading-negative-brand-perception">Negative Brand Perception:</h3>
</li>
</ol>
<p>For online businesses or brands, their authority and image are everything. When their official site takes too much time to load, it ultimately damages the brand’s perception or credibility in their minds. They will think about how you can deliver a top-notch service or product when you aren’t able to properly manage a website.</p>
<p>This negative impression will not only reduce customer engagement but also conversions.</p>
<ol start="4">
<li><h3 id="heading-retaining-mobile-users">Retaining Mobile Users:</h3>
</li>
</ol>
<p>Mobile contributes to <a target="_blank" href="https://www.mobiloud.com/blog/what-percentage-of-internet-traffic-is-mobile">58% of the global internet traffic</a>. It is also true mobile networks often have slow internet speed issues as compared to Wi-Fi. This can be especially true for people living in rural areas. So, that’s why you should always prioritize loading speed to efficiently retain mobile users.</p>
<h2 id="heading-how-to-remove-extra-bits-amp-bytes-from-the-website-different-strategies">How to Remove Extra Bits &amp; Bytes from the Website – Different Strategies</h2>
<p>Here are some of the most proven strategies you can utilize to remove extra bits and bytes from your websites.</p>
<ol>
<li><h3 id="heading-perform-code-optimization">Perform Code Optimization:</h3>
</li>
</ol>
<p>Excessive HTML, CSS, and JavaScript can greatly slow down a website. Due to the large code file, the host server will have to transfer more packets to the client browser, ultimately resulting in slow loading.</p>
<p>To resolve this issue, it is always recommended to perform code optimization. The most widely known and used technique for this purpose is minification. It refers to the process of removing all the:</p>
<ul>
<li><p>Unnecessary characters</p>
</li>
<li><p>White spaces</p>
</li>
<li><p>Line breaks</p>
</li>
<li><p>Comments</p>
</li>
<li><p>Unused elements.</p>
</li>
</ul>
<p>But you’ll want to make sure that the code works as before, even after minification.</p>
<p>Optimizing code boosts application performance by reducing execution time and resource consumption. Refactor inefficient loops, minimize database queries, and leverage caching to enhance speed. You can use profiling tools to identify bottlenecks and streamline functions for smoother, faster performance.</p>
<p>To demonstrate better, below I have discussed an example:</p>
<p><strong>Unoptimized JavaScript Code:</strong></p>
<pre><code class="lang-javascript">greet(name) {
    <span class="hljs-keyword">if</span> (!name) {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Hello, Guest!"</span>);
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Hello, "</span> + name + <span class="hljs-string">"!"</span>);
    }
}
greet(<span class="hljs-string">"John"</span>);
</code></pre>
<p><strong>Minified Version:</strong></p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">greet</span>(<span class="hljs-params">n</span>)</span>{<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Hello, "</span>+(n||<span class="hljs-string">"Guest"</span>)+<span class="hljs-string">"!"</span>)}greet(<span class="hljs-string">"John"</span>);
</code></pre>
<p>As you can see, I created the minified version by removing all the line breaks and whitespaces. Apart from this, I used shortened variables, like “<strong>n</strong>” instead of “<strong>Name</strong>.” Finally, I also replaced the If Else statement with a shorter n || "Guest" expression.</p>
<p>This is how you can easily condense the entire HTML, CSS, and JavaScript code of your website, and enhance the overall loading speed.</p>
<p>Just keep in mind that there are multiple downsides of code minification. For instance, it significantly impacts code readability and can cause challenges in debugging and maintenance. So use this approach judiciously.</p>
<ol start="2">
<li><h3 id="heading-image-amp-media-optimization">Image &amp; Media Optimization:</h3>
</li>
</ol>
<p>Apart from code, unoptimized images, <a target="_blank" href="https://logocreator.io/blog/logo-file-formats/">logo files</a> and other media files are often the main culprits behind the slow <a target="_blank" href="https://www.freecodecamp.org/news/developers-guide-to-website-speed-optimization/">loading speed</a> of a website. This means that you also need to optimize them as well. There are numerous things you can do in this regard.</p>
<p>First of all – you should reduce the image size in terms of storage. It is generally recommended that each <a target="_blank" href="https://www.foregroundweb.com/image-size/">picture should be less than 500 KB in size</a>. But note that this size can vary depending on the use case.</p>
<p>It’s also a good idea to choose next-generation picture formats like WebP instead of typical ones like JPEG or PNG. When it comes to video files, it’s also helpful if you go with the embedded ones from platforms like YouTube.</p>
<p><strong>Now, let us explain all this with a proper example (Before &amp; After).</strong></p>
<p>Let’s say that a website uses a 2MB JPEG image for its blog post. Its optimization process will involve the following steps:</p>
<ul>
<li><p>Resize the image first. The recommended dimensions are 1200x800.</p>
</li>
<li><p>Compress the image size using image compression tools (we’ll discuss one such tool later in this article)</p>
</li>
<li><p>Now, convert the JPEG file into WebP format.</p>
</li>
<li><p>Add alternative text before publishing</p>
</li>
</ul>
<p><strong>After optimization:</strong></p>
<ul>
<li><p>The image file size will now be reduced to KBS somewhere around 120Kb.</p>
</li>
<li><p>Your website will experience better loading speed as well as an improved user experience.</p>
</li>
</ul>
<p>One more tip that you can consider is <a target="_blank" href="https://www.freecodecamp.org/news/how-lazy-loading-works-in-web-development/">lazy loading</a>. This means only loading the images and videos when they are about to be consumed.</p>
<p>By taking care of these few things, you can efficiently optimize images and media files to achieve faster loading speeds.</p>
<ol start="3">
<li><h3 id="heading-manage-plugins-amp-scripts">Manage Plugins &amp; Scripts:</h3>
</li>
</ol>
<p>Your website may contain unused plugins and scripts that can cause bloat. So, to remove the extra bits and bytes, it is essential to perform regular check-ins.</p>
<p>First, make sure you deactivate and delete all the plugins that aren’t needed. Then, start exploring more lightweight alternatives for plugins that you are actively using. If you find any, go for them and uninstall the bulky ones to improve performance and enhance security, especially for processes like identity verification. Ensure you’re using the latest, most optimized version..</p>
<p>For example, Revolution Slider is a heavy plugin. It loads large scripts and images on every page, even when not needed. This ultimately affects the overall website speed. Some of its lightweight alternatives that you might consider for this include <a target="_blank" href="https://smartslider3.com/">Smart Slider 3</a>, or any other CSS-based slider.</p>
<p>Next comes script management. Here you should first limit any third-party scripts, such as excessive code tracking, social media widgets, and embedded content. Apart from this, don’t forget to totally disable scripts on the pages where they aren’t required.</p>
<p>One useful example here is Google Analytics which loads tracking scripts on every page, increasing the request time. To fix this issue, you can use <a target="_blank" href="https://tagmanager.google.com/">Google Tag Manager</a> to load the scripts only when they are needed.</p>
<p>Additionally, you can use <a target="_blank" href="https://www.blaze.tech/post/no-code-automation-how-to-streamline-your-business-now">no-code workflow automation tools</a> like Zapier, Make, or Uncanny Automator which help streamline processes by reducing reliance on heavy plugins and scripts.</p>
<ol start="4">
<li><h3 id="heading-server-amp-hosting-upgrades">Server &amp; Hosting Upgrades:</h3>
</li>
</ol>
<p>This is the final strategy that you can consider. Your hosting provider plays a key role in deciding the loading speed of the website. So, it’s a good idea to upgrade your hosting plan and get it from a reputable and credible service.</p>
<p>Also, do not forget to enable server-side compression. Doing so will automatically reduce the file sizes before transmission. Optimizing database performance is equally crucial, as <a target="_blank" href="https://www.liquibase.com/resources/guides/database-observability">database observability enables database pipeline analytics</a>, helping to identify inefficiencies, reduce query execution time, and enhance overall site responsiveness.</p>
<p>Also, take steps to optimize the database queries. You can do this by removing unnecessary data while also caching data mechanisms. There are also specialized plugins available for this like <a target="_blank" href="https://wordpress.org/plugins/wp-optimize/">WP-Optimize</a>. It effectively cleans up all the unnecessary data saving valuable time and effort.</p>
<p>You should also start caching queries. Store all the frequent ones in memory. This will significantly reduce database load.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> products <span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">category</span> = <span class="hljs-string">'Laptops'</span> <span class="hljs-keyword">CACHE</span>;
</code></pre>
<p>This prevents the server from re-executing the same query repeatedly.</p>
<p>So, these are some of the proven strategies you can apply to eliminate additional bits &amp; bytes from the website to achieve faster loading.</p>
<h2 id="heading-tools-that-you-can-use-to-streamline-the-process">Tools That You Can Use to Streamline the Process</h2>
<p>To simplify the process of optimizing website loading speed, you can consider utilizing the following tools.</p>
<ol>
<li><h3 id="heading-minifierhttpswwwminifierorg"><a target="_blank" href="https://www.minifier.org/">Minifier</a></h3>
</li>
</ol>
<p>First of all, we have Minifier, a dedicated tool that is specifically designed to automate the code minification process with a single click. It is available for free and works for HTML, CSS, and JavaScript codes.</p>
<p>Besides this, the tool features a user-intuitive interface so that you can quickly navigate through it. The minifier is trained according to both development and minification to ensure maximum speed and accuracy in the output.</p>
<p>All you need to do is either paste or upload the code file into the tool, hit the “<strong>Minify</strong>” button, and get a condensed version. You can check out the below screenshot to get a better idea how it works.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXcu5gaAosAaUCZQ7oIp3J_m_CIEyAshp2Ob6rmguvQOQvxuuz6rXJ1QdO_FSD_McnO1S-fkqv38cY7B0e4s5xBtjNa78mVns2VZRe3iUemWxR-dKgct9-OJkb6YIO2fTkhB_W3If4DYj6hb2vnzknY?key=W-8S2j9mlTlf7KW39H_m9bHu" alt="Screenshot showing Minify result" width="600" height="400" loading="lazy"></p>
<p>Minify also offers a wide variety of other useful tools you can use if needed. Some notable options include JSON minifier and XML formatter, among others.</p>
<p>So now there is no need to spend time and effort on manually minifying your code for better loading speed. You can just use this tool and get the job done with a single click.</p>
<ol start="2">
<li><h3 id="heading-tinypnghttpstinypngcom"><a target="_blank" href="https://tinypng.com/">TinyPNG</a></h3>
</li>
</ol>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXckb9b-_Pfw-T4icivrTC6g_pnhjpu3BSK0s-7ussuhsRRY22qGNe8DAyUINv8GgGQ5DmY579muEPcGkCjRsbSZofP9XZ1y3xqPBYriFDyh_2vl6yWM4fNYBKaA7k5Jx05pRjjw3ShVU3tT3JjeHwM?key=W-8S2j9mlTlf7KW39H_m9bHu" alt="AD_4nXckb9b-_Pfw-T4icivrTC6g_pnhjpu3BSK0s-7ussuhsRRY22qGNe8DAyUINv8GgGQ5DmY579muEPcGkCjRsbSZofP9XZ1y3xqPBYriFDyh_2vl6yWM4fNYBKaA7k5Jx05pRjjw3ShVU3tT3JjeHwM?key=W-8S2j9mlTlf7KW39H_m9bHu" width="600" height="400" loading="lazy"></p>
<p>Many of you may have heard of or even used this tool. It is an image compression tool that will help you effectively reduce your image sizes for optimization. The good thing is that TinyPNG perfectly preserves the original quality of the picture (in terms of resolution) even after the compression.</p>
<p>All you need to do is upload the required photo from your local storage, and the tool will automatically provide a compressed version. Don’t worry about the file format, as TinyPNG supports JPG, PNG, JPEG, and many more.</p>
<p>The tool even provides the percentage of how much the uploaded image was compressed, like -51%, and so on. It also mentions the size of the compressed photo in terms of KBs. So, in case you are not satisfied with the file size, you can further compress it.</p>
<ol start="3">
<li><h3 id="heading-png-to-webp-converterhttpscloudconvertcompng-to-webp"><a target="_blank" href="https://cloudconvert.com/png-to-webp">PNG to WebP Converter</a>:</h3>
</li>
</ol>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXcSaBUNNRRqk5A4EetHdg1CK1P6F-Ro213s3DnifuZZFF24BNJZHsP_qXjVe1rn72iPH2jZd707JRsOSIUe7PzEAH7jE0ccacHXaEbqJ0YDILtM4K4gF5IYao0wOpJ13jw-xNOzrKaJiRP926kjqQ?key=W-8S2j9mlTlf7KW39H_m9bHu" alt="AD_4nXcSaBUNNRRqk5A4EetHdg1CK1P6F-Ro213s3DnifuZZFF24BNJZHsP_qXjVe1rn72iPH2jZd707JRsOSIUe7PzEAH7jE0ccacHXaEbqJ0YDILtM4K4gF5IYao0wOpJ13jw-xNOzrKaJiRP926kjqQ?key=W-8S2j9mlTlf7KW39H_m9bHu" width="600" height="400" loading="lazy"></p>
<p>As I mentioned earlier, I recommend using next-gen image formats like WebP instead of older formats when possible. Usually, the widely used format is PNG, but to seamlessly convert into WebP, you can use this PNG to WebP converter.</p>
<p>It’s available for free and does not ask for registration/signup. Simply visit the page and start performing conversions. The conversion is performed without causing any damage to the image’s quality and formatting.</p>
<p>The tool also offers many extra features. For instance, you can adjust both the image’s width and height. You can also set image quality (WebP compression level) if required. And it doesn’t stop here – you can even select the right fit for the photo from the following options:</p>
<ul>
<li><p>Max</p>
</li>
<li><p>Crop</p>
</li>
<li><p>Scale</p>
</li>
</ul>
<ol start="4">
<li><h3 id="heading-google-pagespeed-insighthttpspagespeedwebdev"><a target="_blank" href="https://pagespeed.web.dev/">Google PageSpeed Insight</a></h3>
</li>
</ol>
<p>How can you enhance the loading speed of the website when you don’t even know which elements are causing issues? For this purpose, Google PageSpeed Insight is the best solution. It is developed and managed by Google.</p>
<p>The tool effectively crawls the given page link and highlights all the issues that are causing slow loading. It even provides four different scores (0-100) for evaluation. These include:</p>
<ul>
<li><p>Performance</p>
</li>
<li><p>Accessibility</p>
</li>
<li><p>Best Practices</p>
</li>
<li><p>SEO</p>
</li>
</ul>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXet-k73bv-y0HQXbGHfrcntmms8k_nQvcrrADNI3w9cBrFKGv9CAkMSEdOCWHFVyuRQxVXaUseYQxIa_2GA9Hl7TzDGSSO_vZqZliiX32ZNdkvoQZYhCf4i3PyKtGHOzk8pwqZ6O-gZRCwPC3gzBt0?key=W-8S2j9mlTlf7KW39H_m9bHu" alt="AD_4nXet-k73bv-y0HQXbGHfrcntmms8k_nQvcrrADNI3w9cBrFKGv9CAkMSEdOCWHFVyuRQxVXaUseYQxIa_2GA9Hl7TzDGSSO_vZqZliiX32ZNdkvoQZYhCf4i3PyKtGHOzk8pwqZ6O-gZRCwPC3gzBt0?key=W-8S2j9mlTlf7KW39H_m9bHu" width="600" height="400" loading="lazy"></p>
<p>The good thing is that Google PageSpeed Insights evaluates the page for both mobile and desktop users. The results are also provided separately. The areas of improvement are highlighted in red, along with the necessary instructions you can take. The good parts are marked with green.</p>
<p>By utilizing this tool, you can easily evaluate your website and then make efforts to improve the loading speed.</p>
<ol start="5">
<li><h3 id="heading-cloudflarehttpswwwcloudflarecom"><a target="_blank" href="https://www.cloudflare.com/">Cloudflare</a></h3>
</li>
</ol>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXeKEUhCvUoLArPXA_KRMaH4ws28-YXc6OSzP9idKqis14maZynQIrYUoHaJWF1LJ20q7UcFjAhUGc7WRKpk1S37tkaanh5VRqguD2u7ICzp5eFY5e0mMNjZJU_yl-YCm2O1hdaq2gsnwpWJDbPMGGI?key=W-8S2j9mlTlf7KW39H_m9bHu" alt="AD_4nXeKEUhCvUoLArPXA_KRMaH4ws28-YXc6OSzP9idKqis14maZynQIrYUoHaJWF1LJ20q7UcFjAhUGc7WRKpk1S37tkaanh5VRqguD2u7ICzp5eFY5e0mMNjZJU_yl-YCm2O1hdaq2gsnwpWJDbPMGGI?key=W-8S2j9mlTlf7KW39H_m9bHu" width="600" height="400" loading="lazy"></p>
<p>Last but not least, Cloudflare is a good tool that helps enhance the loading speed of a website by using its global content delivery network (CDN). With this feature, it caches static content across different servers worldwide. This ultimately reduces the overall latency and improves loading speed for users in different locations.</p>
<p>Besides this, Cloudflare also offers a bunch of other features. For example, it automatically minifies HTML, CSS, and JavaScript files. It can even compress and convert images into next-gen formats, especially WebP.</p>
<p>It offers a robust DNS resolution that reduces lookup times and helps the page load faster. This feature also protects the site from DDoS attacks.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>If you want to experience higher ranking and increased user engagement, then you need to optimize your website’s loading speed. The extra bits and bytes like code files, media, and so on can cause real hurdles – but don’t worry.</p>
<p>By using these strategies and tools, you’ll be able to speed up page loading in no time. I hope you found this article interesting and valuable.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Skeleton Screens to Improve Perceived Website Performance ]]>
                </title>
                <description>
                    <![CDATA[ When you’re building a website, it’s important to make sure that it’s fast. People have little to no patience for slow-loading websites. So as developers, we need to use all the techniques available to us to speed up our site’s performance. And somet... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-skeleton-screens-to-improve-perceived-website-performance/</link>
                <guid isPermaLink="false">671955939f7c056f46dd8604</guid>
                
                    <category>
                        <![CDATA[ Frontend Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ performance ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Timothy Olanrewaju ]]>
                </dc:creator>
                <pubDate>Wed, 23 Oct 2024 19:59:15 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1729603651952/5f5f3c38-20e0-41ac-a4a0-7190347b3a59.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When you’re building a website, it’s important to make sure that it’s fast. People have little to no patience for slow-loading websites. So as developers, we need to use all the techniques available to us to speed up our site’s performance.</p>
<p>And sometimes, we need to make users think that something is happening when they’re waiting for a page to load so they don’t give up and leave the site.</p>
<p>Fast webpage loading speed is important these days because humans’ attention spans are shrinking. According to <a target="_blank" href="https://www.wellbrookrecovery.com/post/average-attention-span">statistics on the average human attention span</a>, the average page visit lasts less than a minute, with users often leaving web pages in just 10-20 seconds.</p>
<p>This means that we as developers have had to come up with strategies to keep users engaged while waiting for their requested web page content to load. And this led to the concept of the Skeleton Screen.</p>
<p>In this article, we’ll be looking at what skeleton screens are, how effective they are at enhancing the user experience and build a skeleton screen of our own !. </p>
<h2 id="heading-what-is-a-skeleton-screen"><strong>What is a Skeleton Screen?</strong></h2>
<p>A skeleton screen is like a sketch of a webpage that displays before the final page fully loads. It gives you a glimpse of the form and positioning of elements on your screen (like text, images, and buttons) which are represented by a placeholder.</p>
<p>Here is what a YouTube skeleton screen looks like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729602784479/b4ffa403-f642-41f0-911b-897f2eebad2b.png" alt="YouTube skeleton screen example showing placeholders" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>When you visit a website that uses skeleton screens, the skeleton screen appears first while the content is being fetched. When the content finally gets fetched, it gradually replaces the skeleton screen until the screen is fully populated.</p>
<p>That is what brought about the name <em>Skeleton Screen</em> – because the bare bones akin to a skeleton appear first before being fleshed out by real content.</p>
<p>Skeleton screens take the appearance or form of elements they are meant to “stand in place of” – meaning oval-shaped placeholders are replaced by oval-shaped elements on full loading, and so on. </p>
<p>The ultimate goal of the skeletal screen is to make the waiting game less painful by giving users something to focus on. It has nothing to do with actual load time but all to do with providing a distraction so the waiting time feels shorter. It can also reassure users that content is indeed coming. Clever right?</p>
<h2 id="heading-the-psychology-behind-skeleton-screens"><strong>The Psychology Behind Skeleton Screens</strong></h2>
<p>Here is where things get interesting. You might already be wondering what the reasoning behind such an invention was.</p>
<p>Based on what we’ve already discussed, you probably agree that they are all about “Perceived Performance”. It’s less about how long users have to wait and more about how long it <em>feels</em> like they’re waiting.</p>
<p>If you’ve ever been stuck in traffic, you’d know there is a difference in feeling when you’re moving forward versus sitting still. Moving traffic, even if it’s slow, is better than being stuck in a total gridlock.</p>
<p>The same applies to a user who’s visiting a webpage. A visible and engaging placeholder is better than being greeted with a blank screen while waiting for the final content to show.</p>
<p>With skeleton screens, it's like “Hey, here is the form of the page content you’re looking for, but please, exercise some patience while we get you the real thing!”</p>
<p>This fits perfectly into the <strong>Zeigarnik Effect</strong>, a psychological principle suggesting that we remember incomplete tasks better than completed ones. Think of it like leaving a jigsaw puzzle half-finished on your table – your brain stays engaged, eager to see the final picture.</p>
<p>Similarly, when users see a skeleton screen, they remain mentally hooked, anticipating the moment when the content will fully load.</p>
<h2 id="heading-skeleton-screens-vs-spinners-and-progress-bars"><strong>Skeleton Screens vs Spinners and Progress Bars</strong></h2>
<p>Spinners and progress bars might seem like a viable alternative to skeleton screens, but do they have the same effect on users? The answer is – not quite.</p>
<p>With spinners and progress bars, the load time is somewhat undecided, and it’s a bit like watching a clock tick – the time seems to move slower, as focusing on the hands of the clock makes the duration seem longer and more frustrating.</p>
<p>Skeleton screens, on the other hand, add an interesting extra layer of providing a visual cue of expected content rather than just displaying an indicator (which is what spinners and progress bars do).</p>
<p>Interfaces that use skeleton screens make the user scan the screen thinking things like, “That rectangle must be an image or video, and these blocks look like they are for text”. They don’t leave users idle but keep their brains and eyes engaged.</p>
<h2 id="heading-is-a-skeleton-screen-just-a-visual-illusion"><strong>Is a Skeleton Screen Just a Visual Illusion?</strong></h2>
<p>Yes, skeleton screens are a bit of an illusion. They don’t speed up load times – rather, they just make it feel faster.</p>
<p>But here’s the thing: if not done well, this trick can backfire. Users expect that once they see the skeleton screen, the real content should follow quickly. If not frustration creeps in.</p>
<p>Also, adding motion to skeleton screens makes the illusion effect more effective by decreasing the perceived duration time. It is not uncommon to see sliding effects (left to right) and pulse effects (fading opacity – in and out) used in skeleton screens.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729603561030/235b53ed-a75d-4eb0-9126-c63174d0c59f.gif" alt="skeleton screen with motion" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Finally, for best results, skeleton screens should be neutral in color. This is important as it helps to create a smooth and subtle loading experience without distracting or overwhelming users.</p>
<h2 id="heading-how-to-build-a-skeleton-screen-with-react">How to Build a Skeleton Screen with React</h2>
<p>Now that you know what a skeleton screen is all about, let’s build our own using React.</p>
<h3 id="heading-step-1-set-up-your-react-project"><strong>Step 1: Set up Your React Project</strong></h3>
<p>If you’re new to React and wish to follow along, click this <a target="_blank" href="https://vite.dev/guide/">link</a> and follow the steps to create your React project. When you’re done, come back here and let’s continue building.</p>
<p>If you already have a React project you want to use, that’s great, too.</p>
<h3 id="heading-step-2-install-react-loading-skeleton-package"><strong>Step 2: Install</strong> <code>react-loading-skeleton</code> <strong>Package</strong></h3>
<p>Next, we’ll install a package called <strong>react-loading-skeleton</strong> that helps in creating beautiful and animated skeletons. To install this package:</p>
<ul>
<li><p>Navigate to your project on your terminal.</p>
</li>
<li><p>If you’re using yarn, type this command <code>yarn add react-loading-skeleton</code> or <code>npm install react-loading-skeleton</code> for npm users.</p>
</li>
</ul>
<h3 id="heading-step-3-how-to-handle-states-and-skeleton-imports"><strong>Step 3: How to Handle States and Skeleton Imports</strong></h3>
<p>There are variables that will be changing frequently in our project, and they need to be declared. You can read my article on <a target="_blank" href="https://www.freecodecamp.org/news/react-state-management/">state management</a> if you are not familiar with the concept.</p>
<pre><code class="lang-javascript">  <span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
  <span class="hljs-keyword">import</span> Skeleton <span class="hljs-keyword">from</span> <span class="hljs-string">'react-loading-skeleton'</span>;
  <span class="hljs-keyword">import</span> <span class="hljs-string">'react-loading-skeleton/dist/skeleton.css'</span>;

  <span class="hljs-keyword">const</span> SkeletonScreenComponent = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> [data, setData] = useState([]);
    <span class="hljs-keyword">const</span> [loading, setLoading] = useState(<span class="hljs-literal">true</span>);
    <span class="hljs-keyword">const</span> [error, setError] = useState(<span class="hljs-literal">null</span>);
  }
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> SkeletonScreenComponent;
</code></pre>
<p>In this code, we declared three states in our <strong>SkeletonScreenComponent</strong> which are:</p>
<ul>
<li><p><strong>data</strong>: responsible for storing the data fetched from a fake REST API with its initial value set to an empty array.</p>
</li>
<li><p><strong>loading</strong>: to keep track of data loading with its initial value set to a Boolean value of <strong>true</strong>.</p>
</li>
<li><p><strong>error</strong>: to store any error message with initial value set to <strong>null.</strong></p>
</li>
</ul>
<p>We also imported the <code>useState</code> hook for the states together with the <code>Skeleton</code> component and its CSS from the <code>react-loading-skeleton</code> library.</p>
<h3 id="heading-step-4-fetch-data-from-the-fake-api"><strong>Step 4: Fetch Data from the Fake API</strong></h3>
<p>Our little project will be fetching data from <a target="_blank" href="https://jsonplaceholder.typicode.com/posts">https://jsonplaceholder.typicode.com/posts</a>, which is a free online fake REST API.</p>
<pre><code class="lang-javascript">  useEffect(<span class="hljs-function">() =&gt;</span> {
    fetchData();
  }, []);

  <span class="hljs-keyword">const</span> fetchData = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'https://jsonplaceholder.typicode.com/posts'</span>);
        <span class="hljs-keyword">if</span> (!response.ok) {
          <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Network response was not ok'</span>);
        }
        <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> response.json();
        setData(result);
        setLoading(<span class="hljs-literal">false</span>);
    } <span class="hljs-keyword">catch</span> (err) {
      setError(<span class="hljs-string">'Error fetching data'</span>+ err.message);
      setLoading(<span class="hljs-literal">false</span>);
    } 
  };
</code></pre>
<p>In the code block above:</p>
<ul>
<li><p>The <strong>useEffect hook</strong> is responsible for handling side effects. It’s perfect for data fetching purposes, and has its dependency array set to empty (makes it render on mount).</p>
</li>
<li><p><strong>fetchData</strong> is an asynchronous function that fetches data from the <strong>URL</strong>, updates the <strong>data</strong> state, sets <strong>loading</strong> state to false when done, catches any errors, and updates the <strong>error</strong> state.</p>
</li>
</ul>
<h3 id="heading-step-5-conditional-rendering"><strong>Step 5: Conditional Rendering</strong></h3>
<p>The whole idea of this project revolves around the <strong>loading</strong> state. The component renders different content based on the <strong>loading</strong> state.</p>
<p>If <strong>loading</strong> is true:</p>
<ul>
<li><p>An Array is created where each element is a Skeleton component.</p>
</li>
<li><p>The <strong>Skeleton count</strong> is set to 2, for the post title and body. You can set the count according to the number of placeholders you want to display.</p>
</li>
</ul>
<p>If data loading is successful:</p>
<ul>
<li><p>It maps through the <strong>data</strong> array.</p>
</li>
<li><p>It renders each post’s title and body.</p>
</li>
</ul>
<p>If there is an error, an error message is displayed.</p>
<pre><code class="lang-javascript">   <span class="hljs-keyword">if</span> (loading) {
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        {Array.from({ length: 15 },(_, index) =&gt; (
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span>  <span class="hljs-attr">marginTop:</span> '<span class="hljs-attr">30px</span>'  }}&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Skeleton</span> <span class="hljs-attr">count</span>=<span class="hljs-string">{2}</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{marginBottom:</span>"<span class="hljs-attr">5px</span>"}} /&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        ))}
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
  }

  <span class="hljs-keyword">if</span> (error) {
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{error}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
  }
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      {data.map(({id, title, body}) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{id}</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">marginBottom:</span> '<span class="hljs-attr">20px</span>' }}&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>{title}<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{body}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      ))}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
</code></pre>
<h3 id="heading-final-result"><strong>Final Result</strong></h3>
<p>This is what our Skeleton screen looks like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729711380366/974d0de9-faf1-4050-90e7-1c982ac72e67.gif" alt="974d0de9-faf1-4050-90e7-1c982ac72e67" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Skeleton screens are great at creating the illusion of progress and making users feel like the site is loading faster. But they don’t fix slow-loading pages on their own. The trick is combining skeleton screens with performance-enhancing techniques such as lazy loading, image compression, and server-side rendering.</p>
<p>Balancing the actual speed and the user’s perception of it is vital in web performance. Skeleton screens are just one tool in your UX toolbox – but when used correctly, they can help create a web experience that feels fast, reliable, and most importantly engaging. And in the world of web development, where perception is reality, that’s half the battle won.</p>
<p>For more front-end related posts and articles, you can follow me on <a target="_blank" href="https://www.linkedin.com/in/timothy-olanrewaju750/">LinkedIn</a> and <a target="_blank" href="https://x.com/SmoothTee_DC">X</a>.</p>
<p>See you on the next one!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ A How to Start a Career in Site Reliability Engineering – SRE Career Guide ]]>
                </title>
                <description>
                    <![CDATA[ If you're considering a career in the Site Reliability Engineering (SRE) field, you should understand what SREs do, how to get started, and how to grow as an SRE.  In this article, we'll explore what you need to know to be an SRE, and how you can dev... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/start-a-career-in-site-reliability-engineering/</link>
                <guid isPermaLink="false">66baee8ca844cdf0b398b3f1</guid>
                
                    <category>
                        <![CDATA[ automation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ performance ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Site Reliability Engineering ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Engineering ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Iroro Chadere ]]>
                </dc:creator>
                <pubDate>Fri, 05 Apr 2024 18:24:12 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/07/pexels-tauberman-128362.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you're considering a career in the Site Reliability Engineering (SRE) field, you should understand what SREs do, how to get started, and how to grow as an SRE. </p>
<p>In this article, we'll explore what you need to know to be an SRE, and how you can develop your skills to become a successful one.</p>
<h3 id="heading-heres-what-well-cover-in-this-article">Here's what we'll cover in this article:</h3>
<ol>
<li><a class="post-section-overview" href="#heading-introduction-to-site-reliability-engineering-sre">Introduction to Site Reliability Engineering</a></li>
<li><a class="post-section-overview" href="#role-and-responsibilities-of-an-sre-">Role and Responsibilities of an SRE</a></li>
<li><a class="post-section-overview" href="#heading-importance-of-sre-in-modern-tech-organizations">Importance of SRE in Modern Tech Organizations</a></li>
<li><a class="post-section-overview" href="#prerequisites-and-fundamental-knowledge-">Prerequisites and Fundamental Knowledge</a></li>
<li><a class="post-section-overview" href="#essential-skills-for-sre-&quot;">Essential Skills for SRE</a></li>
<li><a class="post-section-overview" href="#learning-path-and-resources-">Learning Path and Resources</a></li>
<li><a class="post-section-overview" href="#how-to-succeed-in-the-sre-field-">How to Succeed in the SRE Field</a></li>
<li><a class="post-section-overview" href="#conclusion-">Conclusion</a></li>
</ol>
<h3 id="heading-before-we-get-started">Before we get started...</h3>
<p>This isn't a course or a complete tutorial on how to master SRE – that is, <strong>it doesn't teach all the nitty-gritty</strong> of SRE. Instead, it's more like a guide that'll walk you through how to become an SRE by providing the needed materials for you to succeed. </p>
<p>To get started with reading this guide, you should have a desire to learn and become an SRE. SRE is a wide field, and I urge you to have a burning zeal to learn and master it. </p>
<p>Last but not least, keep in mind that the linked resources and additional pointers contained in this post are my <strong>personal recommendations</strong> that should help you as you dive into the SRE field. Just make sure you chose the ones that best match your learning style and goals.</p>
<h2 id="heading-introduction-to-site-reliability-engineering-sre">Introduction to Site Reliability Engineering (SRE)</h2>
<p>The concept of <a target="_blank" href="https://youtu.be/1NF6N2RwVoc">Site Reliability Engineering (SRE) originated at Google</a> in the early 2000s, emerging as a novel approach to tackling large-scale system management challenges. </p>
<p>SRE was born from the necessity to ensure the reliability and scalability of rapidly growing online services. And it has since evolved into a critical discipline within the tech industry. </p>
<p>This origin story not only highlights SRE's roots but also its foundational importance in shaping modern operational practices.</p>
<p>In the early days of Google, the explosive growth of its services and the scale at which they operated introduced unprecedented reliability and scalability challenges. </p>
<p>Traditional IT operations approaches were insufficient for the company's needs, prompting a rethink of how to manage large-scale systems efficiently and reliably. Google's innovative solution was to create a new role that blended software engineering with IT operations, thus giving birth to Site Reliability Engineering.</p>
<p>This new breed of engineers was tasked with making Google's already large and complex systems more reliable, efficient, and scalable. They applied software engineering principles and practices to infrastructure and operations problems, automating tasks that were traditionally performed manually. </p>
<p>This approach not only improved system reliability and efficiency but also allowed for scaling operations in a way that could keep up with the company's rapid growth.</p>
<h3 id="heading-definition-and-purpose-of-sre">Definition and Purpose of SRE</h3>
<p><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a4A3Ns3r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uoonngsuoz7pduffn17m.png" alt="An Image showing the conflicts between devs and ops team" width="600" height="400" loading="lazy"></p>
<p>Photo Credit: <a target="_blank" href="https://dev.to/techworld_with_nana/sre-and-tasks-of-an-sre-explained-3ah9"><em>TechWorld with Nana</em></a></p>
<p>After exploring its origins, you can see that SRE is fundamentally about applying a software engineering mindset to help solve operations problems.</p>
<p>At its core, SRE is about engineering resilience into systems and applications. It focuses on the intersection of software engineering and system administration, applying principles of software design to infrastructure and operations problems. </p>
<p>SRE aims to strike a balance between innovation and reliability, enabling organizations to deliver feature-rich products while maintaining high levels of service reliability.</p>
<p>The primary purpose of SRE is to build and maintain highly reliable, scalable, and efficient systems through a combination of software development, automation, and operational best practices. </p>
<p>By adopting a proactive and engineering-driven approach to operations, SRE teams strive to minimize service disruptions, mitigate risks, and optimize system performance.</p>
<h2 id="heading-role-and-responsibilities-of-an-sre">Role and Responsibilities of an SRE</h2>
<p>The role of an SRE is multifaceted, encompassing a wide range of responsibilities across software development, operations, and system architecture. </p>
<p>Some key responsibilities of an SRE include:</p>
<ul>
<li><strong>Service Reliability</strong>: Ensuring the reliability, availability, and performance of critical services and systems.</li>
<li><strong>Automation and Tooling</strong>: Developing automation tools and systems for provisioning, deployment, monitoring, and incident response.</li>
<li><strong>Capacity Planning</strong>: Analyzing resource usage patterns and forecasting capacity requirements to support business growth.</li>
<li><strong>Incident Management</strong>: Responding to and resolving incidents in a timely manner, and conducting post-incident reviews to identify root causes and prevent recurrence.</li>
<li><strong>Performance Optimization</strong>: Identifying and addressing performance bottlenecks to improve system scalability and efficiency.</li>
<li><strong>Security and Compliance</strong>: Implementing security best practices and ensuring compliance with regulatory requirements to protect sensitive data and infrastructure.</li>
<li><strong>Collaboration and Communication</strong>: Working closely with cross-functional teams, including software engineers, product managers, and system administrators, to drive continuous improvement and innovation.</li>
</ul>
<h3 id="heading-importance-of-sre-in-modern-tech-organizations">Importance of SRE in Modern Tech Organizations:</h3>
<p>In today's digital economy, where user expectations are higher than ever, the reliability and performance of online services are critical to business success. Downtime or poor performance can have significant financial and reputational consequences, leading to lost revenue, customer churn, and damage to brand reputation.</p>
<p>SRE plays a vital role in addressing these challenges by applying software engineering principles to infrastructure and operations. This improves system reliability, scalability, and efficiency. </p>
<p>By fostering a culture of reliability and resilience, SRE enables organizations to deliver better user experiences, reduce operational overhead, and drive business growth.</p>
<p>And as organizations increasingly rely on cloud computing, microservices architecture, and DevOps practices to innovate and scale their operations, the role of SRE becomes even more crucial. SRE provides the expertise and tools necessary to manage complex distributed systems effectively, enabling organizations to leverage technology to achieve their business objectives.</p>
<p>So as you can see, SRE is not just a technical discipline but a strategic imperative for modern tech organizations seeking to thrive in a highly competitive and dynamic market landscape. By investing in SRE principles and practices, organizations can build more resilient and reliable systems, driving innovation, growth, and customer satisfaction.</p>
<h2 id="heading-prerequisites-and-fundamental-knowledge">Prerequisites and Fundamental Knowledge</h2>
<p>If you're going to embark on a career in Site Reliability Engineering (SRE), you'll need a solid foundation in computer science principles, a good grasp of programming, and an understanding of version control systems. </p>
<p>These components equip aspiring SREs with the necessary tools to design, develop, and manage reliable and scalable systems.</p>
<h3 id="heading-understanding-of-computer-science-basics">Understanding of Computer Science Basics</h3>
<p><strong>Operating Systems Concepts</strong>: A deep understanding of operating systems (OS) is crucial for SREs. This knowledge includes, but is not limited to, process management, memory management, file systems, and the OS's role in defining the interactions between hardware and software. </p>
<p>🔗<a target="_blank" href="https://www.freecodecamp.org/news/an-introduction-to-operating-systems/">You can checkout this Handbook</a> that teaches you key OS concepts for Mac, Linux, and Windows.</p>
<p>Familiarity with these concepts helps SREs in optimizing system performance and in diagnosing and troubleshooting system-level issues.</p>
<p><strong>Networking Fundamentals</strong>: Networking is the backbone of the internet and cloud services, making it essential for SREs to understand the basics of networking. This includes 🔗<a target="_blank" href="https://www.freecodecamp.org/news/what-is-tcp-ip-layers-and-protocols-explained/">TCP/IP models</a>, <a target="_blank" href="https://www.freecodecamp.org/news/what-is-dns-for-beginners/">DNS</a>, HTTP, HTTPS, and network protocols, as well as the ability to diagnose network-related issues. </p>
<p>Here's a 🔗<a target="_blank" href="https://www.freecodecamp.org/news/computer-networking-how-applications-talk-over-the-internet/">solid introduction to computer networking basics</a> you can use to get started.</p>
<p>And here's a 🔗<a target="_blank" href="https://www.freecodecamp.org/news/http-full-course/">full handbook on HTTP Networking</a> for beginners.</p>
<p>A solid grasp of networking principles allows SREs to ensure that the services they manage can communicate efficiently and reliably across the internet and within distributed systems.</p>
<h3 id="heading-proficiency-in-programming-languages">Proficiency in Programming Languages</h3>
<p><strong>Recommended Languages (Python, Go, Java)</strong>: SREs must be proficient in at least one programming language. </p>
<p>Python is widely favored for its simplicity and the vast ecosystem of libraries, making it ideal for automation scripts and tools. </p>
<p>freeCodeCamp 🔗<a target="_blank" href="https://www.freecodecamp.org/learn/scientific-computing-with-python/">has a couple Python certifications</a> if you want to learn the basics and get some practice coding in Python.</p>
<p>Go, developed by Google, is becoming increasingly popular in cloud services and systems programming due to its efficiency and performance. </p>
<p>🔗<a target="_blank" href="https://www.freecodecamp.org/news/learn-go-by-building-11-projects/">Here's a full course</a> that'll teach you go by having you build 11 projects.</p>
<p>Java, known for its portability and extensive use in enterprise environments, is also valuable. </p>
<p>🔗<a target="_blank" href="https://www.freecodecamp.org/news/learn-the-basics-of-java-programming/">Here's a full course</a> that teaches you coding in Java, 🔗<a target="_blank" href="https://www.freecodecamp.org/news/the-java-handbook/">along with a handbook</a> to reinforce your skills.</p>
<p>Mastery of these languages enables SREs to write efficient, reliable software that automates and enhances system operations.</p>
<p><strong>Scripting Skills (for example, Shell Scripting)</strong>: Scripting skills are important for automating routine tasks, such as software deployment, system configuration, and monitoring. <a target="_blank" href="https://www.freecodecamp.org/news/shell-scripting-crash-course-how-to-write-bash-scripts-in-linux/">Shell scripting</a>, in particular, is essential for Unix/Linux-based systems. </p>
<p>🔗<a target="_blank" href="https://www.freecodecamp.org/news/bash-scripting-tutorial-linux-shell-script-and-command-line-for-beginners/">Here's a tutorial on bash scripting</a> that'll walk you through some examples.</p>
<p>These scripting skills save time, reduce the likelihood of human error, and ensure that operations can scale efficiently.</p>
<h3 id="heading-familiarity-with-version-control-systems-like-git">Familiarity with Version Control Systems (like Git)</h3>
<p>Version control is fundamental to modern software development and operations. Git, being the most widely used version control system, is crucial for tracking changes in code, collaboration, and maintaining the integrity of software projects. </p>
<p>Understanding Git workflows, branches, commits, and merges is essential for SREs, as it enables them to manage code changes, automate parts of the software delivery pipeline, and roll back changes if necessary.</p>
<p>🔗<a target="_blank" href="https://www.freecodecamp.org/news/gitting-things-done-book/">Here's a full book</a> that'll teach you everything you need to know (and more!) to get started with Git.</p>
<p>And 🔗<a target="_blank" href="https://www.freecodecamp.org/news/learn-git-basics/">here's a handbook</a> that'll review the common commands and actions you'll use in version control every day.</p>
<p>Together, these prerequisites form the foundation upon which SREs build their skills. Mastery of computer science fundamentals, programming, and version control is essential for anyone looking to succeed in the rapidly evolving field of Site Reliability Engineering.</p>
<h2 id="heading-essential-skills-for-sre">Essential Skills for SRE</h2>
<p><img src="https://assets-global.website-files.com/5c9200c49b1194323aff7304/60c87194fb2d0e404ca27073_Top_SRE-570x330.png" alt="An image showing &quot;settings&quot; icon init" width="1140" height="660" loading="lazy"></p>
<p><em>The image above is gotten from <a target="_blank" href="https://www.squadcast.com/blog/top-sre-toolchain-used-by-site-reliability-engineers">SquadCast</a></em></p>
<p>The realm of Site Reliability Engineering is both broad and deep. It encompasses a range of skills that ensure systems are not only reliable but also efficient, scalable, and responsive to the needs of users and businesses alike.</p>
<h3 id="heading-system-administration-and-operations">System Administration and Operations</h3>
<ul>
<li><strong>Knowledge of Linux/Unix Administration</strong>: Proficiency in managing and troubleshooting 🔗<a target="_blank" href="https://www.youtube.com/watch?v=ROjZy1WbCIA">Linux or Unix-based environment</a>s is fundamental. This includes managing file systems, users, processes, packages, and services.</li>
<li><strong>Network Administration</strong>: Understanding network configuration, firewall management, and network services ensures SREs can optimize network performance and security. 🔗<a target="_blank" href="https://www.coursera.org/articles/what-is-a-network-administrator-a-career-guide">Here's an article that explains Network Admin</a>.</li>
<li><strong>Resource Management</strong>: Efficient management of system resources, including CPU, memory, and disk IO, to ensure optimal performance and reliability.</li>
</ul>
<h3 id="heading-automation-and-infrastructure-as-code-iac">Automation and Infrastructure as Code (IaC)</h3>
<ul>
<li><strong>Automation Tools</strong>: Proficiency in tools like <a target="_blank" href="https://www.youtube.com/watch?v=h8MurJBJVNc">Ansible</a>, Chef, or Puppet for 🔗<a target="_blank" href="https://www.redhat.com/en/topics/devops/what-is-ci-cd#:~:text=CI%2FCD%2C%20which%20stands%20for,a%20shared%20source%20code%20repository.">automating deployment, configuration, and management tasks</a>.</li>
<li><strong>Infrastructure as Code</strong>: Using tools such as Terraform and CloudFormation to manage infrastructure through code, enabling scalable and reproducible environments with reduced human error. TerraForm is the most suitable and popular, and I recommend that you 🔗<a target="_blank" href="https://www.youtube.com/watch?v=l5k1ai_GBDE">check out this 15 minute intro</a>.</li>
<li><strong>Scripting and Coding</strong>: Ability to write scripts and small programs to automate tasks and integrate systems</li>
</ul>
<h3 id="heading-monitoring-and-alerting">Monitoring and Alerting</h3>
<ul>
<li><strong>Implementing Monitoring Tools</strong>: Experience with tools like 🔗<a target="_blank" href="https://prometheus.io/docs/prometheus/latest/getting_started/">Prometheus</a>, 🔗<a target="_blank" href="https://www.youtube.com/watch?v=w-c3KYKQQfs">Grafana</a>, ELK Stack, or Splunk for real-time monitoring of applications and infrastructure. There are a lot of tools to mange and monitor incidents, but the ones listed above are the most wildly used in the industry. </li>
<li><strong>Log Management and Analysis</strong>: Ability to aggregate, analyze, and interpret logs from various sources for insight into system behavior and troubleshooting.</li>
<li><strong>Alerting Strategies</strong>: Developing effective alerting mechanisms that accurately reflect system health and operational issues without overwhelming with false positives.</li>
</ul>
<h3 id="heading-incident-response-and-post-incident-analysis">Incident Response and Post-Incident Analysis</h3>
<ul>
<li><strong>Incident Management</strong>: Ability to lead and manage the response to system outages or performance degradations to restore service as quickly as possible.</li>
<li>🔗 <strong><a target="_blank" href="https://www.atlassian.com/incident-management/handbook/postmortems">Blameless Postmortems</a></strong>: Conducting thorough analysis post-incident to identify root causes without attributing blame, focusing instead on learning and improvement.</li>
<li><strong>Reliability Metrics</strong>: Tracking and improving key reliability metrics such as availability, latency, and error rates. 🔗 <a target="_blank" href="https://www.blameless.com/blog/6-software-reliability-metrics-that-matter">Here's an article from <em>Blameless</em> that explains more about reliability metrics</a>.</li>
</ul>
<h3 id="heading-capacity-planning-and-performance-management">Capacity Planning and Performance Management</h3>
<ul>
<li><strong>Performance Tuning</strong>: After you've reviewed and gathered logs from your monitoring tools, it's a good idea to identify and optimise performance bottlenecks in applications and infrastructure.</li>
<li><strong>Scalability Strategies</strong>: Planning and implementing strategies for scaling systems to handle growth in users or data volume efficiently.</li>
<li><strong>Capacity Forecasting</strong>: Using metrics and trends to forecast future capacity needs and planning ahead to meet those requirements. Don't wait and hope the application won't go down – your task is to see into the future with the tools and skills you have to prevent it from going down.</li>
</ul>
<h3 id="heading-cloud-computing-concepts-and-technologies">Cloud Computing Concepts and Technologies</h3>
<ul>
<li><strong>Cloud Service Models</strong>: Understanding the spectrum of cloud services (🔗 <a target="_blank" href="https://www.ibm.com/topics/iaas-paas-saas">IaaS, PaaS, SaaS</a>) and how they can be leveraged for reliability and scalability.</li>
<li><strong>Cloud Providers</strong>: Familiarity with major cloud providers such as AWS, Google Cloud, and Azure, and their specific technologies and services.<br>🔗 <a target="_blank" href="https://www.youtube.com/watch?v=NhDYbskXRgc">Here's a 14 hour course to help you learn AWS</a>, 🔗 <a target="_blank" href="https://www.youtube.com/watch?v=m6ozQnqit50">a 4 hour course on Google Cloud</a>, and a 🔗 <a target="_blank" href="https://www.youtube.com/watch?v=jZx8PMQjobk">13 hour course on Azure</a> to get you on your feet!</li>
<li><strong>Cloud-Native Technologies</strong>: Knowledge of cloud-native technologies and practices, including 🔗 <a target="_blank" href="https://www.brightsidecodes.com/blog/understanding-microservices-and-api-gateway">microservices architecture</a>, <a target="_blank" href="https://www.freecodecamp.org/news/how-docker-containers-work/">containers</a> (for example, Docker), and orchestration tools (for example,  🔗 <a target="_blank" href="https://www.freecodecamp.org/news/the-kubernetes-handbook/">Kubernetes</a>), to build and manage scalable, resilient systems.  🔗 <a target="_blank" href="https://www.freecodecamp.org/news/learn-docker-and-kubernetes-hands-on-course/">This course</a> teaches you both Docker and Kubernetes basics.</li>
</ul>
<p>While all of these skills are vital, it isn't a must to master them, especially all at once. But knowing or having basic understanding of these essential skills enables SREs to ensure that systems are not just up and running, but also optimised for performance, ready to scale as needed, and resilient in the face of failures. </p>
<p>The role of an SRE demands a blend of expertise in software engineering and system operations, making it both a challenging and rewarding career path.</p>
<h2 id="heading-learning-path-and-resources"><strong>Learning Path and Resources</strong></h2>
<p>Like I said earlier in this article, this isn't a tutorial – it's more like a learning path that'll walk you through all that you need to get started in the SRE field. </p>
<p>The journey to becoming a proficient SRE is continuous and multifaceted. Engaging with a variety of resources and communities can significantly enhance your learning experience. </p>
<p>Below are some approaches and resources that you can use to learn or master the SRE field.</p>
<h3 id="heading-online-courses-and-tutorials">Online Courses and Tutorials</h3>
<ul>
<li><strong>Platforms like <a target="_blank" href="https://www.udemy.com/course/sre-bootcamp-builddeployrun-and-implement-observability/?couponCode=KEEPLEARNING">Udemy</a>, <a target="_blank" href="https://www.coursera.org/learn/site-reliability-engineering-slos">Coursera</a>, <a target="_blank" href="https://www.udacity.com/course/site-reliability-engineer-nanodegree--nd087">Udacity</a>, and <a target="_blank" href="https://www.edx.org/certificates/professional-certificate/ibm-site-reliability-engineering">edX</a></strong> offer comprehensive courses on SRE fundamentals, 🔗 <a target="_blank" href="https://www.simplilearn.com/tutorials/cloud-computing-tutorial">cloud computing</a>, 🔗 <a target="_blank" href="https://www.freecodecamp.org/news/automate-boring-tasks-no-code-automation-course/">automation</a>, and more. Look for courses developed in partnership with leading tech companies and universities.</li>
<li><strong>Specific Tutorials</strong> on tools and technologies (for example, 🔗 <a target="_blank" href="https://www.freecodecamp.org/news/learn-kubernetes-and-start-containerizing-your-applications/">Kubernetes</a>, 🔗 <a target="_blank" href="https://www.freecodecamp.org/news/how-to-use-terraform-to-deploy-a-site-on-google-cloud-platform/">Terraform</a>, Prometheus) abound on YouTube, or through the documentation and learning resources provided by the tools themselves. 🔗 <a target="_blank" href="https://www.freecodecamp.org/news/secure-server-infrastructure-clouds-using-falco-prometheus-grafana-and-docker/">Here's a fun tutorial that uses Prometheus</a> as part of a larger tech stack to secure server infrastructure clouds.</li>
</ul>
<h3 id="heading-books-and-publications">Books and Publications</h3>
<ul>
<li>🔗 S<a target="_blank" href="https://relyabilit.ie/">ite Reliability Engineering</a> by Niall Richard Murphy, Betsy Beyer, Chris Jones, and Jennifer Petoff (often referred to as the "SRE Bible"), published by O'Reilly, offers insights directly from Google's SRE team.</li>
<li>🔗 <a target="_blank" href="https://www.amazon.com/Phoenix-Project-DevOps-Helping-Business/dp/0988262592">The Phoenix Project</a> and 🔗 <a target="_blank" href="https://www.amazon.com/DevOps-Handbook-World-Class-Reliability-Organizations/dp/1942788002">The DevOps Handbook</a> by Gene Kim, Jez Humble, and others provide excellent insights into DevOps principles, which overlap significantly with SRE practices. If you're a fan of books, then you can purchase those books to read. </li>
<li><strong>Industry Publications</strong> such as ACM Queue or 🔗 <a target="_blank" href="https://www.ieee.org/">IEEE</a> Software regularly feature articles on SRE topics, case studies, and best practices.</li>
</ul>
<h3 id="heading-hands-on-projects-and-exercises">Hands-On Projects and Exercises</h3>
<ul>
<li><strong>Cloud Platforms</strong> offer free tiers or trial periods that are perfect for experimenting with cloud-based infrastructure and services.</li>
<li><strong>GitHub and GitLab</strong> host a multitude of open-source projects where you can contribute code, documentation, or even participate in issue resolution and feature requests.</li>
<li><strong>Personal Projects</strong> can also serve as a valuable learning tool. Try to replicate real-world systems, or automate the deployment and management of an application from scratch. The best way to learn is to practice. </li>
<li><strong>Contributing to open-source projects</strong> related to SRE tools and technologies not only gives you hands-on experience but also helps you understand the community standards and practices. Open source is a great way to learn from others, improve your knowledge, and gain valuable experience. Think of working on an open source project like an entry-level job where you get to do real things! Contribute, contribute, contribute.</li>
</ul>
<p>Embarking on your SRE learning journey is both exciting and demanding. It requires a commitment to continuous learning and improvement. </p>
<p>Leveraging a mix of online resources, books, hands-on projects, community participation, and professional networking will equip aspiring SREs with the knowledge, skills, and insights needed to succeed in this dynamic field.</p>
<h2 id="heading-how-to-succeed-in-the-sre-field">How to Succeed in the SRE Field</h2>
<p>Navigating a successful career in Site Reliability Engineering (SRE) requires more than just technical acumen. You'll also need to cultivate a mindset geared towards growth, collaboration, and resilience. </p>
<p>Achieving success as an SRE involves a blend of continuous learning, adaptability, communication, problem-solving, and a commitment to fostering a culture of reliability.</p>
<h3 id="heading-continual-learning-and-skill-development">Continual Learning and Skill Development</h3>
<ul>
<li><strong>Stay Updated</strong>: The tech field evolves rapidly, with new tools, languages, and practices emerging constantly. Dedicate time regularly to learn new skills and technologies. Search through YouTube, LinkedIn and Twitter and connects with friends, folks and people who share the same goal and skills with you. </li>
<li><strong>Deepen and Broaden Your Knowledge</strong>: While specializing in certain areas is valuable, having a broad understanding of related disciplines, such as cloud services, networking, and cybersecurity, can significantly enhance your effectiveness as an SRE.</li>
</ul>
<h3 id="heading-adaptability-to-new-technologies-and-methodologies">Adaptability to New Technologies and Methodologies</h3>
<ul>
<li><strong>Be Open to Change</strong>: Embrace new methodologies and technologies. The willingness to adapt and experiment with innovative solutions is crucial in an environment where reliability and efficiency are paramount.</li>
<li><strong>Experimentation and Evaluation</strong>: Apply critical thinking to assess the applicability of new tools and practices to your organization's specific challenges and objectives.</li>
</ul>
<h3 id="heading-effective-communication-and-collaboration">Effective Communication and Collaboration</h3>
<ul>
<li><strong>Clear Communication</strong>: Whether it's documenting an incident report, explaining a technical concept to a non-technical stakeholder, or writing code comments, clear communication is key.<br>🔗 <a target="_blank" href="https://www.coursera.org/articles/communication-effectiveness">Here's an article I found that can help with some effect communication</a>. </li>
<li><strong>Collaborative Mindset</strong>: SRE involves working closely with development, operations, and business teams. Building strong relationships based on trust and mutual respect is essential for achieving common goals.<br>🔗 <a target="_blank" href="https://www.linkedin.com/advice/1/how-can-software-developers-build-strong-relationships-ipv4c">Here's some killer advice from LinkedIn</a> that can help.</li>
</ul>
<h3 id="heading-problem-solving-and-troubleshooting-skills">Problem-Solving and Troubleshooting Skills</h3>
<ul>
<li><strong>Analytical Approach</strong>: Develop a methodical approach to troubleshooting and problem-solving. This includes breaking down complex systems into smaller components, identifying potential failure points, and systematically eliminating possibilities.</li>
<li><strong>Learning from Failures</strong>: Adopt a mindset that views failures as learning opportunities. Conduct blameless postmortems to understand what went wrong and how similar incidents can be prevented in the future.</li>
</ul>
<h3 id="heading-embracing-a-culture-of-reliability-and-resilience">Embracing a Culture of Reliability and Resilience</h3>
<ul>
<li><strong>Prioritize Reliability</strong>: Advocate for reliability and uptime within your organization, emphasizing that reliability is a feature not just for customers but for the business's bottom line.</li>
<li><strong>Resilience Engineering</strong>: Focus on building systems that are not only reliable under normal conditions but can also gracefully handle unexpected stressors and failures. This involves designing for failure, anticipating bottlenecks, and implementing fallback mechanisms. 🔗 <a target="_blank" href="https://devops.com/what-is-resilience-engineering/">Check out this article</a> to learn more about Resilience Engineering.</li>
</ul>
<p>Success in the SRE field is about more than just keeping the systems running. You'll also need to foresee potential issues, enhance system resilience, and ensure that the infrastructure can support the organization's long-term goals. </p>
<p>By focusing on continual learning, adaptability, communication, problem-solving, and a culture of reliability, you can contribute significantly to your team and organization, while also advancing your career in this dynamic and critical field. </p>
<p>If for some reasons you're still lost in this SRE thing, you can connect with me on <a target="_blank" href="https://www.linkedin.com/in/irorochadere/">LinkedIn</a> or <a target="_blank" href="https://twitter.com/iroro_chad">Twitter</a> where I'll be sharing some news, info, and updates about trending SRE topics and discussions. </p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this guide, we've journeyed through the essentials of what it takes to embark on a career in SRE. You should now understand its foundational principles and know how to acquire the necessary skills to excel in the role and make a significant impact within tech organizations. </p>
<p>Here's a recap of what we covered:</p>
<h3 id="heading-key-points">Key Points</h3>
<ul>
<li><strong>Introduction to SRE</strong>: We started with the genesis of SRE at Google, outlining its purpose to bridge the gap between development and operations, emphasizing reliability, scalability, and operational efficiency.</li>
<li><strong>Prerequisites and Fundamental Knowledge</strong>: A strong foundation in computer science principles, programming languages, and version control is essential for aspiring SREs.</li>
<li><strong>Essential Skills for SRE</strong>: We delved into system administration, automation, monitoring, incident response, and cloud computing as critical skills for anyone in the SRE domain.</li>
<li><strong>Learning Path and Resources</strong>: The path to becoming an SRE involves continuous learning through online courses, books, hands-on projects, and community engagement.</li>
<li><strong>Succeeding in the SRE Field</strong>: Success hinges on continual learning, adaptability, effective communication, problem-solving skills, and fostering a culture of reliability and resilience.</li>
</ul>
<h3 id="heading-pursue-sre-as-a-career-path">Pursue SRE as a Career Path</h3>
<p>Site Reliability Engineering is a mindset and a set of practices that can lead to highly rewarding careers. As businesses increasingly rely on technology, the demand for people who can ensure systems are reliable, scalable, and efficient has never been higher. </p>
<p>Pursuing a career in SRE offers the opportunity to work at the forefront of technology innovation, solving complex problems and making a tangible impact on the digital landscape.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ React Optimization Techniques to Help You Write More Performant Code ]]>
                </title>
                <description>
                    <![CDATA[ Performance optimization is a critical aspect of developing web applications. Users expect applications to load quickly and respond to their interactions smoothly.  In the React ecosystem, performance optimization techniques can significantly enhance... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/react-performance-optimization-techniques/</link>
                <guid isPermaLink="false">66bf4b68be10d17622ff3a0c</guid>
                
                    <category>
                        <![CDATA[ optimization ]]>
                    </category>
                
                    <category>
                        <![CDATA[ performance ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Temitope Oyedele ]]>
                </dc:creator>
                <pubDate>Fri, 16 Feb 2024 00:57:28 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/02/pexels-howard-adams-575835--1-.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Performance optimization is a critical aspect of developing web applications. Users expect applications to load quickly and respond to their interactions smoothly. </p>
<p>In the React ecosystem, performance optimization techniques can significantly enhance the user experience by reducing load times and improving responsiveness.</p>
<p>In this article, we will discuss eight effective techniques for optimizing the performance of your React application.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><a class="post-section-overview" href="#heading-why-performance-optimization-is-important">Why Performance Optimization is Important</a></li>
<li><a class="post-section-overview" href="#heading-list-visualization">List visualization</a></li>
<li><a class="post-section-overview" href="#heading-lazy-loading-images">Lazy Loading Images</a></li>
<li><a class="post-section-overview" href="#heading-memoization">Memoization</a></li>
<li><a class="post-section-overview" href="#heading-throttling-and-debouncing-events">Throttling and Debouncing Events</a></li>
<li><a class="post-section-overview" href="#heading-code-splitting">Code Splitting</a></li>
<li><a class="post-section-overview" href="#heading-react-fragments">React Fragments</a></li>
<li><a class="post-section-overview" href="#heading-web-workers">Web Workers</a></li>
<li><a class="post-section-overview" href="#heading-usetransition-hook">UseTransition Hook</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ol>
<h2 id="heading-why-performance-optimization-is-important">Why Performance Optimization is Important</h2>
<p>Optimizing the performance of your React application is crucial for several reasons:</p>
<ul>
<li><strong>Better User Experience:</strong> A slow-loading or laggy application can lead to a poor user experience, negatively impacting your business. Users expect fast and responsive interactions, and performance optimization helps deliver that.</li>
<li><strong>Improved SEO:</strong> Search engines like Google consider page load times and overall performance when ranking websites. A well-optimized application will rank higher in search results, making it more visible to potential users.</li>
<li><strong>Reduced Bounce Rates:</strong> If your application takes too long to load or respond, users will likely leave and never return. By optimizing performance, you can reduce bounce rates and increase engagement.</li>
<li><strong>Cost Savings</strong> A performant application requires fewer resources (like servers and memory) to handle the same workload. This means lower hosting costs and reduced infrastructure needs.</li>
<li><strong>Competitive Advantage:</strong> A fast and efficient application sets you apart from competitors whose applications may be slower or less optimized. According to research by <a target="_blank" href="https://www.portent.com/blog/analytics/research-site-speed-hurting-everyones-revenue.htm">Portent</a>, a website that loads within one second has a conversion rate five times higher than a site that takes ten seconds to load. Therefore, ensuring your React applications perform well is crucial for retaining users and maintaining a competitive edge.</li>
</ul>
<h2 id="heading-8-react-performance-optimization-techniques">8 React Performance Optimization Techniques</h2>
<p>Below are eight React performance optimization techniques you can use to speed up your applications.</p>
<h3 id="heading-list-visualization">List visualization</h3>
<p>List visualization, or windowing, involves rendering only the items currently visible on the screen. </p>
<p>When dealing with a large number of items in a list, rendering all the items at once can lead to slow performance and consume a significant amount of memory. List virtualization tackles this issue by rendering only a subset of the list items currently visible within the view, which conserves resources as the users scroll through the list.</p>
<p>The virtualization technique dynamically replaces rendered items with new ones, keeping the visible portion of the list updated and responsive. It efficiently allows you to render large lists or tabular data by only rendering the visible portion, recycling components as needed, and optimizing scroll performance.</p>
<p>There are different approaches to implementing list visualization in React, and one is using a popular library called <a target="_blank" href="https://www.npmjs.com/package/react-virtualized">React Virtualized</a>. </p>
<p>To install <code>react-virtualized</code>, you can use the following command:</p>
<pre><code class="lang-bash">npm install react-virtualized --save
</code></pre>
<p>After installing <code>react-virtualized</code>, you can import the required components and styles. Below is an example of how to use the <code>List</code> component to create a virtualized list:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { List } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-virtualized'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'react-virtualized/styles.css'</span>; <span class="hljs-comment">// Import styles</span>

<span class="hljs-comment">// Your list data</span>
<span class="hljs-keyword">const</span> list = <span class="hljs-built_in">Array</span>(<span class="hljs-number">5000</span>).fill().map(<span class="hljs-function">(<span class="hljs-params">_, index</span>) =&gt;</span> ({
  <span class="hljs-attr">id</span>: index,
  <span class="hljs-attr">name</span>: <span class="hljs-string">`Item <span class="hljs-subst">${index}</span>`</span>
}));

<span class="hljs-comment">// Function to render each row</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">rowRenderer</span>(<span class="hljs-params">{ index, key, style }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{key}</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{style}</span>&gt;</span>
      {list[index].name}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-comment">// Main component</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyVirtualizedList</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">List</span>
      <span class="hljs-attr">width</span>=<span class="hljs-string">{300}</span>
      <span class="hljs-attr">height</span>=<span class="hljs-string">{300}</span>
      <span class="hljs-attr">rowCount</span>=<span class="hljs-string">{list.length}</span>
      <span class="hljs-attr">rowHeight</span>=<span class="hljs-string">{20}</span>
      <span class="hljs-attr">rowRenderer</span>=<span class="hljs-string">{rowRenderer}</span>
    /&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> MyVirtualizedList;
</code></pre>
<p>In this example, <code>List</code> is the main component provided by <code>react-virtualized</code>. The <code>rowRenderer</code> function defines how each row should be rendered. The <code>width</code>, <code>height</code>, <code>rowCount</code>, <code>rowHeight</code>, and <code>rowRenderer</code> props are essential for configuring the list's behavior and appearance. </p>
<p>React applications can handle massive amounts of data by leveraging list virtualization without sacrificing performance or user experience.</p>
<h3 id="heading-lazy-loading-images">Lazy Loading Images</h3>
<p>Similar to the list virtualization technique, lazy loading images prevents the creation of unnecessary DOM nodes, thereby boosting performance. Lazy loading allows you to defer or delay the loading of images until they are needed or visible to the user instead of loading all the images on page load.</p>
<p>The concept behind lazy loading is to initiate the load of a placeholder or a small low-resolution version of the image, typically a small-sized thumbnail or a blurred placeholder. As the user scrolls or interacts with the page, the actual image is loaded dynamically, replacing the placeholder when the user enters the viewport or when it becomes visible.</p>
<p>Lazy loading in React can be achieved using various libraries and techniques. One of the popular libraries is the <a target="_blank" href="https://www.npmjs.com/package/react-lazyload">react-lazyload</a>.  </p>
<p>To install <code>react-lazyload</code>, you can use the following command:</p>
<pre><code class="lang-bash">npm install --save react-lazyload
</code></pre>
<p>Below is an example of a simple React component that uses <code>react-lazyload</code> to implement lazy loading for images:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> LazyLoad <span class="hljs-keyword">from</span> <span class="hljs-string">'react-lazyload'</span>;

<span class="hljs-keyword">const</span> MyLazyLoadedImage = <span class="hljs-function">(<span class="hljs-params">{ src, alt }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">LazyLoad</span> <span class="hljs-attr">height</span>=<span class="hljs-string">{200}</span> <span class="hljs-attr">offset</span>=<span class="hljs-string">{100}</span>&gt;</span>
      {/* The height and offset props control when the image should start loading */}
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{src}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">{alt}</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">LazyLoad</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> MyLazyLoadedImage;
</code></pre>
<p>In this example, <code>MyLazyLoadedImage</code> uses the <code>LazyLoad</code> component from <code>react-lazyload</code>. The <code>height</code> prop specifies the height of the placeholder, and the <code>offset</code> prop determines how far below the viewport the placeholder should start loading.</p>
<p>Another approach is to use the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API">intersection observer API</a>, which is a web API that allows you to detect when an element enters or exists the viewport efficiently. Here's how we can use the Intersection Observer API along with the <code>useEffect</code> hook in React:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React, { useEffect, useRef } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">const</span> IntersectionLazyLoad = <span class="hljs-function">(<span class="hljs-params">{ src, alt }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> imageRef = useRef();

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> options = {
      <span class="hljs-attr">root</span>: <span class="hljs-literal">null</span>, <span class="hljs-comment">// Use the viewport as the root</span>
      <span class="hljs-attr">rootMargin</span>: <span class="hljs-string">'0px'</span>, <span class="hljs-comment">// No margin around the root</span>
      <span class="hljs-attr">threshold</span>: <span class="hljs-number">0.5</span>, <span class="hljs-comment">// 50% of the image should be visible</span>
    };

    <span class="hljs-keyword">const</span> observer = <span class="hljs-keyword">new</span> IntersectionObserver(handleIntersection, options);

    <span class="hljs-keyword">if</span> (imageRef.current) {
      observer.observe(imageRef.current);
    }

    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-comment">// Cleanup the observer when the component is unmounted</span>
      observer.disconnect();
    };
  }, []);

  <span class="hljs-keyword">const</span> handleIntersection = <span class="hljs-function">(<span class="hljs-params">entries</span>) =&gt;</span> {
    entries.forEach(<span class="hljs-function">(<span class="hljs-params">entry</span>) =&gt;</span> {
      <span class="hljs-keyword">if</span> (entry.isIntersecting) {
        <span class="hljs-comment">// Load the image when it becomes visible</span>
        imageRef.current.src = src;
        imageRef.current.alt = alt;
      }
    });
  };

  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">{imageRef}</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">height:</span> '<span class="hljs-attr">200px</span>' }} <span class="hljs-attr">alt</span>=<span class="hljs-string">"Placeholder"</span> /&gt;</span></span>;
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> IntersectionLazyLoad;
</code></pre>
<p>In this example, <code>IntersectionLazyLoad</code> uses the Intersection Observer API to determine when the image becomes visible in the viewport. </p>
<p>By utilizing this API along with React <code>useEffect</code> hook, you can implement your custom lazy loading solution for images in React.</p>
<h3 id="heading-memoization">Memoization</h3>
<p>Memoization in React is a technique used to optimize the performance of functional components by caching the results of expensive computations or function calls. It's particularly useful when dealing with computationally intensive or frequently called functions with the same input values, as it helps avoid redundant calculations and improves the overall efficiency of the application.</p>
<p>In React, there are three techniques for memoization: <code>React.memo()</code>, <code>useMemo(),</code> and <code>useCallback().</code> Let's delve into the details for each:</p>
<h4 id="heading-how-to-use-reactmemo">How to use <code>React.memo()</code></h4>
<p>This higher-order component wraps purely functional components to prevent re-rendering if the received props remain unchanged.</p>
<p>By using <code>React.memo()</code>, the rendering result is cached based on props. If the props haven't changed since the last render, React reuses the previously rendered result instead of redoing the rendering process. This saves time and resources.</p>
<p> Below is an example on how to use the <code>React.memo</code> with a functional component:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">const</span> Post = <span class="hljs-function">(<span class="hljs-params">{ signedIn, post }</span>) =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Rendering Post'</span>);
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>{post.title}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{post.content}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      {signedIn &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>Edit Post<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> React.memo(Post);
</code></pre>
<p>In the code above, <code>Post</code> (functional component) depends on the <code>signedIn</code> and <code>post</code> props. By wrapping it with <code>React.memo()</code>, React will only re-render the <code>Post</code> component if either <code>signedIn</code> or <code>post</code> changes.</p>
<p>You can now use the memoized component like any other component in your application:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React, { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> Post <span class="hljs-keyword">from</span> <span class="hljs-string">'./Post'</span>;

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [signedIn, setSignedIn] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> post = { <span class="hljs-attr">title</span>: <span class="hljs-string">'Hello World'</span>, <span class="hljs-attr">content</span>: <span class="hljs-string">'Welcome to my blog!'</span> };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Post</span> <span class="hljs-attr">signedIn</span>=<span class="hljs-string">{signedIn}</span> <span class="hljs-attr">post</span>=<span class="hljs-string">{post}</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setSignedIn(!signedIn)}&gt;
        Toggle Signed In
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>When you click the <code>Toggle Signed In</code> button, it will toggle the <code>signedIn</code> state. Since <code>Post</code> is wrapped with <code>React.memo()</code>, it will only re-render when the <code>signedIn</code> prop changes, thus saving rendering time and resources</p>
<h4 id="heading-how-to-use-usememo">How to use <code>useMemo()</code></h4>
<p>The <code>useMemo()</code> hook optimizes performance by memoizing the result of a function call or an expensive computation. It caches the result and recalculates it only when the input values change. Below is an example on how to use the <code>useMemo</code> hook in functional component:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React, { useMemo } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [count, setCount] = React.useState(<span class="hljs-number">0</span>);
  <span class="hljs-keyword">const</span> [otherState, setOtherState] = React.useState(<span class="hljs-string">''</span>);

  <span class="hljs-keyword">const</span> expensiveComputation = <span class="hljs-function">(<span class="hljs-params">num</span>) =&gt;</span> {
    <span class="hljs-keyword">let</span> i =  <span class="hljs-number">0</span>;
    <span class="hljs-keyword">while</span> (i &lt;  <span class="hljs-number">1000000000</span>) i++;
    <span class="hljs-keyword">return</span> num * num;
  };

  <span class="hljs-keyword">const</span> memoizedValue = useMemo(<span class="hljs-function">() =&gt;</span> expensiveComputation(count), [count]);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Count: {count}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Square: {memoizedValue}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setCount(count +  1)}&gt;Increase Count<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setOtherState(e.target.value)} /&gt;
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>In the code above, the <code>expensiveComputation</code> function simulates a resource-intensive operation, like squaring a number. </p>
<p>The <code>useMemo</code> hook is utilized to cache the result of this computation. The memoized value, stored in <code>memoizedValue</code>, is only recalculated when the <code>count</code> state changes, as <code>count</code> is specified as a dependency in the <code>useMemo</code> dependency array. Consequently, clicking the <code>Increase Count</code> button increments the <code>count</code> state, triggering a recalculation of the memoized value. </p>
<p>Conversely, changing the <code>otherState</code> via the input field does not prompt a recalculation, as <code>otherState</code> is not included in the <code>useMemo</code> dependency array.</p>
<h4 id="heading-how-to-use-usecallback">How to use <code>useCallback()</code></h4>
<p>The <code>useCallback()</code> hook in React is used to memoize a function instead of memoizing the function result. It is particularly useful when passing events as props to child components to prevent unnecessary re-renders. </p>
<p><code>useCallback()</code> memoizes the function, ensuring it remains the same across re-renders as long as the dependencies haven't changed. </p>
<p>This is especially beneficial when passing functions as props to child components, preventing unnecessary re-renders. It is often used with <code>React.memo()</code> to ensure child components do not re-render when unnecessary. Below is an exmple of how to use the <code>useCallback()</code> hook:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React, { useState, useCallback } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">const</span> ParentComponent = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);

  <span class="hljs-comment">// Define a function that increments the count state</span>
  <span class="hljs-keyword">const</span> incrementCount = <span class="hljs-function">() =&gt;</span> {
    setCount(count + <span class="hljs-number">1</span>);
  };

  <span class="hljs-comment">// Memoize the incrementCount function using useCallback</span>
  <span class="hljs-keyword">const</span> memoizedIncrement = useCallback(incrementCount, [count]);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Count: {count}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ChildComponent</span> <span class="hljs-attr">onIncrement</span>=<span class="hljs-string">{memoizedIncrement}</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">const</span> ChildComponent = React.memo(<span class="hljs-function">(<span class="hljs-params">{ onIncrement }</span>) =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Child component rendered'</span>);
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{onIncrement}</span>&gt;</span>Increment Count<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> ParentComponent;
</code></pre>
<p>In the code above, the <code>ParentComponent</code> is responsible for managing a state variable named <code>count</code> and introduces a function called <code>incrementCount</code>, which handles the incrementation of the count. Utilizing the <code>useCallback</code> hook, the <code>incrementCount</code> function is memoized, guaranteeing its stability across renders unless any of its dependencies, in this case, <code>count</code>, undergo changes.</p>
<p>On the other hand, the <code>ChildComponent</code> is a component nested within the parent. It receives the memoized <code>onIncrement</code> function from the parent as a prop. </p>
<p>To optimize performance and prevent unnecessary re-renders when the props remain constant, the <code>ChildComponent</code> is wrapped with <code>React.memo()</code>. This ensures that the child component will only re-render when its props, specifically the memoized function, experience changes, contributing to a more efficient rendering process.</p>
<p>It's important to note that <code>useCallback</code> should be used sparingly and only for performance-critical parts of your application. Overusing <code>useCallback</code> can actually lead to worse performance due to the overhead of memoization itself. Always measure the performance impact before and after using <code>useCallback</code> to ensure it's having the desired effect.</p>
<h3 id="heading-throttling-and-debouncing-events">Throttling and Debouncing Events</h3>
<p>Throttling in React is a technique used to limit the number of times a function or an event handler is invoked. It ensures that the function is called at a specified interval, preventing it from being executed too frequently. </p>
<p>Throttling allows you to control the rate at which the function is called by setting up a minimum time interval between each function invocation. If the function is called multiple times within that interval, only the first invocation is executed, and subsequent invocations are ignored until the interval elapses</p>
<p>Now, let's illustrate throttling with a code example. First, without throttling:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Without throttling, this function will be called every time the event is triggered</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleResize</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Window resized'</span>);
}

<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'resize'</span>, handleResize);
</code></pre>
<p>With throttling, we can limit how often the <code>handleResize</code> function is called:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Throttling function</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">throttle</span>(<span class="hljs-params">func, delay</span>) </span>{
  <span class="hljs-keyword">let</span> lastCall =  <span class="hljs-number">0</span>;
  <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">...args</span>) </span>{
    <span class="hljs-keyword">const</span> now = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getTime();
    <span class="hljs-keyword">if</span> (now - lastCall &lt; delay) {
      <span class="hljs-keyword">return</span>;
    }
    lastCall = now;
    func(...args);
  };
}

<span class="hljs-comment">// Throttled event handler</span>
<span class="hljs-keyword">const</span> throttledHandleResize = throttle(handleResize,  <span class="hljs-number">200</span>);

<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'resize'</span>, throttledHandleResize)
</code></pre>
<p>In this example, the <code>throttle</code> function wraps <code>handleResize</code> and ensures it's not called more often than every 200 milliseconds. If the <code>resize</code> event fires more frequently than that, the <code>handleResize</code> function will only be executed once every 200 milliseconds, reducing the potential for performance issues caused by rapid, repeated function calls</p>
<p>Debouncing, on the other hand, is also used to limit the number of times a function or an event handler is invoked. It ensures that the function is called only after a certain period of inactivity. Debouncing allows you to postpone the function call until the user has finished typing or a specific time has elapsed since the last event.</p>
<p>For example, imagine you have a search input field and want to trigger a search API request only when the user has finished typing for a certain duration, like <code>300ms</code>. </p>
<p>With debouncing, the search function will only be invoked after the user stops typing for <code>300ms</code>. If the user continues typing within that interval, the function call will be delayed until the pause occurs. Without debouncing, the function will be called for every keystroke, potentially leading to excessive function calls and unnecessary computation. let's demonstrate with a code example:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React, { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">const</span> SearchComponent = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [searchTerm, setSearchTerm] = useState(<span class="hljs-string">''</span>);

  <span class="hljs-comment">// Function to simulate a search API request</span>
  <span class="hljs-keyword">const</span> searchAPI = <span class="hljs-function">(<span class="hljs-params">query</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Searching for: <span class="hljs-subst">${query}</span>`</span>);
    <span class="hljs-comment">// In a real application, you would make an API request here</span>
  };

  <span class="hljs-comment">// Debounce function to delay the searchAPI call</span>
  <span class="hljs-keyword">const</span> debounce = <span class="hljs-function">(<span class="hljs-params">func, delay</span>) =&gt;</span> {
    <span class="hljs-keyword">let</span> timeoutId;
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">...args</span>) </span>{
      <span class="hljs-built_in">clearTimeout</span>(timeoutId);
      timeoutId = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
        func(...args);
      }, delay);
    };
  };

  <span class="hljs-comment">// Debounced search function</span>
  <span class="hljs-keyword">const</span> debouncedSearch = debounce(searchAPI, <span class="hljs-number">300</span>);

  <span class="hljs-comment">// useEffect to watch for changes in searchTerm and trigger debouncedSearch</span>
  useEffect(<span class="hljs-function">() =&gt;</span> {
    debouncedSearch(searchTerm);
  }, [searchTerm, debouncedSearch]);

  <span class="hljs-comment">// Event handler for the search input</span>
  <span class="hljs-keyword">const</span> handleSearchChange = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
    setSearchTerm(event.target.value);
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"search"</span>&gt;</span>Search:<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
        <span class="hljs-attr">id</span>=<span class="hljs-string">"search"</span>
        <span class="hljs-attr">value</span>=<span class="hljs-string">{searchTerm}</span>
        <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleSearchChange}</span>
        <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Type to search..."</span>
      /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> SearchComponent;
</code></pre>
<p>With this setup, the <code>searchAPI</code> function will only be invoked after the user stops typing for 300ms, preventing excessive API requests and improving the overall performance of the search functionality.</p>
<h3 id="heading-code-splitting">Code Splitting</h3>
<p>Code splitting in React is a technique used to split a large JavaScript bundle into smaller, manageable chunks. It helps improve performance by loading only the necessary code for a specific part of an application rather than loading the entire bundle upfront. </p>
<p>When you develop a new React application, all your JavaScript code is typically bundled together into a single file. This file contains all the components, libraries, and other code required for your application to function. But as your application grows, the bundle size can become quite large, resulting in slow initial load times for your users.</p>
<p>Code splitting allows you to divide a single bundle into multiple chunks, which can be loaded selectively based on the current needs of your application. Instead of downloading the entire bundle upfront, only the necessary code is fetched and executed when a user visits a particular page or triggers a specific action.</p>
<p>Below is a basic example of code splitting:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// AsyncComponent.js</span>
<span class="hljs-keyword">import</span> React, { lazy, Suspense } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">const</span> DynamicComponent = lazy(<span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">'./DynamicComponent'</span>));

<span class="hljs-keyword">const</span> AsyncComponent = <span class="hljs-function">() =&gt;</span> (
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Suspense</span> <span class="hljs-attr">fallback</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">div</span>&gt;</span>Loading...<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>}&gt;
    <span class="hljs-tag">&lt;<span class="hljs-name">DynamicComponent</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">Suspense</span>&gt;</span></span>
);

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> AsyncComponent;


<span class="hljs-comment">// DynamicComponent.js</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">const</span> DynamicComponent = <span class="hljs-function">() =&gt;</span> (
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>This is a dynamically loaded component!<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
);

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> DynamicComponent;
</code></pre>
<p>In this example, <code>AsyncComponent</code> is a component that uses <code>lazy</code> and <code>Suspense</code> to perform code splitting. The <code>DynamicComponent</code> is dynamically imported using the import() syntax. </p>
<p>When <code>AsyncComponent</code> is rendered, React will load <code>DynamicComponent</code> only when it is needed, reducing the initial bundle size and improving the application's performance. The fallback prop in Suspense specifies what to render while waiting for the dynamic import to resolve, providing a better user experience during the loading process.</p>
<h3 id="heading-react-fragments">React Fragments</h3>
<p>React Fragments are a feature introduced in <a target="_blank" href="https://legacy.reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html">React 16.2</a> that allows you to group multiple elements together without adding an additional DOM node. This is particularly useful when you need to return multiple elements from a component's render method, but you don't want to introduce unnecessary DOM elements that could affect the layout or styles of your application.</p>
<p>Imagine you are arranging books on a bookshelf. Each book represents a React component, and the bookshelf represents the DOM. </p>
<p>Normally, if you have multiple books, you might want to group them together under a category label (analogous to a DOM element like a <code>&lt;div&gt;</code>). But sometimes you just want to place the books side by side without a label because the label itself doesn't hold any value and only takes up physical space. </p>
<p>React Fragments are like the option to arrange the books without a label, saving space and making the arrangement cleaner.</p>
<p>Here's an example of how to utilize React fragments:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">BookShelf</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Book</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"React for Beginners"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Book</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Mastering Redux"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Book</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"JavaScript Essentials"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Book</span>(<span class="hljs-params">{ title }</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>{title}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span></span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> BookShelf;
</code></pre>
<p>In this example, the <code>BookShelf</code> component returns a list of <code>Book</code> components without wrapping them in a <code>&lt;div&gt;</code> or other unnecessary DOM element. Instead, it uses the <code>&lt;&gt;</code> shorthand syntax for React Fragments. </p>
<p>This results in a cleaner DOM structure, which can improve the performance of your React application by reducing the number of elements that the browser has to process and render. Using fragments can also reduce unnecessary markup and contribute to a cleaner and more efficient render tree.</p>
<h3 id="heading-web-workers">Web Workers</h3>
<p>JavaScript operates as a single-threaded application designed to handle synchronous tasks. </p>
<p>When a web page is being rendered, JavaScript executes multiple tasks, including manipulating DOM elements, managing UI interactions, handling API response data, and enabling CSS animations, all within a single thread. Despite its efficiency in managing these tasks, executing them in a single thread can sometimes lead to performance bottlenecks.</p>
<p>Web Workers serve as a solution to alleviate the burden on the main thread. They allow the execution of scripts in the background on a separate thread, distinct from the main JavaScript thread. </p>
<p>This separation enables the handling of computationally intensive tasks, execution of long-running operations, or management of tasks that might otherwise block the main thread. By doing so, Web Workers contribute to maintaining user interface responsiveness and overall application performance.</p>
<p>To use web worker in React, create a new JavaScript file that will contain the code for the worker thread:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// worker.js</span>
self.onmessage = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>) </span>{
  <span class="hljs-keyword">var</span> input = event.data;
  <span class="hljs-keyword">var</span> result = performHeavyComputation(input);
  postMessage(result);
};

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">performHeavyComputation</span>(<span class="hljs-params">input</span>) </span>{
  <span class="hljs-comment">// Insert your heavy computation logic here</span>
  <span class="hljs-keyword">return</span> input *   <span class="hljs-number">2</span>; <span class="hljs-comment">// Just a placeholder operation</span>
}
</code></pre>
<p>In your React component, instantiate the Web Worker and establish a communication channel with it:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React, { useEffect, useRef } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyComponent</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> workerRef = useRef();

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// Initialize the worker</span>
    workerRef.current = <span class="hljs-keyword">new</span> Worker(<span class="hljs-string">'path-to-your-worker-file.js'</span>);

    <span class="hljs-comment">// Handle incoming messages from the worker</span>
    workerRef.current.onmessage = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Message received from worker:'</span>, event.data);
    };

    <span class="hljs-comment">// Cleanup the worker when the component unmounts</span>
    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      workerRef.current.terminate();
    };
  }, []);

  <span class="hljs-comment">// Function to send a message to the worker</span>
  <span class="hljs-keyword">const</span> sendMessageToWorker = <span class="hljs-function">(<span class="hljs-params">message</span>) =&gt;</span> {
    workerRef.current.postMessage(message);
  };

  <span class="hljs-comment">// Rest of your component</span>
  <span class="hljs-keyword">return</span> (
    <span class="hljs-comment">// ...</span>
  );
}
</code></pre>
<p>In this example, a Web Worker is initialized in the <code>useEffect</code> hook and stored in a ref for future use. Messages from the worker are handled with an <code>onmessage</code> event listener, and the worker is terminated when the component is unmounted to clean up resources. The <code>sendMessageToWorker</code> function demonstrates how to communicate with the worker using <code>postMessage</code></p>
<h3 id="heading-usetransition-hook">UseTransition Hook</h3>
<p>The <code>useTransition</code> hook in React plays a pivotal role in improving the performance of applications by allowing the marking of state updates as non-blocking transitions. This capability enables React to defer rendering for these updates, preventing UI blocking and enhancing overall responsiveness. </p>
<p>When utilizing <code>useTransition,</code> state updates within the <code>startTransition</code> function are treated as low-priority transitions, susceptible to interruption by higher-priority state updates. So if a high-priority update occurs during a transition, React may prioritize finishing the high-priority update, interrupting the ongoing transition.</p>
<p>This non-blocking transition mechanism is valuable in preventing UI blocking during intensive operations such as data fetching or large-scale updates. By deferring the rendering of components associated with transition updates, React ensures that the user interface remains responsive even in scenarios where the UI might otherwise become unresponsive.</p>
<p>This example demonstrates the use of <code>useTransition</code> in a React component:</p>
<pre><code class="lang-javascript=">import React, { useState, useTransition } from 'react';

function MyComponent() {
  const [state, setState] = useState(initialState);
  const [isPending, startTransition] = useTransition();

  function handleClick() {
    startTransition(() =&gt; {
      setState(newState); // This state update is marked as a transition
    });
  }

  return (
    &lt;&gt;
      {/* Your component JSX */}
      &lt;button onClick={handleClick}&gt;Update State&lt;/button&gt;
      {isPending &amp;&amp; &lt;div&gt;Loading...&lt;/div&gt;}
    &lt;/&gt;
  );
}
</code></pre>
<p>This example showcases how React avoids blocking the UI during transitions triggered by user actions, allowing for interruption if higher-priority state updates are detected. </p>
<p>Note that <code>useTransition</code> is part of the Concurrent Mode API, introduced in React 18 and later versions. As a powerful tool for altering the default behavior of state updates, make sure you use it with care, considering the specific implications of deferring rendering within the context of your application.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Optimizing the performance of a React application involves a combination of strategies, from the fundamental understanding of React's diffing algorithm to leveraging built-in features and third-party tools. </p>
<p>By applying these techniques judiciously, you can create applications that are not only visually appealing but also performant, leading to a better overall user experience.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Immutable JavaScript – How to Improve the Performance of Your JS Applications ]]>
                </title>
                <description>
                    <![CDATA[ Javascript has become a very popular programming language thanks to its growing use in frontend and backend development.  And as devs build JavaScript applications for different companies and organizations, the size and complexity of these applicatio... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/immutable-javascript-improve-application-performance/</link>
                <guid isPermaLink="false">66bc4c5c303bf450de87bc7c</guid>
                
                    <category>
                        <![CDATA[ immutability ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ performance ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Clinton Joy ]]>
                </dc:creator>
                <pubDate>Mon, 05 Feb 2024 15:09:28 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/02/Screen-Shot-2024-02-01-at-11.16.23-AM.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Javascript has become a very popular programming language thanks to its growing use in frontend and backend development. </p>
<p>And as devs build JavaScript applications for different companies and organizations, the size and complexity of these applications can pose interesting performance challenges.</p>
<p>As developers, we seek to create applications that not only have high performance but also have an enhanced user experience. To do this, we must fully understand how immutability works in Javascript, as this is one factor that contributes a lot to achieving the enhanced performance we seek.</p>
<h2 id="heading-table-of-contents">Table of Contents:</h2>
<ol>
<li><a class="post-section-overview" href="#heading-what-is-immutability-in-javascript">What is immutability in JavaScript?</a></li>
<li><a class="post-section-overview" href="#heading-benefits-of-immutability-in-applications">Benefits of Immutability in Applications</a></li>
<li><a class="post-section-overview" href="#heading-techniques-for-achieving-immutability">Techniques for Achieving Immutability</a></li>
<li><a class="post-section-overview" href="#heading-how-to-use-es6-features-for-immutability-spread-syntax-and-objectfreeze">How to Use ES6 Features for Immutability – Spread Syntax and <code>Object.freeze()</code></a></li>
<li><a class="post-section-overview" href="#heading-performance-optimization-through-immutability">Performance Optimization through Immutability</a></li>
<li><a class="post-section-overview" href="#heading-real-world-examples-of-companies-and-projects-benefiting-from-immutability">Real-World Examples of Companies and Projects Benefiting from Immutability</a></li>
<li><a class="post-section-overview" href="#heading-common-pitfalls-of-immutable-javascript">Common Pitfalls of Immutable JavaScript</a></li>
<li><a class="post-section-overview" href="#heading-best-practices-for-overcoming-immutability-related-issues">Best Practices for Overcoming Immutability-Related Issues</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ol>
<h2 id="heading-what-is-immutability-in-javascript">What is Immutability in JavaScript?</h2>
<p>According to the Oxford English Learners Dictionary, immutability means "something that cannot be changed; that will never change," whereas on the other hand, we have mutability, which is the direct opposite of immutability and means something that can change.</p>
<p>If you want to grasp the full meaning of immutability, we have to differentiate it from mutability. Mutability, in JavaScript, refers to the ability to modify the value of a variable or a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures">data type</a>. Immutability, on the other hand, means that once a value is created, it cannot be changed. JavaScript differentiates data types by their default characters.</p>
<p>Primitive data types such as <code>strings</code>, <code>numbers</code>, <code>booleans</code>, and symbols are immutable, while reference data types such as <code>objects</code>, <code>arrays</code>, and <code>functions</code> are mutable. </p>
<p>To explain further, let's take a look at this simple example:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> person1 = <span class="hljs-number">10</span>;
person2 = person1
person2 = <span class="hljs-number">8</span>;

<span class="hljs-built_in">console</span>.log(person2) <span class="hljs-comment">// 8</span>
<span class="hljs-built_in">console</span>.log(person1) <span class="hljs-comment">// 10</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/image-7.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>On examining this example, it would seem as if <code>person1</code> was modified. But the variable <code>person1</code> was reassigned to take the value of <code>person2</code>. But when we check the value of <code>person1</code> in the console, we will notice that the value of <code>person1</code> remains unchanged.</p>
<p>This means that the <code>person2</code> variable is just a clone of the <code>person1</code> variable that has been reassigned, and the actual value of the <code>person1</code> variable was not modified. </p>
<p>On the other hand, if we were to try to do the same with a reference data type, here's what would happen:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> student1 = {
    <span class="hljs-attr">name</span>: <span class="hljs-string">"Kevin"</span>,
    <span class="hljs-attr">age</span>: <span class="hljs-number">20</span>,
};

student2 = student1;

student2.name = <span class="hljs-string">"Paul"</span>;

<span class="hljs-built_in">console</span>.log(student1) <span class="hljs-comment">// {name: 'Paul', age: 20}</span>
<span class="hljs-built_in">console</span>.log(student2) <span class="hljs-comment">// {name: 'Paul', age: 20}</span>
</code></pre>
<p><img src="https://hackmd.io/_uploads/B1WcDwZDp.png" alt="Screen Shot 2023-12-21 at 9.21.34 AM" width="600" height="400" loading="lazy">
<em>Mutable data example</em></p>
<p>On close inspection, you'll notice that as we set <code>student2</code> to be equal to <code>student1</code> and reassign the value of <code>student2.name</code>, it also changes the value of <code>student1.name</code>. This means that the value of <code>student1</code> was not just reassigned but modified. This proves that reference data types are mutable.</p>
<h2 id="heading-benefits-of-immutability-in-applications">Benefits of Immutability in Applications</h2>
<p>You may already be wondering: Isn't it a good thing for your variable to be mutable rather than rigid? Though mutability comes with some perks, so does immutability. In this section, we will see the benefits of immutability in applications.</p>
<p>First of all, immutable data structures are more stable and predictable. They're immune to unexpected alterations, rendering the code more deterministic and less prone to unforeseen bugs or side effects, which is very useful in large-scale applications.</p>
<p>You also have fewer bugs and data races. Immutability eliminates the possibility of accidentally modifying data in place, which can lead to data races and concurrency issues. By preventing direct modifications, immutability promotes thread safety and ensures that data remains consistent across multiple threads or processes.</p>
<p>Memory management and optimization also improve with immutability. It improves memory utilization by enabling safe data structure sharing, eliminating concerns about unintended modifications. </p>
<p>While the idea of creating copies might seem counterintuitive in pursuit of immutability, it is balanced by the benefits of structural sharing, efficient garbage collection, and the design of data structures.</p>
<p>These elements work to ensure that, despite the initial creation of copies for immutability, the long-term memory optimization prevails. The utilization of structural sharing minimizes the need for complete data duplication, while efficient garbage collection promptly removes unused data structures, contributing to optimal memory usage over time. </p>
<p>This approach not only reduces memory overhead but also optimizes resource utilization, playing a crucial role in scaling applications efficiently.</p>
<p>And finally, immutability produces more effective testing and debugging. Immutable data simplifies testing and debugging by providing a stable and predictable environment. Since objects cannot be modified, tests can focus on specific behaviors without worrying about external changes. This makes it easier to isolate and fix bugs.</p>
<h2 id="heading-techniques-for-achieving-immutability">Techniques for Achieving Immutability</h2>
<p>You can achieve immutability in JavaScript in a lot of ways, from using a persistent data structure to using ES6 features such as <code>Object.freeze()</code>. This section seeks to explain these techniques for achieving immutability and how you can use them.</p>
<h3 id="heading-introduction-to-persistent-data-structures">Introduction to Persistent Data Structures</h3>
<p>Persistence in data structures refers to the ability to retain the previous version while accommodating changes. In traditional mutable data structures, alterations directly modify the existing data. But in <a target="_blank" href="https://en.wikipedia.org/wiki/Persistent_data_structure#1">persistent data structures</a>, any modification creates a new version of the structure, leaving the original intact.</p>
<p>This characteristic is what makes persistent data structures inherently immutable. This promotes code reliability, efficient memory utilization, and allows for concurrent operations.</p>
<p>JavaScript itself, as a language, does not inherently offer persistent data structures in its standard library. But libraries and frameworks in JavaScript, like <a target="_blank" href="https://immutable-js.com/">Immutable.js</a>, offer implementations of persistent data structures. </p>
<p>Immutable.js provides various persistent data structures, including:</p>
<ul>
<li>Persistent List</li>
<li>Persistent Maps &amp; Sets</li>
<li>Persistent Trees</li>
</ul>
<h3 id="heading-persistent-lists">Persistent Lists</h3>
<p>Persistent lists, like immutable linked lists, allow for efficient modifications by creating new versions while reusing the majority of the existing structure.<br>Let's consider this example:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ShoppingList</span> </span>{
  <span class="hljs-keyword">constructor</span>(item, next = null) {
    <span class="hljs-built_in">this</span>.item = item;
    <span class="hljs-built_in">this</span>.next = next;
  }

  addItem(newItem) {
    <span class="hljs-comment">// Create a copy of the list</span>
    <span class="hljs-keyword">const</span> copiedList = <span class="hljs-built_in">this</span>.copyList();
    <span class="hljs-comment">// Add the new item to the copied list</span>
    <span class="hljs-keyword">return</span> copiedList.addItemToCopy(newItem);
  }

  addItemToCopy(newItem) {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ShoppingList(newItem, <span class="hljs-built_in">this</span>);
  }

  removeItem(itemToRemove) {
    <span class="hljs-comment">// Create a copy of the list</span>
    <span class="hljs-keyword">const</span> copiedList = <span class="hljs-built_in">this</span>.copyList();
    <span class="hljs-keyword">let</span> current = copiedList;
    <span class="hljs-keyword">let</span> previous = <span class="hljs-literal">null</span>;

    <span class="hljs-keyword">while</span> (current !== <span class="hljs-literal">null</span>) {
      <span class="hljs-keyword">if</span> (current.item === itemToRemove) {
        <span class="hljs-keyword">if</span> (previous === <span class="hljs-literal">null</span>) {
          <span class="hljs-keyword">return</span> current.next;
        } <span class="hljs-keyword">else</span> {
          previous.next = current.next;
          <span class="hljs-keyword">return</span> copiedList;
        }
      }
      previous = current;
      current = current.next;
    }
    <span class="hljs-keyword">return</span> copiedList;
  }

  copyList() {
    <span class="hljs-comment">// Create a copy of the list</span>
    <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>;
    <span class="hljs-keyword">let</span> newList = <span class="hljs-literal">null</span>;
    <span class="hljs-keyword">let</span> newListTail = <span class="hljs-literal">null</span>;

    <span class="hljs-keyword">while</span> (current !== <span class="hljs-literal">null</span>) {
      <span class="hljs-keyword">if</span> (newList === <span class="hljs-literal">null</span>) {
        newList = <span class="hljs-keyword">new</span> ShoppingList(current.item);
        newListTail = newList;
      } <span class="hljs-keyword">else</span> {
        newListTail.next = <span class="hljs-keyword">new</span> ShoppingList(current.item);
        newListTail = newListTail.next;
      }
      current = current.next;
    }
    <span class="hljs-keyword">return</span> newList;
  }
}

<span class="hljs-comment">// Creating a persistent shopping list</span>
<span class="hljs-keyword">const</span> originalList = <span class="hljs-keyword">new</span> ShoppingList(<span class="hljs-string">"Milk"</span>).addItem(<span class="hljs-string">"Eggs"</span>).addItem(<span class="hljs-string">"Bread"</span>);
<span class="hljs-keyword">const</span> updatedList = originalList.addItem(<span class="hljs-string">"Butter"</span>).addItem(<span class="hljs-string">"Cheese"</span>);

<span class="hljs-comment">// Removing an item from the list</span>
<span class="hljs-keyword">const</span> removedItem = updatedList.removeItem(<span class="hljs-string">"Eggs"</span>);

<span class="hljs-comment">// Logging the original, updated, and list after removing an item</span>
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Original List:"</span>);
<span class="hljs-built_in">console</span>.log(originalList);

<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"\nUpdated List:"</span>);
<span class="hljs-built_in">console</span>.log(updatedList);

<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"\nList after removing an item:"</span>);
<span class="hljs-built_in">console</span>.log(removedItem);
</code></pre>
<p>In our code above, we define a class called <code>ShoppingList</code> which we use as a representation of a persistent shopping list. This persistent shopping list is one that can be modified and added to over time.</p>
<p>We have a constructor <code>constructor(item, next = null)</code> which initializes a new list node with the specified item and an optional reference to the next node.</p>
<p>The <code>addItem(newItem)</code> method creates a copy of the current list, adds the new item to the copy, and returns the modified copy. This ensures that the original list remains unchanged. The private <code>addItemToCopy(newItem)</code> method appends the new item to the end of the copied list. It creates a new list node with the new item and links it to the end of the copied list.</p>
<p>Then we have the <code>removeItem(itemToRemove)</code> method which is used to create a copy of the current list. It then searches for the specified item to remove, and returns the modified copy. It handles the case of removing an item from the beginning or middle of the list.</p>
<p>The <code>copyList()</code> method recursively traverses the current list, creating new list nodes in the copied list and linking them together. It ensures that the copied list accurately represents the original list.</p>
<p>This example demonstrates using the ShoppingList class to create a persistent shopping list. It starts with an initial list of "Milk", "Eggs", and "Bread", adds "Butter" and "Cheese", and then removes "Eggs".</p>
<p>Here is the result of our code:</p>
<p><img src="https://hackmd.io/_uploads/r1Uk3SNup.png" alt="Screen Shot 2024-01-04 at 5.16.10 PM" width="600" height="400" loading="lazy"></p>
<p><img src="https://hackmd.io/_uploads/Byf9CH4_p.png" alt="Screen Shot 2024-01-04 at 5.27.59 PM" width="600" height="400" loading="lazy">
<em>Persistent list Example</em></p>
<p>As you can see, despite updating the list, the original list still retains its original data and can be accessed.</p>
<h3 id="heading-persistent-maps-and-sets">Persistent Maps and Sets</h3>
<p>Persistent maps and sets retain their previous versions when modified, offering efficient key-value pair storage or unique value collections with immutability.<br>Let's consider this example:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TreasureChest</span> </span>{
  <span class="hljs-keyword">constructor</span>(contents = {}) {
    <span class="hljs-built_in">this</span>.contents = { ...contents };
  }

  addTreasure(key, value) {
    <span class="hljs-keyword">const</span> newContents = { ...this.contents, [key]: value };
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> TreasureChest(newContents);
  }
}

<span class="hljs-comment">// Creating a persistent treasure chest (map-like)</span>
<span class="hljs-keyword">const</span> originalChest = <span class="hljs-keyword">new</span> TreasureChest({ <span class="hljs-attr">gold</span>: <span class="hljs-number">100</span>, <span class="hljs-attr">gems</span>: <span class="hljs-number">5</span> });
<span class="hljs-keyword">const</span> updatedChest = originalChest.addTreasure(<span class="hljs-string">"diamonds"</span>, <span class="hljs-number">10</span>);

<span class="hljs-comment">// Logging the original and updated chests</span>
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Original Chest:"</span>);
<span class="hljs-built_in">console</span>.log(originalChest);

<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"\nUpdated Chest:"</span>);
<span class="hljs-built_in">console</span>.log(updatedChest);
</code></pre>
<p>Above, we have a real-world code example. I like to call her Treasure Chest. In the code, we define a class called <code>TreasureChest</code> to represent a treasure chest that holds various items. The constructor method initializes the treasure chest with an optional contents object, which represents the initial items in the chest. If no contents object is provided, an empty object is used.</p>
<p>The <code>addTreasure</code> method adds a new item to the treasure chest. It takes two arguments: the name of the item (<code>key</code>) and the quantity (<code>value</code>). It creates a new contents object by merging the current contents with a new entry for the specified item, ensuring that the new item is added without modifying the original contents. It then creates a new <code>TreasureChest</code> object using the updated contents and returns it.</p>
<p>From the code, you can see we created two instances for the <code>TreasureChest</code> class. First the <code>originalChest</code> and then the <code>updatedChest</code>. <code>originalChest</code> is initialized with { <code>gold: 100, gems: 5</code> }, representing a chest with 100 gold coins and 5 gems. <code>updatedChest</code> is created using <code>originalChest.addTreasure('diamonds', 10)</code>, adding 10 diamonds to the chest.  </p>
<p><img src="https://hackmd.io/_uploads/HyU7JIVua.png" alt="Screen Shot 2024-01-04 at 5.30.27 PM" width="600" height="400" loading="lazy">
<em>Persistent Maps and Sets Example</em></p>
<p>From our console, we can confirm that this immutability technique works as our original Chest value is retrieved.</p>
<h3 id="heading-persistent-trees">Persistent Trees</h3>
<p>Persistent trees, such as binary trees or trie structures, are yet another technique for achieving immutability. They maintain previous versions during operations like insertion, deletion, or search. They create new tree instances upon modifications while reusing unchanged parts from the original tree.</p>
<p>This preservation of versions enables efficient manipulation and retrieval of data without mutating the original tree. Aside from immutability, a persistent tree also has some key characteristics, such as shared structure and efficient modifications.</p>
<p>These two features let us share unchanged parts between versions of our tree and enable efficient operations (such as insertion, deletion, or traversal) by reusing existing nodes and creating new branches only when necessary, respectively.<br>Here is an example:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TreeNode</span> </span>{
  <span class="hljs-keyword">constructor</span>(value, left = null, right = null) {
    <span class="hljs-built_in">this</span>.value = value;
    <span class="hljs-built_in">this</span>.left = left;
    <span class="hljs-built_in">this</span>.right = right;
  }

  insert(newValue) {
    <span class="hljs-keyword">if</span> (newValue &lt; <span class="hljs-built_in">this</span>.value) {
      <span class="hljs-comment">// Insert to the left</span>
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> TreeNode(
        <span class="hljs-built_in">this</span>.value,
        <span class="hljs-built_in">this</span>.left ? <span class="hljs-built_in">this</span>.left.insert(newValue) : <span class="hljs-keyword">new</span> TreeNode(newValue),
        <span class="hljs-built_in">this</span>.right
      );
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-comment">// Insert to the right</span>
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> TreeNode(
        <span class="hljs-built_in">this</span>.value,
        <span class="hljs-built_in">this</span>.left,
        <span class="hljs-built_in">this</span>.right ? <span class="hljs-built_in">this</span>.right.insert(newValue) : <span class="hljs-keyword">new</span> TreeNode(newValue)
      );
    }
  }
}

<span class="hljs-comment">// Creating a simplified tree structure</span>
<span class="hljs-keyword">const</span> root = <span class="hljs-keyword">new</span> TreeNode(<span class="hljs-number">5</span>);
root.left = <span class="hljs-keyword">new</span> TreeNode(<span class="hljs-number">3</span>);
root.right = <span class="hljs-keyword">new</span> TreeNode(<span class="hljs-number">8</span>);

<span class="hljs-comment">// Logging the original tree</span>
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Original Tree:"</span>);
<span class="hljs-built_in">console</span>.log(root);

<span class="hljs-comment">// Creating an updated tree by inserting a new value (in this case, 10)</span>
<span class="hljs-keyword">const</span> updatedRoot = root.insert(<span class="hljs-number">10</span>);

<span class="hljs-comment">// Logging the updated tree</span>
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"\nUpdated Tree:"</span>);
<span class="hljs-built_in">console</span>.log(updatedRoot);
</code></pre>
<p>In the code above, we focused on creating and modifying a <code>TreeNode</code>. We defined a <code>TreeNode</code> class that represents a node in a binary tree. A binary tree is a hierarchical data structure where each node can have up to two child nodes, referred to as the left child and the right child.</p>
<p>Our <code>TreeNode</code> class has two constructors: the default constructor, which creates a node with the specified value, and two child nodes, which can also be null. The left and right properties represent the left and right child nodes, respectively.</p>
<p>The <code>TreeNode</code> class also has the insert method, which is used to insert a new value into the tree. The method takes a single argument, which is the value to be inserted.</p>
<p>The insert method first compares the new value to the value of the current node. If the new value is less than the value of the current node, it is inserted to the left of the current node. If the new value is greater than the value of the current node, it is inserted to the right of the current node.</p>
<p>If the current node does not have a child node corresponding to the insertion direction, a new node is created and inserted as the child node. If the current node already has a child node in that direction, the insertion method is recursively called on that child node to handle the insertion.</p>
<p>The code then creates a simplified tree structure with the root node having value 5, a left child node with value 3, and a right child node with value 8. Later on, we insert a new value of 10 into the tree using the insert method of the root node.</p>
<p><img src="https://hackmd.io/_uploads/SkQ2yLEOa.png" alt="Screen Shot 2024-01-04 at 5.32.43 PM" width="600" height="400" loading="lazy">
<em>Persistent Tree Example</em></p>
<h2 id="heading-how-to-use-es6-features-for-immutability-spread-syntax-and-objectfreeze">How to Use ES6 Features for Immutability – Spread Syntax and <code>Object.freeze()</code></h2>
<p>At the start of this tutorial, I explained the difference between mutable and immutable data types, and we saw some examples illustrating how they work. </p>
<p>Mutable data types such as arrays and objects are still super useful, and through the use of some ES6 features such as the spread operator and <code>Object.freeze</code>, we can utilize data types like arrays and objects while maintaining a degree of immutability.</p>
<h3 id="heading-the-spread-syntax">The Spread Syntax</h3>
<p>The <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax">spread operator (…)</a> in JavaScript is a powerful tool that facilitates the creation of shallow copies of arrays, objects, and iterables. When it comes to immutability, the spread operator plays a crucial role in ensuring that the original data remains unchanged while allowing the creation of new, independent data structures.</p>
<p>Let's consider an array in JavaScript:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> originalArray = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>];
</code></pre>
<p>Using the spread operator, you can create an immutable copy of this array:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> immutableCopy = [...originalArray];
</code></pre>
<p>Here, <code>immutableCopy</code> holds a new array with the same elements as <code>originalArray</code>. Any modifications to <code>immutableCopy</code> won't affect <code>originalArray</code>, ensuring the immutability of the original data. This technique has been adopted by a lot of developers in modern-day software development.</p>
<h3 id="heading-the-objectfreeze-method">The <code>Object.freeze()</code> method</h3>
<p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze"><code>Object.freeze()</code></a> is a method that can be used on reference data types. It prevents any form of modification or addition to an object, thereby creating an object with a read-only type.</p>
<p>Let's consider this example:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Creating an immutable object using Object.freeze()</span>
<span class="hljs-keyword">const</span> originalObj = { <span class="hljs-attr">name</span>: <span class="hljs-string">"Alice"</span>, <span class="hljs-attr">age</span>: <span class="hljs-number">25</span> };
<span class="hljs-keyword">const</span> immutableObj = <span class="hljs-built_in">Object</span>.freeze(originalObj); <span class="hljs-comment">// Freezes the object</span>

<span class="hljs-comment">// Attempting to modify a frozen object (in strict mode)</span>
immutableObj.age = <span class="hljs-number">30</span>; <span class="hljs-comment">// This change will not take effect (in strict mode)</span>
<span class="hljs-built_in">console</span>.log(originalObj);
<span class="hljs-built_in">console</span>.log(immutableObj);
</code></pre>
<p>In the code, the <code>originalObj</code> is a simple object with two properties: <code>name</code> and <code>age</code>. The <code>immutableObj</code> is created by calling <code>Object.freeze(originalObj)</code>. This effectively freezes the <code>originalObj</code>, preventing any further modifications to its properties.</p>
<p>We then try to code to modify the <code>immutableObj</code> by changing its age property to 30. However, since the object is frozen, this change will not take effect. In strict mode, attempting to modify a frozen object will result in a type error.</p>
<p><img src="https://hackmd.io/_uploads/r16WDjNvp.png" alt="Screen Shot 2023-12-23 at 8.29.17 PM" width="600" height="400" loading="lazy"></p>
<p>Without strict mode, the <code>immutableObj</code> object remains immutable.</p>
<p><img src="https://hackmd.io/_uploads/ByKYwjNwT.png" alt="Screen Shot 2023-12-23 at 8.30.34 PM" width="600" height="400" loading="lazy">
<em>Object.freeze for javaScript immutability.</em></p>
<h2 id="heading-performance-optimization-through-immutability">Performance Optimization through Immutability</h2>
<p>Immutability, unchangeability—you might wonder, does this cause more harm than good? Well, let's see.</p>
<p>Let's consider a few more steps in the process, like safeguarding your data. These steps are an investment in stability and predictability.</p>
<p>In mutable structures, whenever data changes, it involves copying or altering existing data, which can get messy, especially in large-scale applications.</p>
<p>When you're dealing with tons of data and constantly modifying it in a mutable structure, your code has to keep track of those changes. This constant tracking and altering can add up, leading to higher computational costs.</p>
<p>On the other hand, with immutable data, once created, it stays put. This means fewer surprises and less overhead in managing changes.</p>
<h3 id="heading-comparison-of-mutable-vs-immutable-data">Comparison of mutable vs. immutable data</h3>
<p>When comparing mutable and immutable data structures in large-scale applications, it's essential to highlight the fundamental differences that affect performance, memory usage, and overall stability.</p>
<p>Here's a breakdown in a more technical manner:</p>
<table><thead><tr><th><span>Aspect</span></th><th><span>Mutablility</span></th><th><span>Immutability</span></th></tr></thead><tbody><tr><td><span>Memory Management</span></td><td><span>Tend to require more memory due to in-place modifications.</span></td><td><span>Often use memory more efficiently by creating new instances.</span></td></tr><tr><td><span>Concurrent Access</span></td><td><span>Prone to issues with concurrent access (race conditions).</span></td><td><span>Safe for concurrent access as data doesn't change.</span></td></tr><tr><td><span>Error-Prone</span></td><td><span>Mutable states can lead to unintended bugs and errors.</span></td><td><span>Immutable structures minimize unintended side effects, reducing errors.</span></td></tr><tr><td><span>Ease of Reasoning and Debugging</span></td><td><span>Tracking changes can be complex, making debugging harder.</span></td><td><span>Easier to reason about as data remains consistent. Debugging is more straightforward.</span></td></tr><tr><td><span>Scalability</span></td><td><span>Can pose challenges in managing state changes at scale.</span></td><td><span>Facilitate scalability due to predictable and consistent data.</span></td></tr></tbody></table>

<p>As you can see from the table, there is a lot to benefit from adopting immutability.</p>
<h2 id="heading-real-world-examples-of-companies-and-projects-benefiting-from-immutability">Real-World Examples of Companies and Projects Benefiting from Immutability</h2>
<p>Immutability in programming is a concept that has several advantages, and several companies and projects have benefited from it. Some of the companies are Facebook, Github, Netflix, WhatsApp, and Uber.</p>
<p>React, a Facebook library, uses immutability as a major principle, providing improved performance and responsiveness. Github, a collaborative tool, also makes use of React in its user interface, thereby benefiting from immutability. Netflix, WhatsApp, and Uber also benefit from immutability by making use of immutable data structures to produce consistent and reliable applications.</p>
<p>These are a few of the many examples of companies benefiting from immutability.</p>
<h2 id="heading-common-pitfalls-of-immutable-javascript">Common Pitfalls of Immutable JavaScript</h2>
<p>Although immutability has its advantages, it can also be quite challenging if you don't use it properly. Most of the time, many beneficial things come with a cost.</p>
<p>Immutability may cause a decrease in performance if used on large datasets, because new data structures are created instead of modifying existing ones.<br>Also totally learning the immutable approach to programming can be quite tasking, since it may involve learning how to use new libraries and techniques. But it's definitely worth it.</p>
<p>Since immutability works hand in hand with functional programming, it's also a good idea for someone implementing immutability to learn functional programming.</p>
<h3 id="heading-data-races-and-concurrency-issues">Data races and concurrency issues</h3>
<p>When working with immutable data, data races and concurrency issues can still be potential issues. Some of these issues relate to shared reference, asynchronous operations, and state management.</p>
<p>While the data itself might be immutable, references to that data can still be shared among different parts of the code. And if multiple parts of the codebase hold references to the same immutable data and one of them attempts to modify it (even though it can't change the original data), it can cause unintended consequences or unexpected behavior elsewhere in the code that relies on the unchanged data.</p>
<p>In JavaScript, asynchronous operations can introduce concurrency issues. Even though JavaScript is single-threaded, asynchronous functions (like fetching data from APIs or handling events) can create scenarios where different parts of the code operate at different times. </p>
<p>If these asynchronous operations involve shared state, even with immutable data, managing the changes or handling the state updates can lead to unexpected behavior or bugs.</p>
<p>While immutability helps in maintaining predictable state changes, managing the state across an application can still be a challenge. Libraries like Redux in React applications, for instance, rely heavily on immutable data principles to manage state changes effectively. But improper handling of state updates or mutations within such libraries can lead to unexpected behavior.</p>
<h2 id="heading-best-practices-for-overcoming-immutability-related-issues">Best Practices for Overcoming Immutability-Related Issues</h2>
<p>Since immutability can pose some challenges, especially when used in a large-scale applications, it's a good idea to learn ways that we can overcome these challenges.</p>
<ul>
<li>Selective usage: To avoid decreased performance in your projects, you can choose where to use immutable data. Since immutability relies on creating new datasets instead of modifying them, you should use it in parts of your projects that do not require high performance.</li>
<li>Avoid deep cloning: Deep clones of data structures can be resource-intensive, so it's best to make use of shallow or partial updates if they can also provide the same results for you.</li>
<li>Implement Copy-on-Write strategy: The creation of new copies of data should only be used when you need data in a data structure to be modified. This can reduce copies that could be avoided, thereby increasing performance.</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The benefits of leveraging immutability in your applications are clear. Immutability serves as a powerful tool that can help you reduce bugs, improve responsiveness, and prevent unnecessary re-renders. </p>
<p>It may still pose some challenges, but through practicing the right strategies, you can handle them and use them to develop very high-quality as well as cost-effective applications.</p>
<p>Immutability remains an essential component of the optimization and responsiveness of large-scale applications being built in the JavaScript community, an area where JavaScript is still growing.</p>
<p>As a developer, learning to apply the immutable method of programming will go a long way toward optimizing most of your projects and reducing the occurrence of bugs and errors. Learning this method of programming would be an investment that yields long-term benefits.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
