<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Shahar Polak</title>
    <description>The latest articles on Forem by Shahar Polak (@polakshahar).</description>
    <link>https://forem.com/polakshahar</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F248515%2F37f9a64c-9d93-4870-b692-52162d9cac5c.jpg</url>
      <title>Forem: Shahar Polak</title>
      <link>https://forem.com/polakshahar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/polakshahar"/>
    <language>en</language>
    <item>
      <title>FastAPI, Furious Tests: The Need for Speed</title>
      <dc:creator>Shahar Polak</dc:creator>
      <pubDate>Sat, 13 Sep 2025 16:11:27 +0000</pubDate>
      <link>https://forem.com/polakshahar/fastapi-furious-tests-the-need-for-speed-11oi</link>
      <guid>https://forem.com/polakshahar/fastapi-furious-tests-the-need-for-speed-11oi</guid>
      <description>&lt;p&gt;&lt;em&gt;A no-nonsense guide to making your test suite so fast, it'll make The Flash jealous&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;by Shahar Polak&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR - The "I Just Want Results" Section
&lt;/h2&gt;

&lt;p&gt;Your CI is slow. Your developers are sad. Your coffee budget is through the roof because people are waiting 28 minutes for tests to run. Here's how we turned a sluggish test suite into a speed demon that finishes in under 3 minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Magic Formula:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SQLite in-memory for 95% of tests&lt;/li&gt;
&lt;li&gt;MySQL for the 5% that actually need it&lt;/li&gt;
&lt;li&gt;pytest-xdist with work stealing&lt;/li&gt;
&lt;li&gt;Proper fixtures (finally!)&lt;/li&gt;
&lt;li&gt;Split jobs by runtime, not count&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Skip to any section, but if you implement this wrong, don't blame us when your tests become flakier than a croissant factory.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who Are You? (And Why Should You Care?)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faqbd9y0pwkgjfcr7qrlm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faqbd9y0pwkgjfcr7qrlm.jpg" alt="Lego characters with engineering look" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;The Frustrated Developer:&lt;/strong&gt; You write a test, run the suite, grab coffee, check Twitter, contemplate life choices, and maybe your tests are done. Maybe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The DevOps Engineer:&lt;/strong&gt; Your CI bill makes your CFO weep, and developers complain about feedback loops longer than a soap opera plotline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Tech Lead:&lt;/strong&gt; You know there's a better way, but every "quick fix" turns into a three-week rabbit hole that breaks everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Project Manager:&lt;/strong&gt; You just want features shipped without the engineering team disappearing into "test optimization" black holes.&lt;/p&gt;

&lt;p&gt;If any of these sound familiar, buckle up. We're about to make your tests faster than your last relationship ended.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Problem: When Tests Become Slower Than Dial-Up Internet
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmpm9bnkihzivjhw4cuj1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmpm9bnkihzivjhw4cuj1.jpg" alt="The pain before optimization - with a slow hourglass" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
Picture this: You have 2,900 tests. Your CI takes 28 minutes to run them. That's roughly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1,680 seconds&lt;/strong&gt; of pure waiting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;27.5 minutes&lt;/strong&gt; longer than your attention span&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infinite sadness&lt;/strong&gt; for developers trying to iterate quickly&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  The Real Cost
&lt;/h3&gt;

&lt;p&gt;It's not just time. It's:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Developers skipping test runs locally (risky business)&lt;/li&gt;
&lt;li&gt;Context switching while waiting for CI (productivity killer)&lt;/li&gt;
&lt;li&gt;Delayed releases because nobody wants to wait for slow feedback&lt;/li&gt;
&lt;li&gt;Your AWS bill looking like a phone number&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  What We're Optimizing For
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Speed without stupidity:&lt;/strong&gt; Fast tests that still catch bugs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer happiness:&lt;/strong&gt; Quick feedback loops&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainability:&lt;/strong&gt; No Rube Goldberg test machines&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost efficiency:&lt;/strong&gt; Your CFO might even smile&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  The Hall of Failed Attempts (Learn From Our Pain)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Attempt #1: Dockerized MySQL for Everything
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;"Let's just use production-like everything!"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flfnhwj9nyy8zs8a8shpl.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flfnhwj9nyy8zs8a8shpl.jpg" alt="Dockerize everything" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;What we thought:&lt;/strong&gt; Production parity! MySQL everywhere! What could go wrong?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What actually happened:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Container startup: 10-15 seconds per test run&lt;/li&gt;
&lt;li&gt;Migration overhead: Another 5-10 seconds&lt;/li&gt;
&lt;li&gt;Parallel runs: Database collisions everywhere&lt;/li&gt;
&lt;li&gt;Result: Slower, flakier, sadder&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Lesson learned:&lt;/strong&gt; Production parity is great, but not for every single test.&lt;/p&gt;
&lt;h3&gt;
  
  
  Attempt #2: Shared Test Database
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;"One database to rule them all!"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What we thought:&lt;/strong&gt; Share a test database across all workers. Efficiency!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What actually happened:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data collisions like bumper cars&lt;/li&gt;
&lt;li&gt;Order-dependent tests (the worst kind)&lt;/li&gt;
&lt;li&gt;Cleanup between tests took forever&lt;/li&gt;
&lt;li&gt;Flakiness increased by approximately 1000%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Lesson learned:&lt;/strong&gt; Shared state is the root of all evil in testing.&lt;/p&gt;
&lt;h3&gt;
  
  
  Attempt #3: Self-Hosted AWS Runners
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;"We'll just throw more hardware at it!"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What we thought:&lt;/strong&gt; Bigger machines, faster tests, problem solved!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What actually happened:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setup complexity through the roof&lt;/li&gt;
&lt;li&gt;Security headaches&lt;/li&gt;
&lt;li&gt;Actually slower than managed runners&lt;/li&gt;
&lt;li&gt;Higher costs (ouch)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Lesson learned:&lt;/strong&gt; Sometimes the boring solution is the right solution.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Solution: SQLite in-Memory (The Chosen One)
&lt;/h2&gt;

&lt;p&gt;Here's the controversial take: &lt;strong&gt;You don't need MySQL for most of your tests.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The SQLite Advantage
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Speed:&lt;/strong&gt; In-memory databases are lightning fast&lt;br&gt;
&lt;strong&gt;Isolation:&lt;/strong&gt; Fresh database per test (or per worker)&lt;br&gt;
&lt;strong&gt;Simplicity:&lt;/strong&gt; No containers, no networking, no tears&lt;/p&gt;
&lt;h3&gt;
  
  
  But Wait, There's a Catch
&lt;/h3&gt;

&lt;p&gt;SQLite isn't MySQL. Shocking, we know. Here's what you need to handle:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyxltfxu2pz7h4w3bc11k.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyxltfxu2pz7h4w3bc11k.jpg" alt="There are edge cases" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  The 10 Commandments of SQLite Testing
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Foreign Keys:&lt;/strong&gt; SQLite doesn't enforce them by default. Add &lt;code&gt;?foreign_keys=1&lt;/code&gt; to your connection string or face the consequences.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Type Affinity:&lt;/strong&gt; SQLite is more forgiving with data types. Your integer column will happily store "banana" unless you use strict tables.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Auto Increment:&lt;/strong&gt; Different behavior from MySQL's AUTO_INCREMENT. Test carefully if you rely on specific ID sequences.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Concurrency:&lt;/strong&gt; SQLite serializes writes. MySQL doesn't. Keep some MySQL tests for concurrency edge cases.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Transaction Isolation:&lt;/strong&gt; Different default isolation levels. If your code depends on specific isolation behavior, test on MySQL.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DDL Migrations:&lt;/strong&gt; Limited ALTER TABLE support in SQLite. Test your migrations on the real database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Date/Time Handling:&lt;/strong&gt; Different timezone and format handling. Verify timestamp-sensitive logic on MySQL.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;JSON Functions:&lt;/strong&gt; SQLite's JSON support differs from MySQL's native JSON type and functions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Collations:&lt;/strong&gt; Case sensitivity and sorting behavior can differ. Test locale-specific queries on MySQL.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Database Engine Features:&lt;/strong&gt; If you use stored procedures, triggers, or MySQL-specific functions, you need the real deal.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  The Implementation: Code That Actually Works
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Project Structure (The Foundation)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;your-awesome-project/
├── app/
│   ├── __init__.py
│   ├── main.py              # FastAPI app factory
│   ├── db.py                # Database configuration
│   ├── models/              # Your beautiful models
│   └── routers/             # API routes
├── tests/
│   ├── unit/                # Pure logic, no I/O
│   ├── component/           # FastAPI + SQLite tests
│   ├── e2e/                 # The "real deal" MySQL tests
│   └── conftest.py          # Fixture magic happens here
├── pytest.ini              # Configuration central
└── requirements-dev.txt     # All the testing goodies
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  The Database Setup (app/db.py)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/db.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;contextlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asynccontextmanager&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy.ext.asyncio&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AsyncSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;async_sessionmaker&lt;/span&gt;

&lt;span class="c1"&gt;# Global sessionmaker (we'll configure this in tests)
&lt;/span&gt;&lt;span class="n"&gt;SessionLocal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;async_sessionmaker&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;AsyncSession&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;configure_sessionmaker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sessionmaker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;async_sessionmaker&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;AsyncSession&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Configure the global sessionmaker. Call this in your app startup and test fixtures.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;SessionLocal&lt;/span&gt;
    &lt;span class="n"&gt;SessionLocal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sessionmaker&lt;/span&gt;

&lt;span class="nd"&gt;@asynccontextmanager&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_db&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;AsyncSession&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;FastAPI dependency for database sessions.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;SessionLocal&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SessionLocal not configured. Did you forget to call configure_sessionmaker?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;SessionLocal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  The FastAPI App (app/main.py)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/main.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Depends&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy.ext.asyncio&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AsyncSession&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_db&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;  &lt;span class="c1"&gt;# Your SQLModel models
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_app&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;App factory pattern. Makes testing easier and dependency injection cleaner.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Lightning Fast API&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/healthz&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;health_check&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The most important endpoint. If this is broken, everything is broken.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Still alive!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AsyncSession&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Create a user. Revolutionary stuff.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;

    &lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/users/{user_id}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AsyncSession&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_db&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Get a user by ID. Also revolutionary.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User not found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  The Test Configuration (pytest.ini)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[pytest]&lt;/span&gt;
&lt;span class="py"&gt;addopts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;-ra -q -m "not e2e"&lt;/span&gt;
&lt;span class="py"&gt;testpaths&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;tests&lt;/span&gt;
&lt;span class="py"&gt;markers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="err"&gt;unit:&lt;/span&gt; &lt;span class="err"&gt;Pure&lt;/span&gt; &lt;span class="err"&gt;logic&lt;/span&gt; &lt;span class="err"&gt;tests,&lt;/span&gt; &lt;span class="err"&gt;no&lt;/span&gt; &lt;span class="err"&gt;I/O,&lt;/span&gt; &lt;span class="err"&gt;fast&lt;/span&gt; &lt;span class="err"&gt;as&lt;/span&gt; &lt;span class="err"&gt;lightning&lt;/span&gt;
    &lt;span class="err"&gt;component:&lt;/span&gt; &lt;span class="err"&gt;FastAPI&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="err"&gt;SQLite&lt;/span&gt; &lt;span class="err"&gt;tests,&lt;/span&gt; &lt;span class="err"&gt;reasonably&lt;/span&gt; &lt;span class="err"&gt;fast&lt;/span&gt;
    &lt;span class="err"&gt;e2e:&lt;/span&gt; &lt;span class="err"&gt;Full&lt;/span&gt; &lt;span class="err"&gt;stack&lt;/span&gt; &lt;span class="err"&gt;with&lt;/span&gt; &lt;span class="err"&gt;MySQL,&lt;/span&gt; &lt;span class="err"&gt;use&lt;/span&gt; &lt;span class="err"&gt;sparingly&lt;/span&gt;
    &lt;span class="err"&gt;slow:&lt;/span&gt; &lt;span class="err"&gt;Tests&lt;/span&gt; &lt;span class="err"&gt;that&lt;/span&gt; &lt;span class="err"&gt;take&lt;/span&gt; &lt;span class="err"&gt;&amp;gt;1&lt;/span&gt; &lt;span class="err"&gt;second&lt;/span&gt; &lt;span class="err"&gt;(we're&lt;/span&gt; &lt;span class="err"&gt;watching&lt;/span&gt; &lt;span class="err"&gt;you)&lt;/span&gt;
&lt;span class="py"&gt;asyncio_mode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;auto&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  The Fixture Magic (tests/conftest.py)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tests/conftest.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AsyncClient&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlmodel&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SQLModel&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy.ext.asyncio&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_async_engine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AsyncSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;async_sessionmaker&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.main&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_app&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;configure_sessionmaker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_db&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;worker_id&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Get the pytest-xdist worker ID. Each worker gets its own database file.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PYTEST_XDIST_WORKER&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gw0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worker_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Create a SQLite engine per worker. 

    Per-worker file databases are more reliable than :memory: for parallel testing.
    Each worker gets its own file, avoiding connection sharing issues.

    CRITICAL: foreign_keys=1 in connection string enables FK for ALL connections.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;db_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./test_db_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;worker_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.sqlite3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="c1"&gt;# FIXED: foreign_keys=1 in connection string, not per-connection pragma
&lt;/span&gt;    &lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_async_engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sqlite+aiosqlite:///&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;db_file&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;?foreign_keys=1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SQLModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_all&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;

    &lt;span class="c1"&gt;# Cleanup
&lt;/span&gt;    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;FileNotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;  &lt;span class="c1"&gt;# Already deleted or never created
&lt;/span&gt;
&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sessionmaker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Create a sessionmaker factory.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;async_sessionmaker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;AsyncSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expire_on_commit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;autouse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_configure_sessionmaker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sessionmaker&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Auto-configure the global sessionmaker for all tests.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="nf"&gt;configure_sessionmaker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sessionmaker&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sessionmaker&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Create a FastAPI app with test database dependency override.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_app&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Override the database dependency
&lt;/span&gt;    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_test_db&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;sessionmaker&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;

    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dependency_overrides&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;get_db&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_test_db&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;HTTP client for testing FastAPI endpoints.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://testserver&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;

&lt;span class="c1"&gt;# E2E fixtures for MySQL testing
&lt;/span&gt;&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mysql_url&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;MySQL connection URL for e2e tests.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TEST_MYSQL_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mysql+asyncmy://root:test@localhost:3306/testdb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_e2e&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pytestconfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Check if e2e tests are selected.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;selected_markers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pytestconfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getoption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-m&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;e2e&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;selected_markers&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mysql_engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_e2e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mysql_url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;MySQL engine for e2e tests only.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;run_e2e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;e2e tests not selected&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_async_engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mysql_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Run migrations or create tables here
&lt;/span&gt;        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SQLModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_all&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;e2e_app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mysql_engine&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;FastAPI app configured with MySQL for e2e tests.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# FIXED: Use existing configure_sessionmaker function consistently
&lt;/span&gt;    &lt;span class="n"&gt;mysql_sessionmaker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;async_sessionmaker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mysql_engine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;AsyncSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expire_on_commit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;configure_sessionmaker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mysql_sessionmaker&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_app&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Test Data Factories (tests/factories.py)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tests/factories.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;faker&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Faker&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;

&lt;span class="c1"&gt;# Deterministic fake data for consistent tests
&lt;/span&gt;&lt;span class="n"&gt;fake&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Faker&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;Faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1337&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Factory&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;

    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LazyAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LazyAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="c1"&gt;# Helper for creating database entities
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;contextlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asynccontextmanager&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy.ext.asyncio&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AsyncSession&lt;/span&gt;

&lt;span class="nd"&gt;@asynccontextmanager&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_entity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AsyncSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Create and persist an entity, yielding it for use in tests.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Get the ID without committing
&lt;/span&gt;    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt;
    &lt;span class="c1"&gt;# Cleanup happens automatically when session ends
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Sample Tests That Actually Work
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tests/unit/test_math.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.mark.unit&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_addition_still_works&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The most important test. If this fails, we have bigger problems.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.mark.unit&lt;/span&gt;  
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_our_business_logic&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Test your actual business logic here.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;calculate_something_important&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;calculate_something_important&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The answer to everything&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tests/component/test_users.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;tests.factories&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;UserFactory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;create_entity&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.mark.component&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_create_user_endpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Test user creation through the API.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test@example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Test User&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test@example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Test User&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.mark.component&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_user_endpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sessionmaker&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Test getting a user by ID.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# Create a user in the database
&lt;/span&gt;    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;sessionmaker&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;create_entity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UserFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

            &lt;span class="c1"&gt;# Test the endpoint
&lt;/span&gt;            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/users/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;

            &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;
            &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.mark.component&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_nonexistent_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Test 404 behavior for missing users.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/users/99999&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;not found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;detail&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tests/e2e/test_mysql_specifics.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.mark.e2e&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_foreign_key_constraints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e2e_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mysql_sessionmaker&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Test that foreign key constraints work properly on MySQL.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# This test would fail on SQLite without proper FK setup
&lt;/span&gt;    &lt;span class="c1"&gt;# but should work correctly on MySQL
&lt;/span&gt;    &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.mark.e2e&lt;/span&gt;  
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_transaction_isolation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e2e_client&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Test MySQL-specific transaction behavior.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# Test concurrent access patterns that behave differently on MySQL
&lt;/span&gt;    &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.mark.e2e&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_json_queries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e2e_client&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Test MySQL JSON functions that SQLite doesn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t support.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Parallelization: pytest-xdist Wizardry
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpo2slhwde0v42ha1i1eo.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpo2slhwde0v42ha1i1eo.jpg" alt="xdist python for tests" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Basic Parallelization
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start with this&lt;/span&gt;
pytest &lt;span class="nt"&gt;-n&lt;/span&gt; auto

&lt;span class="c"&gt;# Graduate to this&lt;/span&gt;
pytest &lt;span class="nt"&gt;-n&lt;/span&gt; logical &lt;span class="nt"&gt;--dist&lt;/span&gt; worksteal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Why &lt;code&gt;--dist worksteal&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh0dyg8o7xd1g9lx0cgsn.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh0dyg8o7xd1g9lx0cgsn.jpg" alt="Workers working together" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
When some tests are slower than others (looking at you, that one test that takes 30 seconds), work stealing lets idle workers grab tasks from busy workers. It's like having helpful coworkers instead of the ones who disappear when there's work to do.&lt;/p&gt;
&lt;h3&gt;
  
  
  Avoiding Parallel Test Hell
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The Golden Rules:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No shared state between tests:&lt;/strong&gt; Each test should be an island&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use unique file paths:&lt;/strong&gt; &lt;code&gt;tmp_path&lt;/code&gt; fixture or worker-specific names&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean up globals:&lt;/strong&gt; Reset singletons or make them worker-aware&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic test data:&lt;/strong&gt; Seed your faker, control your randomness&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Ensuring Test Independence
&lt;/h3&gt;

&lt;p&gt;Instead of relying on randomization to catch order dependencies:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use proper fixtures&lt;/strong&gt; for test isolation (per-worker databases, clean state)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pytest-xdist parallel execution&lt;/strong&gt; will reveal most order issues naturally&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Careful test design&lt;/strong&gt; prevents dependencies from forming in the first place&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If tests pass individually but fail in parallel, check for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Global variables being modified&lt;/li&gt;
&lt;li&gt;File system conflicts (use &lt;code&gt;tmp_path&lt;/code&gt; fixture)&lt;/li&gt;
&lt;li&gt;Database record pollution between tests&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Debugging Parallel Failures
&lt;/h3&gt;

&lt;p&gt;When tests work individually but fail in parallel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Reproduce the failure&lt;/span&gt;
pytest &lt;span class="nt"&gt;-n&lt;/span&gt; 0 &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="s2"&gt;"failing_test_name"&lt;/span&gt; &lt;span class="nt"&gt;-vv&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;

&lt;span class="c"&gt;# Check for shared state&lt;/span&gt;
&lt;span class="c"&gt;# Look for global variables, file system conflicts, or database sharing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The CI Configuration: GitHub Actions That Don't Suck
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Fast Lane (SQLite Tests)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .github/workflows/tests.yml&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Tests That Actually Finish&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;concurrency&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ci-${{ github.ref }}&lt;/span&gt;
  &lt;span class="na"&gt;cancel-in-progress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;fast-lane&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Fast Tests (SQLite)&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;fail-fast&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
        &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;3&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;4&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# Split into 4 parallel jobs&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt;  &lt;span class="c1"&gt;# If it takes longer, something's wrong&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Python&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@v5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.12'&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pip'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;pip install -r requirements-dev.txt&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run fast tests&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;PYTEST_SPLIT_FILE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.pytest-split-durations.json&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;pytest -m "not e2e" \&lt;/span&gt;
                 &lt;span class="s"&gt;--splits 4 --group ${{ matrix.group }} \&lt;/span&gt;
                 &lt;span class="s"&gt;-n logical --dist worksteal \&lt;/span&gt;
                 &lt;span class="s"&gt;--alluredir=allure-results/${{ matrix.group }} \&lt;/span&gt;
                 &lt;span class="s"&gt;--durations=25&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload test results&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-artifact@v4&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always()&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;allure-fast-${{ matrix.group }}&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;allure-results/${{ matrix.group }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The E2E Lane (MySQL Tests)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;e2e-tests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;E2E Tests (MySQL)&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;fast-lane&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# Only run if fast tests pass&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;

    &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;mysql&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql:8.0&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
          &lt;span class="na"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;testdb&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;3306:3306&lt;/span&gt;
        &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
          &lt;span class="s"&gt;--health-cmd="mysqladmin ping -h 127.0.0.1 -ptest"&lt;/span&gt;
          &lt;span class="s"&gt;--health-interval=5s&lt;/span&gt;
          &lt;span class="s"&gt;--health-timeout=2s&lt;/span&gt;
          &lt;span class="s"&gt;--health-retries=20&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Python&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@v5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.12'&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pip'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pip install -r requirements-dev.txt&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Wait for MySQL&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;for i in {1..30}; do&lt;/span&gt;
            &lt;span class="s"&gt;mysqladmin ping -h 127.0.0.1 -ptest &amp;amp;&amp;amp; break&lt;/span&gt;
            &lt;span class="s"&gt;sleep 2&lt;/span&gt;
          &lt;span class="s"&gt;done&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run E2E tests&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;TEST_MYSQL_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mysql+asyncmy://root:test@127.0.0.1:3306/testdb"&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;pytest -m e2e \&lt;/span&gt;
                 &lt;span class="s"&gt;-n 0 \&lt;/span&gt;
                 &lt;span class="s"&gt;--alluredir=allure-results/e2e \&lt;/span&gt;
                 &lt;span class="s"&gt;--durations=25&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload E2E results&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-artifact@v4&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always()&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;allure-e2e&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;allure-results/e2e&lt;/span&gt;

&lt;span class="c1"&gt;### Taking CI Speed Even Further: Blacksmith.sh&lt;/span&gt;
&lt;span class="pi"&gt;&amp;gt;&lt;/span&gt; &lt;span class="err"&gt;No&lt;/span&gt; &lt;span class="err"&gt;they&lt;/span&gt; &lt;span class="err"&gt;do&lt;/span&gt; &lt;span class="err"&gt;not&lt;/span&gt; &lt;span class="err"&gt;pay&lt;/span&gt; &lt;span class="err"&gt;me&lt;/span&gt; &lt;span class="err"&gt;to&lt;/span&gt; &lt;span class="err"&gt;say&lt;/span&gt; &lt;span class="err"&gt;that,&lt;/span&gt; &lt;span class="err"&gt;I&lt;/span&gt; &lt;span class="err"&gt;just&lt;/span&gt; &lt;span class="err"&gt;truly&lt;/span&gt; &lt;span class="err"&gt;love&lt;/span&gt; &lt;span class="err"&gt;them&lt;/span&gt; &lt;span class="err"&gt;🙏&lt;/span&gt;

&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="s"&gt;[ ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l3p03np6zz2xqvd8ibbd.jpg)&lt;/span&gt;
&lt;span class="err"&gt;A&lt;/span&gt;&lt;span class="s"&gt;fter optimizing our tests, we discovered our CI runners were the next bottleneck. GitHub Actions runners, while convenient, aren't optimized for compute-heavy workloads like parallel test execution.&lt;/span&gt;

&lt;span class="err"&gt;W&lt;/span&gt;&lt;span class="s"&gt;e migrated to [Blacksmith.sh](https://blacksmith.sh) for our CI infrastructure and saw additional improvements:&lt;/span&gt;

&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="s"&gt;*Performance gains:**&lt;/span&gt;
&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="s"&gt; **2-3x faster** build times due to dedicated bare-metal runners&lt;/span&gt;
&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="s"&gt; **More predictable** performance (no noisy neighbor problems)&lt;/span&gt;
&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="s"&gt; **Better parallelization** support with higher core counts&lt;/span&gt;

&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="s"&gt;*Cost savings:**&lt;/span&gt;
&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="s"&gt; **40-60% reduction** in CI costs compared to GitHub Actions&lt;/span&gt;
&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="s"&gt; Pay only for actual usage, not idle time&lt;/span&gt;
&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="s"&gt; Better price/performance ratio for compute-intensive workflows&lt;/span&gt;

&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="s"&gt;*Migration effort:**&lt;/span&gt;
&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="s"&gt; **1-3 lines of code** changes per repository&lt;/span&gt;
&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="s"&gt; Minimal disruption to existing workflows&lt;/span&gt;
&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="s"&gt; Same GitHub Actions syntax, just different runners&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
yaml&lt;/p&gt;
&lt;h1&gt;
  
  
  Before (GitHub Actions)
&lt;/h1&gt;

&lt;p&gt;runs-on: ubuntu-latest&lt;/p&gt;
&lt;h1&gt;
  
  
  After (Blacksmith)
&lt;/h1&gt;

&lt;p&gt;runs-on: blacksmith-4vcpu-ubuntu-2204&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
The combination of optimized tests + faster runners reduced our total CI time from 28 minutes to under 2 minutes, while cutting costs by over 70%.

**When to consider dedicated CI infrastructure:**
- High-frequency builds (multiple times per hour)
- Compute-intensive test suites (parallel execution, compilation)
- Cost-sensitive environments where CI bills are significant
- Teams that value predictable, fast feedback loops
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Smart Test Splitting with pytest-split
&lt;/h2&gt;

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

&lt;p&gt;Most people split tests by count: "Run tests 1-100 on worker 1, 101-200 on worker 2." This is like dividing a pizza by number of slices instead of size - you might get one tiny slice and one massive one.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem with Count-Based Splitting
&lt;/h3&gt;

&lt;p&gt;When you have tests that take vastly different amounts of time (some finish in 0.1s, others take 30s), splitting by count leads to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Job 1: 100 fast tests (finishes in 2 minutes)&lt;/li&gt;
&lt;li&gt;Job 2: 100 tests including 3 slow ones (finishes in 15 minutes)&lt;/li&gt;
&lt;li&gt;Your CI is only as fast as the slowest job&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The pytest-split Solution
&lt;/h3&gt;

&lt;p&gt;pytest-split distributes tests based on &lt;strong&gt;actual runtime data&lt;/strong&gt;, ensuring each CI job takes roughly the same total time.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1: Generate Timing Data
&lt;/h4&gt;

&lt;p&gt;First, collect timing data from your test suite:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run this once to generate timing data&lt;/span&gt;
pytest &lt;span class="nt"&gt;--store-durations&lt;/span&gt; &lt;span class="nt"&gt;--durations-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;.pytest-durations.json &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"not e2e"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a &lt;code&gt;.pytest-durations.json&lt;/code&gt; file with historical timing data for each test.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2: Use Runtime-Based Splitting in CI
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Split tests into 4 groups based on runtime, not count&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PYTEST_SPLIT_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;.pytest-durations.json

&lt;span class="c"&gt;# Each group gets ~25% of total runtime&lt;/span&gt;
pytest &lt;span class="nt"&gt;--splits&lt;/span&gt; 4 &lt;span class="nt"&gt;--group&lt;/span&gt; 1 &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"not e2e"&lt;/span&gt;  &lt;span class="c"&gt;# Group 1&lt;/span&gt;
pytest &lt;span class="nt"&gt;--splits&lt;/span&gt; 4 &lt;span class="nt"&gt;--group&lt;/span&gt; 2 &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"not e2e"&lt;/span&gt;  &lt;span class="c"&gt;# Group 2&lt;/span&gt;
pytest &lt;span class="nt"&gt;--splits&lt;/span&gt; 4 &lt;span class="nt"&gt;--group&lt;/span&gt; 3 &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"not e2e"&lt;/span&gt;  &lt;span class="c"&gt;# Group 3&lt;/span&gt;
pytest &lt;span class="nt"&gt;--splits&lt;/span&gt; 4 &lt;span class="nt"&gt;--group&lt;/span&gt; 4 &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"not e2e"&lt;/span&gt;  &lt;span class="c"&gt;# Group 4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  When to Use pytest-split
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use pytest-split when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your tests have highly variable runtimes (some 0.1s, others 10s+)&lt;/li&gt;
&lt;li&gt;You're running multiple CI jobs and jobs finish at very different times&lt;/li&gt;
&lt;li&gt;You have a large test suite (1000+ tests) where timing distribution matters&lt;/li&gt;
&lt;li&gt;You want predictable CI job completion times&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Skip pytest-split when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your tests have relatively uniform runtime (all finish within 1-2 seconds)&lt;/li&gt;
&lt;li&gt;You're only running one CI job&lt;/li&gt;
&lt;li&gt;Your test suite is small (&amp;lt;200 tests)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--dist worksteal&lt;/code&gt; already balances your workload well&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Setting Up pytest-split for Your Project
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Install pytest-split:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;pytest-split
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Generate initial timing data:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run your full test suite once to collect timings&lt;/span&gt;
pytest &lt;span class="nt"&gt;--store-durations&lt;/span&gt; &lt;span class="nt"&gt;--durations-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;.pytest-durations.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Update your CI configuration:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run fast tests&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;PYTEST_SPLIT_FILE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.pytest-durations.json&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;pytest -m "not e2e" \&lt;/span&gt;
           &lt;span class="s"&gt;--splits 4 --group ${{ matrix.group }} \&lt;/span&gt;
           &lt;span class="s"&gt;-n logical --dist worksteal \&lt;/span&gt;
           &lt;span class="s"&gt;--durations=25&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Keep timing data updated:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Regenerate timing data weekly or when adding many new tests&lt;/span&gt;
pytest &lt;span class="nt"&gt;--store-durations&lt;/span&gt; &lt;span class="nt"&gt;--durations-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;.pytest-durations.json &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"not e2e"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  pytest-split Best Practices
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Timing Data Maintenance:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Regenerate timing data when you add/remove many tests&lt;/li&gt;
&lt;li&gt;Update weekly for active projects, monthly for stable ones&lt;/li&gt;
&lt;li&gt;Store the &lt;code&gt;.pytest-durations.json&lt;/code&gt; file in your repo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Debugging Imbalanced Jobs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check timing data: &lt;code&gt;cat .pytest-durations.json | head -20&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Look for tests without timing data (new tests default to 0 seconds)&lt;/li&gt;
&lt;li&gt;Verify your job matrix splits match your &lt;code&gt;--splits&lt;/code&gt; parameter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Combining with worksteal:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Best of both worlds: balanced groups + work stealing within groups&lt;/span&gt;
pytest &lt;span class="nt"&gt;--splits&lt;/span&gt; 4 &lt;span class="nt"&gt;--group&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="p"&gt;{ matrix.group &lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; logical &lt;span class="nt"&gt;--dist&lt;/span&gt; worksteal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures each CI job gets a balanced workload, and within each job, workers can steal work from each other when they finish early.&lt;/p&gt;




&lt;h2&gt;
  
  
  Memory Management and Cleanup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Memory Guidelines
&lt;/h3&gt;

&lt;p&gt;With 8-core runners and 32GB RAM, your test suite should rarely exceed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;4-6 GB RSS&lt;/strong&gt; during parallel execution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fail the build&lt;/strong&gt; if you hit 8GB+ (something's leaking)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Monitoring Memory Usage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Add this to your measurement script&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in &lt;/span&gt;1 2 3 4 5&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  /usr/bin/time &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"cpu=%P rss=%Mkb elapsed=%E"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  pytest &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"not e2e"&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; logical &lt;span class="nt"&gt;--dist&lt;/span&gt; worksteal &lt;span class="se"&gt;\&lt;/span&gt;
         &lt;span class="nt"&gt;--durations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;25 &lt;span class="nt"&gt;-q&lt;/span&gt; | &lt;span class="nb"&gt;tee &lt;/span&gt;reports/run_&lt;span class="nv"&gt;$i&lt;/span&gt;.log
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SQLite File Cleanup
&lt;/h3&gt;

&lt;p&gt;The per-worker SQLite files need cleanup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In your engine fixture
&lt;/span&gt;&lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Clean up the database file
&lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./test_db_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;worker_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.sqlite3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;FileNotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;  &lt;span class="c1"&gt;# Already gone
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Measurement Protocol (No More Hand-Wavy Claims)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Hardware Baseline
&lt;/h3&gt;

&lt;p&gt;Record this info and include it in your repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Test Environment Specifications&lt;/span&gt;

&lt;span class="gs"&gt;**Local Development:**&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; MacBook Pro M2 Max, 12-core CPU, 32GB RAM
&lt;span class="p"&gt;-&lt;/span&gt; macOS 14.2, Python 3.12

&lt;span class="gs"&gt;**CI Environment:**&lt;/span&gt;  
&lt;span class="p"&gt;-&lt;/span&gt; GitHub Actions ubuntu-latest
&lt;span class="p"&gt;-&lt;/span&gt; 4-core x86_64, 16GB RAM, SSD storage
&lt;span class="p"&gt;-&lt;/span&gt; Python 3.12, pytest 8.x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The 5-Run Protocol
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# scripts/measure_performance.sh&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Test Performance Measurement ==="&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Hardware: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Python: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;python &lt;span class="nt"&gt;--version&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Cores: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;nproc&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"RAM: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;free &lt;span class="nt"&gt;-h&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'^Mem:'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;

&lt;span class="c"&gt;# Warm up (don't count this run)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Warming up..."&lt;/span&gt;
pytest &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"not e2e"&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; logical &lt;span class="nt"&gt;--dist&lt;/span&gt; worksteal &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null

&lt;span class="c"&gt;# Measure 5 runs&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Measuring performance across 5 runs..."&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;1..5&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Run &lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;/5"&lt;/span&gt;
    /usr/bin/time &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"run_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: cpu=%P rss=%Mkb elapsed=%E"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    pytest &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"not e2e"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
           &lt;span class="nt"&gt;-n&lt;/span&gt; logical &lt;span class="nt"&gt;--dist&lt;/span&gt; worksteal &lt;span class="se"&gt;\&lt;/span&gt;
           &lt;span class="nt"&gt;--durations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;25 &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
           &lt;span class="nt"&gt;--alluredir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;allure-results/perf_run_&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
           2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;tee &lt;/span&gt;reports/perf_run_&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.log
&lt;span class="k"&gt;done

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Results Summary ==="&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"elapsed="&lt;/span&gt; reports/perf_run_&lt;span class="k"&gt;*&lt;/span&gt;.log | &lt;span class="nb"&gt;sort&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Results Table Format
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Always include these details with any performance claims:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Performance Results&lt;/span&gt;

&lt;span class="gs"&gt;**Test Environment:**&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Hardware: [Your specific hardware]
&lt;span class="p"&gt;-&lt;/span&gt; OS: [Specific version]
&lt;span class="p"&gt;-&lt;/span&gt; Python: [Version]
&lt;span class="p"&gt;-&lt;/span&gt; Test count: [Total number] ([breakdown by type])

&lt;span class="gs"&gt;**Before:**&lt;/span&gt; [Describe the baseline setup]
&lt;span class="gs"&gt;**After:**&lt;/span&gt; [Describe the optimized setup]

| Metric                    | Before    | After   | Notes |
|---------------------------|-----------|---------|--------|
| Median wall time (5 runs) | 28m 34s  | 3m 47s  | Measured with &lt;span class="sb"&gt;`/usr/bin/time`&lt;/span&gt; |
| P95 wall time             | 31m 12s  | 4m 15s  | Worst case scenario |
| Memory peak (RSS)         | 8.2 GB   | 4.1 GB  | Peak during parallel execution |
| Test composition          | 100% MySQL | 93% SQLite, 7% MySQL | [breakdown] |

&lt;span class="gs"&gt;**Methodology:**&lt;/span&gt; 5 consecutive runs, median reported, hardware specs documented above.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Red Flags to Avoid:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claims without specific hardware mentioned&lt;/li&gt;
&lt;li&gt;No test composition breakdown
&lt;/li&gt;
&lt;li&gt;Hand-wavy estimates ("~9x faster")&lt;/li&gt;
&lt;li&gt;Missing measurement methodology&lt;/li&gt;
&lt;li&gt;Comparing different hardware/environments&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Common Pitfalls (Learn From Others' Mistakes)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The "It Works on My Machine" Special
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Tests pass locally but fail in CI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usually caused by:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Different database versions&lt;/li&gt;
&lt;li&gt;Timezone differences
&lt;/li&gt;
&lt;li&gt;File path separators (Windows vs. Unix)&lt;/li&gt;
&lt;li&gt;Environment variables not set in CI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Make your test environment configuration explicit and reproducible.&lt;/p&gt;

&lt;h3&gt;
  
  
  The "Shared State Surprise"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Tests pass individually but fail when run together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usually caused by:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Global variables being modified&lt;/li&gt;
&lt;li&gt;Database records not cleaned up&lt;/li&gt;
&lt;li&gt;File system state persisting&lt;/li&gt;
&lt;li&gt;Module-level imports with side effects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Embrace test isolation like it's your religion.&lt;/p&gt;

&lt;h3&gt;
  
  
  The "Foreign Key Forgotten"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Tests pass but production breaks due to referential integrity violations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usually caused by:&lt;/strong&gt; SQLite's &lt;code&gt;PRAGMA foreign_keys=OFF&lt;/code&gt; default behavior, or incorrectly setting FK pragma per connection instead of in the connection string.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Use &lt;code&gt;?foreign_keys=1&lt;/code&gt; in your SQLite connection string, not per-connection pragma statements.&lt;/p&gt;

&lt;h3&gt;
  
  
  The "Performance Regression Creep"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Tests gradually get slower over time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usually caused by:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New tests being added without performance consideration&lt;/li&gt;
&lt;li&gt;Test data growing without bounds&lt;/li&gt;
&lt;li&gt;Dependencies being added carelessly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Monitor your test durations and fail builds when they exceed thresholds.&lt;/p&gt;

&lt;h3&gt;
  
  
  The "False Confidence Trap"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Fast tests give you confidence, but they don't catch real bugs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usually caused by:&lt;/strong&gt; Mocking too much or using overly simplified test scenarios.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Balance fast feedback with meaningful coverage. Keep that MySQL test suite!&lt;/p&gt;




&lt;h2&gt;
  
  
  The Incremental Migration Plan
&lt;/h2&gt;

&lt;p&gt;Don't try to do everything at once. You'll break things, blame this guide, and we'll both be sad.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1: Measure Everything
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Implement the 5-run measurement protocol&lt;/li&gt;
&lt;li&gt;Document current performance&lt;/li&gt;
&lt;li&gt;Identify the 10 slowest tests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Success criteria:&lt;/strong&gt; You have baseline numbers you can defend.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 2: Fix the Fixtures
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Implement proper test database isolation&lt;/li&gt;
&lt;li&gt;Add the session-scoped engine fixture&lt;/li&gt;
&lt;li&gt;Keep using MySQL initially&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Success criteria:&lt;/strong&gt; Tests are isolated but not necessarily faster yet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 3: Switch to SQLite
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Convert component tests to use SQLite fixtures&lt;/li&gt;
&lt;li&gt;Keep a small MySQL suite for edge cases&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;?foreign_keys=1&lt;/code&gt; to connection string&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Success criteria:&lt;/strong&gt; Most tests use SQLite, MySQL suite is &amp;lt;5% of total.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 4: Enable Parallelization
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Start with &lt;code&gt;pytest -n auto&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Fix any parallel test failures&lt;/li&gt;
&lt;li&gt;Verify test isolation with proper fixtures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Success criteria:&lt;/strong&gt; All tests pass consistently with &lt;code&gt;-n auto&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 5: Consider Runtime-Based Splitting (Optional)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;First try:&lt;/strong&gt; &lt;code&gt;--dist worksteal&lt;/code&gt; to see if it resolves job imbalance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If needed:&lt;/strong&gt; Implement manual category-based job splitting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced:&lt;/strong&gt; Add &lt;code&gt;pytest-split&lt;/code&gt; only if you have stable timing patterns and maintenance capacity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Success criteria:&lt;/strong&gt; CI jobs finish within 2-3 minutes of each other, no job consistently much slower.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 6: Polish and Monitor
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Add memory monitoring&lt;/li&gt;
&lt;li&gt;Set up performance regression detection&lt;/li&gt;
&lt;li&gt;Document what belongs in MySQL vs. SQLite suites&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Success criteria:&lt;/strong&gt; Performance is stable and regressions are caught early.&lt;/p&gt;




&lt;h2&gt;
  
  
  Troubleshooting Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  "My tests are using :memory: but still failing in parallel"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Likely cause:&lt;/strong&gt; Multiple connections to the same in-memory database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Switch to per-worker file databases as shown in the fixtures.&lt;/p&gt;

&lt;h3&gt;
  
  
  "SQLite tests pass but MySQL tests fail"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Likely cause:&lt;/strong&gt; You found a real difference between the databases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Either adjust your code to be database-agnostic, or move that test case to the MySQL suite permanently.&lt;/p&gt;

&lt;h3&gt;
  
  
  "One worker is much slower than others"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Likely cause:&lt;/strong&gt; Imbalanced test distribution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Use &lt;code&gt;pytest-split&lt;/code&gt; with actual timing data, not just test counts.&lt;/p&gt;

&lt;h3&gt;
  
  
  "Tests are fast but flaky"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Likely causes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shared global state&lt;/li&gt;
&lt;li&gt;Race conditions in parallel execution&lt;/li&gt;
&lt;li&gt;Non-deterministic test data&lt;/li&gt;
&lt;li&gt;Insufficient test isolation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Add more debugging output, check for global variables, ensure proper fixture isolation.&lt;/p&gt;

&lt;h3&gt;
  
  
  "Memory usage is through the roof"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Likely causes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database connections not being closed&lt;/li&gt;
&lt;li&gt;Large test data sets being held in memory&lt;/li&gt;
&lt;li&gt;Circular references preventing garbage collection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Add explicit connection cleanup, use smaller test datasets, profile with &lt;code&gt;memory_profiler&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Results (Finally!)
&lt;/h2&gt;

&lt;p&gt;After implementing this approach across multiple projects, here's what we typically see:&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Improvements
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Typical Before&lt;/th&gt;
&lt;th&gt;Typical After&lt;/th&gt;
&lt;th&gt;Improvement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Total wall time (CI)&lt;/td&gt;
&lt;td&gt;25-35 minutes&lt;/td&gt;
&lt;td&gt;3-6 minutes&lt;/td&gt;
&lt;td&gt;6-10x faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local test feedback&lt;/td&gt;
&lt;td&gt;5-10 minutes&lt;/td&gt;
&lt;td&gt;30-60 seconds&lt;/td&gt;
&lt;td&gt;5-10x faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory usage (peak)&lt;/td&gt;
&lt;td&gt;6-12 GB&lt;/td&gt;
&lt;td&gt;2-4 GB&lt;/td&gt;
&lt;td&gt;50-70% less&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI cost (monthly)&lt;/td&gt;
&lt;td&gt;$400-800&lt;/td&gt;
&lt;td&gt;$150-300&lt;/td&gt;
&lt;td&gt;60-70% savings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Developer happiness&lt;/td&gt;
&lt;td&gt;😢&lt;/td&gt;
&lt;td&gt;😊&lt;/td&gt;
&lt;td&gt;Priceless&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  What You Get
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Faster feedback loops:&lt;/strong&gt; Developers actually run tests locally again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reliable CI:&lt;/strong&gt; No more "let's just restart the build and hope" debugging sessions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lower costs:&lt;/strong&gt; Your AWS bill will thank you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better test coverage:&lt;/strong&gt; When tests are fast, people write more of them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fewer production bugs:&lt;/strong&gt; Fast tests get run more often, catching issues earlier.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You Don't Get
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Perfect MySQL compatibility:&lt;/strong&gt; You'll still need that small MySQL test suite.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero maintenance:&lt;/strong&gt; Test infrastructure still needs care and feeding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Magic bullet:&lt;/strong&gt; Some tests will still be inherently slow - that's okay.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Complete Checklist
&lt;/h2&gt;

&lt;p&gt;Print this out and check items off as you implement them:&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup Phase
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Document current test performance (5-run protocol)&lt;/li&gt;
&lt;li&gt;[ ] Set up project structure with proper test directories&lt;/li&gt;
&lt;li&gt;[ ] Configure &lt;code&gt;pytest.ini&lt;/code&gt; with markers and asyncio mode&lt;/li&gt;
&lt;li&gt;[ ] Create the database abstraction layer (&lt;code&gt;app/db.py&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Fixtures Phase
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Implement session-scoped SQLite engine fixture&lt;/li&gt;
&lt;li&gt;[ ] Add per-worker database file creation&lt;/li&gt;
&lt;li&gt;[ ] Set up &lt;code&gt;?foreign_keys=1&lt;/code&gt; in connection string&lt;/li&gt;
&lt;li&gt;[ ] Create FastAPI app fixture with dependency overrides&lt;/li&gt;
&lt;li&gt;[ ] Add HTTP client fixture&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Testing Phase
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Convert component tests to use SQLite fixtures&lt;/li&gt;
&lt;li&gt;[ ] Set up test data factories with deterministic seeds&lt;/li&gt;
&lt;li&gt;[ ] Identify and preserve critical MySQL test cases&lt;/li&gt;
&lt;li&gt;[ ] Add E2E fixtures for MySQL testing&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Parallelization Phase
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Enable &lt;code&gt;pytest -n auto&lt;/code&gt; and fix any failures&lt;/li&gt;
&lt;li&gt;[ ] Upgrade to &lt;code&gt;pytest -n logical --dist worksteal&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Ensure test isolation with proper fixtures&lt;/li&gt;
&lt;li&gt;[ ] Verify no shared state between tests&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  CI Phase
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Split fast tests into multiple parallel jobs&lt;/li&gt;
&lt;li&gt;[ ] Set up MySQL service for E2E tests&lt;/li&gt;
&lt;li&gt;[ ] Consider &lt;code&gt;pytest-split&lt;/code&gt; for runtime-based job splitting&lt;/li&gt;
&lt;li&gt;[ ] Add Allure or similar reporting&lt;/li&gt;
&lt;li&gt;[ ] Configure artifact upload for test results&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Monitoring Phase
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Add memory usage monitoring&lt;/li&gt;
&lt;li&gt;[ ] Set up performance regression detection&lt;/li&gt;
&lt;li&gt;[ ] Document what belongs in MySQL vs SQLite suites&lt;/li&gt;
&lt;li&gt;[ ] Create runbook for troubleshooting parallel test failures&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Resources and Further Reading
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Essential Tools
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;pytest-xdist:&lt;/strong&gt; Parallel test execution - &lt;a href="https://pytest-xdist.readthedocs.io/" rel="noopener noreferrer"&gt;docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pytest-split:&lt;/strong&gt; Runtime-based test splitting - &lt;a href="https://github.com/jerry-git/pytest-split" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;factory_boy:&lt;/strong&gt; Test data factories - &lt;a href="https://factoryboy.readthedocs.io/" rel="noopener noreferrer"&gt;docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faker:&lt;/strong&gt; Generate fake data - &lt;a href="https://faker.readthedocs.io/" rel="noopener noreferrer"&gt;docs&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  FastAPI Testing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Official FastAPI Testing Guide:&lt;/strong&gt; &lt;a href="https://fastapi.tiangolo.com/tutorial/testing/" rel="noopener noreferrer"&gt;fastapi.tiangolo.com/tutorial/testing&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AsyncClient documentation:&lt;/strong&gt; For testing async endpoints&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Database Testing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SQLAlchemy Testing:&lt;/strong&gt; &lt;a href="https://docs.sqlalchemy.org/en/20/orm/session_transaction.html" rel="noopener noreferrer"&gt;docs.sqlalchemy.org/en/20/orm/session_transaction.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQLite vs MySQL differences:&lt;/strong&gt; Know what you're getting into&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  CI/CD Optimization
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Actions best practices:&lt;/strong&gt; Caching, artifacts, and parallelization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pytest-split documentation:&lt;/strong&gt; Runtime-based test distribution&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  FAQ (The Questions You're Going to Ask)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Q: "Should I use SQLite for ALL tests?"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; No. Use SQLite for the majority (90-95%) but keep MySQL tests for database-specific behavior, migrations, and edge cases that depend on MySQL semantics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q: "What about other databases like PostgreSQL?"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; The same principles apply. SQLite for speed, PostgreSQL for the cases that need PostgreSQL-specific features. Adjust the edge cases accordingly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q: "How do I know what tests need the real database?"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; Start by converting everything to SQLite. The tests that break will tell you what needs the real database. Common candidates: migrations, JSON queries, stored procedures, specific isolation levels.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q: "Is this approach suitable for microservices?"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; Absolutely. Each service can have its own fast test suite with selective real database testing for integration points.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q: "What about integration tests with external services?"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; Mock them in your component tests, test them for real in your E2E suite. Same principle as SQLite vs MySQL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q: "How often should I regenerate test timing data?"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; Weekly for active projects, monthly for stable ones. Whenever you add a lot of new tests or notice job imbalance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q: "Can I use this with other web frameworks?"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; Yes! The core principles (fast database, fixtures, parallelization) apply to Django, Flask, or any other framework. Adjust the specifics accordingly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q: "What if my tests are CPU-bound instead of I/O-bound?"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; Parallelization will still help, but you might need to optimize algorithms rather than just database access. Profile to find the bottlenecks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q: "How do I convince my team to adopt this?"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; Start with measuring current performance. Numbers don't lie. Implement incrementally and show results at each phase.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion: Go Forth and Test Quickly
&lt;/h2&gt;

&lt;p&gt;You now have a complete, battle-tested approach to making your FastAPI tests faster without sacrificing reliability. This isn't theoretical - it's been used to optimize test suites across multiple production systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Key Insights
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Speed and quality aren't mutually exclusive.&lt;/strong&gt; You can have fast tests that catch real bugs by being strategic about what you test where.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure matters.&lt;/strong&gt; Proper fixtures, isolation, and parallelization are force multipliers for test performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Measure everything.&lt;/strong&gt; Without numbers, you're just guessing. With numbers, you can make informed decisions and track progress.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Incremental improvement beats big rewrites.&lt;/strong&gt; You can adopt this approach piece by piece without breaking your existing workflow.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Implement incrementally:&lt;/strong&gt; Don't try to do everything at once&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measure before and after:&lt;/strong&gt; Document your improvements
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Share your results:&lt;/strong&gt; Help others learn from your experience&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iterate and improve:&lt;/strong&gt; This is a living system, not a one-time fix&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  A Final Warning
&lt;/h3&gt;

&lt;p&gt;Fast tests are addictive. Once your team experiences 3-minute CI runs instead of 30-minute ones, there's no going back. You'll find yourself optimizing other slow things in your development process.&lt;/p&gt;

&lt;p&gt;Don't say we didn't warn you.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Now stop reading and start implementing. Your future self (and your teammates) will thank you.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Shahar Polak&lt;/strong&gt; has spent way too much time waiting for slow tests and decided to do something about it. When not optimizing test suites, he can be found building things with FastAPI, SQLAlchemy, and probably complaining about some CI pipeline somewhere.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Got questions? Found a bug in this guide? Implemented this successfully (or unsuccessfully)? Share your experience - the testing community learns from real-world results, not just theory.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>fastapi</category>
      <category>sqlmodel</category>
      <category>sqlite</category>
      <category>testing</category>
    </item>
    <item>
      <title>Great article, I love every single tool here</title>
      <dc:creator>Shahar Polak</dc:creator>
      <pubDate>Wed, 23 Apr 2025 22:07:39 +0000</pubDate>
      <link>https://forem.com/polakshahar/great-article-i-love-every-single-tool-here-26al</link>
      <guid>https://forem.com/polakshahar/great-article-i-love-every-single-tool-here-26al</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/code42cate/how-i-save-by-self-hosting-these-5-open-source-tools-17mb" class="crayons-story__hidden-navigation-link"&gt;How I save $$$ by self-hosting these 5 open source tools&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/code42cate" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F461127%2F034233c4-ba6e-473c-8a8d-783831764a10.jpeg" alt="code42cate profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/code42cate" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Jonas Scholz
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Jonas Scholz
                &lt;a href="/++"&gt;&lt;img alt="Subscriber" class="subscription-icon" src="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png"&gt;&lt;/a&gt;
              
              &lt;div id="story-author-preview-content-2422165" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/code42cate" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F461127%2F034233c4-ba6e-473c-8a8d-783831764a10.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Jonas Scholz&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/code42cate/how-i-save-by-self-hosting-these-5-open-source-tools-17mb" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Apr 21 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/code42cate/how-i-save-by-self-hosting-these-5-open-source-tools-17mb" id="article-link-2422165"&gt;
          How I save $$$ by self-hosting these 5 open source tools
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/cloud"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;cloud&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devops"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devops&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/code42cate/how-i-save-by-self-hosting-these-5-open-source-tools-17mb" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;164&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/code42cate/how-i-save-by-self-hosting-these-5-open-source-tools-17mb#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              16&lt;span class="hidden s:inline"&gt; comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>opensource</category>
      <category>cloud</category>
      <category>devops</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Stopping Errors Before They Stop You</title>
      <dc:creator>Shahar Polak</dc:creator>
      <pubDate>Thu, 19 Sep 2024 15:01:08 +0000</pubDate>
      <link>https://forem.com/polakshahar/stopping-errors-before-they-stop-you-26fp</link>
      <guid>https://forem.com/polakshahar/stopping-errors-before-they-stop-you-26fp</guid>
      <description>&lt;h2&gt;
  
  
  Stopping Errors Before They Stop You: The Safe Assignment Operator (&lt;code&gt;?=&lt;/code&gt;) and Handling Promises Gracefully
&lt;/h2&gt;

&lt;p&gt;As JavaScript evolves, new features and proposals keep rolling in, aiming to make coding more efficient and error-proof. One such feature is the &lt;strong&gt;Safe Assignment Operator&lt;/strong&gt; (&lt;code&gt;?=&lt;/code&gt;), a proposed addition to the language. While we're still waiting for its official release, we can implement similar functionality today to safeguard our code from common issues like &lt;code&gt;null&lt;/code&gt; or &lt;code&gt;undefined&lt;/code&gt; values.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore the &lt;code&gt;?=&lt;/code&gt; operator, build our own version using existing JavaScript, and introduce practical ways to handle promises more gracefully in asynchronous operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Safe Assignment Operator (&lt;code&gt;?=&lt;/code&gt;)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is the &lt;code&gt;?=&lt;/code&gt; Operator?
&lt;/h3&gt;

&lt;p&gt;The Safe Assignment Operator (&lt;code&gt;?=&lt;/code&gt;) allows developers to assign a value to a variable only if the target is &lt;code&gt;null&lt;/code&gt; or &lt;code&gt;undefined&lt;/code&gt;. It’s a more concise way of saying, "Assign this value if the variable is empty." &lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Shahar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: "Shahar"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, the variable &lt;code&gt;username&lt;/code&gt; gets assigned &lt;code&gt;"Shahar"&lt;/code&gt; because its value was &lt;code&gt;null&lt;/code&gt;. If &lt;code&gt;username&lt;/code&gt; had an existing value, the operator would simply pass over the assignment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why It's Useful
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;?=&lt;/code&gt; operator simplifies code by reducing the need for explicit &lt;code&gt;if&lt;/code&gt; checks or ternary operations to ensure safe assignment. However, this operator is still in the &lt;strong&gt;proposal stage&lt;/strong&gt; within ECMAScript, meaning it could change before becoming part of the JavaScript language. You can track its development &lt;a href="https://github.com/tc39/proposal-logical-assignment" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Crafting a Safe Assignment Function
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Rolling Out &lt;code&gt;safeAssign&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;While we're waiting for &lt;code&gt;?=&lt;/code&gt; to become official, we can mimic its behavior today using a custom utility function called &lt;code&gt;safeAssign&lt;/code&gt;. This function uses the nullish coalescing operator (&lt;code&gt;??&lt;/code&gt;), which is already widely supported in modern environments.&lt;/p&gt;

&lt;p&gt;Here’s our &lt;code&gt;safeAssign&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;safeAssign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example in Action
&lt;/h3&gt;

&lt;p&gt;Let’s see how it works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;safeAssign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Shahar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: "Shahar"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is effectively what the &lt;code&gt;?=&lt;/code&gt; operator would do. If the variable is &lt;code&gt;null&lt;/code&gt; or &lt;code&gt;undefined&lt;/code&gt;, we assign it a value; otherwise, we leave it untouched.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations of &lt;code&gt;safeAssign&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;While &lt;code&gt;safeAssign&lt;/code&gt; provides similar functionality to &lt;code&gt;?=&lt;/code&gt;, it has limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity&lt;/strong&gt;: &lt;code&gt;safeAssign&lt;/code&gt; is a utility function and cannot provide the same level of syntactic elegance as the native &lt;code&gt;?=&lt;/code&gt; operator. Overusing custom functions can lead to more verbose code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt;: Although the performance impact of &lt;code&gt;safeAssign&lt;/code&gt; is negligible in small-scale applications, native operators like &lt;code&gt;?=&lt;/code&gt; will likely be faster in larger-scale systems due to engine optimizations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browser Support&lt;/strong&gt;: The nullish coalescing operator (&lt;code&gt;??&lt;/code&gt;) used in &lt;code&gt;safeAssign&lt;/code&gt; is supported in most modern browsers and environments, but older environments may not support it without polyfills.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  A Quick Comparison with Other Languages
&lt;/h3&gt;

&lt;p&gt;Many other languages offer similar features to the proposed &lt;code&gt;?=&lt;/code&gt; operator:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;C#&lt;/strong&gt; has the null-coalescing assignment operator (&lt;code&gt;??=&lt;/code&gt;), which behaves similarly to JavaScript’s &lt;code&gt;?=&lt;/code&gt; proposal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python&lt;/strong&gt; uses the &lt;code&gt;or&lt;/code&gt; keyword for safe assignments, where &lt;code&gt;a = a or value&lt;/code&gt; is a common pattern to assign a value only if &lt;code&gt;a&lt;/code&gt; is falsy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These operators make handling potentially empty values more straightforward, reducing boilerplate code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Asynchronous Operations with &lt;code&gt;safeAwait&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Introducing &lt;code&gt;safeAwait&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;When working with asynchronous operations in JavaScript, it’s easy to run into rejected promises or unexpected results. Instead of manually handling every rejection with &lt;code&gt;.catch()&lt;/code&gt;, we can streamline the process using a custom function called &lt;code&gt;safeAwait&lt;/code&gt;, which wraps promises in a cleaner, safer structure.&lt;/p&gt;

&lt;p&gt;Here’s the &lt;code&gt;safeAwait&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;safeAwait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;errorHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// Success: No error, return the data&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errorHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;errorHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Optional error handler&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// Error occurred, return error with null data&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example: Fetching Data with Error Handling
&lt;/h3&gt;

&lt;p&gt;Let’s use &lt;code&gt;safeAwait&lt;/code&gt; to fetch data from an API and handle potential errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;safeAwait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Request failed:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Exit if there's an error&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Return response if successful&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, &lt;code&gt;safeAwait&lt;/code&gt; handles both the success and error cases, allowing the calling function to handle the result in a more predictable way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Variations of &lt;code&gt;safeAwait&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;We can also extend &lt;code&gt;safeAwait&lt;/code&gt; for different use cases. For instance, here’s a version that retries the promise once before failing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;safeAwaitWithRetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;errorHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;retries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;retries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;safeAwait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;errorHandler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Max retries reached&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This variation retries the promise up to a specified number of times before throwing in the towel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for Error Handling in JavaScript
&lt;/h2&gt;

&lt;p&gt;When working with asynchronous code, proper error handling is crucial. Here are some best practices:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Always handle rejected promises&lt;/strong&gt;: Unhandled promise rejections can lead to crashes or undefined behavior. Use &lt;code&gt;try/catch&lt;/code&gt; or &lt;code&gt;.catch()&lt;/code&gt; to ensure promises are properly handled.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Centralize error handling&lt;/strong&gt;: Utility functions like &lt;code&gt;safeAwait&lt;/code&gt; allow you to centralize error handling, making it easier to manage and debug your code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graceful degradation&lt;/strong&gt;: Ensure that your application can recover from errors gracefully without crashing or leaving the user in an undefined state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use custom error messages&lt;/strong&gt;: When throwing errors, provide meaningful error messages to help with debugging.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Before and After: Clean Code with &lt;code&gt;safeAssign&lt;/code&gt; and &lt;code&gt;safeAwait&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Here’s a quick comparison of how these utilities can clean up your code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Without &lt;code&gt;safeAssign&lt;/code&gt;:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Shahar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  With &lt;code&gt;safeAssign&lt;/code&gt;:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;safeAssign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Shahar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Without &lt;code&gt;safeAwait&lt;/code&gt;:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Request failed:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  With &lt;code&gt;safeAwait&lt;/code&gt;:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;safeAwait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Request failed:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;In summary, while the Safe Assignment Operator (&lt;code&gt;?=&lt;/code&gt;) is still a proposal, we can replicate its behavior today using the &lt;code&gt;safeAssign&lt;/code&gt; function for nullish values and &lt;code&gt;safeAwait&lt;/code&gt; for more complex asynchronous operations. Both utilities simplify your code, making it more readable and maintainable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Takeaways:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;?=&lt;/code&gt; operator simplifies safe assignments but is still in the proposal stage.&lt;/li&gt;
&lt;li&gt;You can replicate &lt;code&gt;?=&lt;/code&gt; functionality with &lt;code&gt;safeAssign&lt;/code&gt; using the nullish coalescing operator (&lt;code&gt;??&lt;/code&gt;), which is widely supported.&lt;/li&gt;
&lt;li&gt;For asynchronous operations, &lt;code&gt;safeAwait&lt;/code&gt; provides a cleaner way to handle promise rejections and errors.&lt;/li&gt;
&lt;li&gt;Keep an eye on &lt;a href="https://github.com/tc39/proposals" rel="noopener noreferrer"&gt;ECMAScript proposals&lt;/a&gt; for future updates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By leveraging these patterns, you can handle errors like a pro and keep your code clean, readable, and safe.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>beginners</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Blast Off with Python: Mastering Concurrent Processing in Space and Beyond</title>
      <dc:creator>Shahar Polak</dc:creator>
      <pubDate>Mon, 27 Nov 2023 07:56:17 +0000</pubDate>
      <link>https://forem.com/polakshahar/blast-off-with-python-mastering-concurrent-processing-in-space-and-beyond-1f07</link>
      <guid>https://forem.com/polakshahar/blast-off-with-python-mastering-concurrent-processing-in-space-and-beyond-1f07</guid>
      <description>&lt;p&gt;🚀 Welcome, Python space explorers and data scientists! Are you ready to supercharge your Python scripts and bid farewell to the sluggishness of sequential execution? Embrace the power of &lt;code&gt;concurrent.futures&lt;/code&gt;! &lt;/p&gt;

&lt;p&gt;This module isn't just a tool; it's your mission control for parallelizing tasks, launching your code from the mundane to the realms of high-efficiency computing. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A big shout-out to my dear friend Adar Cohen for his invaluable assistance in writing this article. Your insights and support were crucial in navigating the vast universe of Python's concurrent processing. Thank you, Adar!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What will we find in this article?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Python concurrency&lt;/li&gt;
&lt;li&gt;Parallel processing&lt;/li&gt;
&lt;li&gt;Efficient Python scripting&lt;/li&gt;
&lt;li&gt;Data science optimization&lt;/li&gt;
&lt;li&gt;Python multiprocessing&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Slow March Across the Data Universe
&lt;/h2&gt;

&lt;p&gt;Visualize your Python code as a lone rover on the vast plains of Mars, tasked with a mission to analyze a colossal dataset:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;all_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_massive_dataset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;all_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
   &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;super_cpu_intensive_work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nf"&gt;save_result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This scenario screams inefficiency. &lt;code&gt;super_cpu_intensive_work&lt;/code&gt; consumes all CPU resources, while &lt;code&gt;save_result&lt;/code&gt; waits idly. This is a classic example of sequential processing at its most tedious.&lt;/p&gt;

&lt;p&gt;And the memory usage? It's as barren and uneventful as the Martian landscape:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Rl9ALACo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y8rgsof6c9zcervdde18.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Rl9ALACo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y8rgsof6c9zcervdde18.png" alt="GPU waiting on finishing tasks" width="800" height="56"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's time for a revolutionary leap in data processing!&lt;/p&gt;

&lt;h2&gt;
  
  
  Mission Control: Concurrent Futures
&lt;/h2&gt;

&lt;p&gt;Now, let's inject some excitement with &lt;code&gt;concurrent.futures&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;concurrent.futures&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ProcessPoolExecutor&lt;/span&gt;  

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;ProcessPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
   &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;all_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
      &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;super_cpu_intensive_work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;save_result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where the magic happens! &lt;code&gt;super_cpu_intensive_work&lt;/code&gt; and &lt;code&gt;save_result&lt;/code&gt; now work in tandem, efficiently processing data like a team of synchronized satellites.&lt;/p&gt;

&lt;p&gt;Behold the transformation in memory usage – it's as dynamic and bustling as a space station at peak hour:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SmJc__l9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/arihkuzirmyrf963qttq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SmJc__l9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/arihkuzirmyrf963qttq.png" alt="GPU and CPU stats together for better performance" width="800" height="47"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Steering Through ProcessPoolExecutor
&lt;/h2&gt;

&lt;p&gt;In the vast expanse of CPU-bound tasks, &lt;code&gt;ProcessPoolExecutor&lt;/code&gt; is your trusted spacecraft:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Specialized in managing multiple processes, perfect for parallelizing CPU-intensive tasks.&lt;/li&gt;
&lt;li&gt;Operates like separate spacecraft, each with its own Python command center.&lt;/li&gt;
&lt;li&gt;Isolated processes ensure mission integrity, even if one encounters an issue.&lt;/li&gt;
&lt;li&gt;Ideal for heavy-duty tasks that require significant computational power.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For tasks that are more about communication than brute force, ThreadPoolExecutor is your go-to option, leveraging threads for I/O-bound operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  CPU vs GPU: Choosing Your Spacecraft
&lt;/h2&gt;

&lt;h3&gt;
  
  
  When to Harness GPU Power:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Parallel Numerical Computations&lt;/strong&gt;: Ideal for tasks like matrix multiplications or operations on large data arrays, which are common in data science and machine learning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image and Video Processing&lt;/strong&gt;: Essential for quick processing of visual data, a must in fields like computer vision and digital media.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deep Learning&lt;/strong&gt;: GPUs are indispensable for training neural networks and AI models, offering unparalleled processing speeds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graphical Simulations&lt;/strong&gt;: In simulations and gaming, GPUs provide the necessary horsepower for rendering complex graphics.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When to Rely on CPU:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;General Computing&lt;/strong&gt;: From running operating systems to executing diverse software applications, CPUs are the backbone of general computing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sequential Processing&lt;/strong&gt;: For tasks where operations must occur in a specific order, CPUs are more efficient.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex Logic&lt;/strong&gt;: When your code involves intricate decision-making, CPUs offer the required sophistication.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server-side Operations&lt;/strong&gt;: Hosting web servers or applications where parallel processing isn't critical, CPUs are more reliable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  To Infinity and Beyond!
&lt;/h2&gt;

&lt;p&gt;With &lt;code&gt;concurrent.futures&lt;/code&gt;, we're no longer confined to the tedious world of sequential data processing. We've stepped into the era of high-speed, efficient Python computing, perfect for the demanding needs of modern data science and machine learning. Python programmers, it's time to strap on your rocket boosters and explore the limitless possibilities of multiprocessing and parallel computing! 🌌&lt;/p&gt;

</description>
      <category>cpu</category>
      <category>gpu</category>
      <category>python</category>
      <category>datascience</category>
    </item>
    <item>
      <title>Converting MongoDB ObjectId to Timestamp in Snowflake: A Friendly Guide</title>
      <dc:creator>Shahar Polak</dc:creator>
      <pubDate>Fri, 24 Nov 2023 14:02:27 +0000</pubDate>
      <link>https://forem.com/polakshahar/converting-mongodb-objectid-to-timestamp-in-snowflake-a-friendly-guide-2mgp</link>
      <guid>https://forem.com/polakshahar/converting-mongodb-objectid-to-timestamp-in-snowflake-a-friendly-guide-2mgp</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Hey there, fellow developers! Today, let's unravel a nifty trick that often stumps many of us when working with MongoDB and Snowflake. &lt;/p&gt;

&lt;p&gt;Picture this: You've got data in MongoDB, each record stamped with that unique ObjectId MongoDB is famous for. Now, you need to migrate this data into Snowflake, but wait – how do you make sense of those timestamps hidden inside those ObjectIds? Fear not! &lt;/p&gt;

&lt;p&gt;In this post, I'll walk you through a simple yet effective solution to extract those timestamps and bring them into the light in Snowflake.&lt;/p&gt;



&lt;h3&gt;
  
  
  The Challenge Explained
&lt;/h3&gt;

&lt;p&gt;MongoDB's ObjectId isn't just a random jumble of characters; it's a treasure trove of information. The first 8 characters are actually a timestamp, representing when the data was created. &lt;/p&gt;

&lt;p&gt;The catch? It's in hexadecimal format. And here's where Snowflake throws us a curveball – it doesn't have a built-in function to convert hexadecimal to decimal. So, how do we crack this code? Let's find out!&lt;/p&gt;



&lt;h3&gt;
  
  
  Step-by-Step Solution
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Step 1: The Goal
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Objective&lt;/strong&gt;: Extract the timestamp from MongoDB's ObjectId in Snowflake.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Hurdle&lt;/strong&gt;: Snowflake's SQL doesn't directly convert hex to decimal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tQcm7N8h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3o8fmw4cl22dklcg77mr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tQcm7N8h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3o8fmw4cl22dklcg77mr.png" alt="Image depicts a data table with four columns and four rows. The first column header is '_id' indicating unique identifiers for records. The second column header is 'current_version' showing version numbers . The third column is 'model_id' . On the right side of the image, there is a  text 'CREATED_AT' with a shrugging emoji above it, suggesting the addition of a new 'created_at' column to indicate record creation times. The overall image conveys the process of enhancing a database with time-related data." width="800" height="125"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2: Our Secret Weapon – A Custom JavaScript UDF
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;UDFs to the Rescue&lt;/strong&gt;: In Snowflake, we can create User-Defined Functions (UDFs) to perform custom operations. We'll use JavaScript for our UDF.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;UDF Creation Script&lt;/strong&gt;: Here's how we set up our UDF.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;hex_to_dec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hex_str&lt;/span&gt; &lt;span class="n"&gt;STRING&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="nb"&gt;FLOAT&lt;/span&gt;
&lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;JAVASCRIPT&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HEX_STR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;What's Happening Here?&lt;/strong&gt;: This snippet tells Snowflake, "Hey, let's turn those hex values into something we can work with in decimal!"&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;h4&gt;
  
  
  Step 3: Putting Our UDF to Work
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Magic Query&lt;/strong&gt;: Now, we use our UDF in a SQL query.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;MONGO_IMPORT_WITH_TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;TO_TIMESTAMP_NTZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hex_to_dec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SUBSTR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
    &lt;span class="n"&gt;MONGO_IMPORT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Breaking It Down&lt;/strong&gt;: This query is our secret sauce. It says, "Let's create a new table, MONGO_IMPORT_WITH_TIMESTAMP, where we'll turn those hex timestamps into human-friendly dates."&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;h4&gt;
  
  
  Step 4: Test and Celebrate
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Always Test&lt;/strong&gt;: Before we break out the confetti, let's test this with some sample data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What to Expect&lt;/strong&gt;: If all goes well, you'll have a shiny new table in Snowflake, with all your MongoDB timestamps now readable and ready to use.&lt;/li&gt;
&lt;/ul&gt;



&lt;h3&gt;
  
  
  Wrapping Up
&lt;/h3&gt;

&lt;p&gt;In wrapping up, let's recall the words of W. Edwards Deming: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;'Without data, you're just another person with an opinion.' &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Our journey through converting MongoDB ObjectIds to readable timestamps in Snowflake is a testament to this truth. By unlocking the data hidden within these identifiers, we turn mere numbers into meaningful insights.&lt;/p&gt;





&lt;h3&gt;
  
  
  Helpful Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Check out Snowflake's &lt;a href="https://docs.snowflake.com/developer-guide/udf/javascript/udf-javascript-introduction"&gt;UDF documentation&lt;/a&gt; for more cool tricks.&lt;/li&gt;
&lt;li&gt;Stuck or want to share your success? Join the conversation in &lt;a href="https://community.snowflake.com/s/"&gt;Snowflake forums&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>mongodb</category>
      <category>snowflak</category>
      <category>db</category>
      <category>nosql</category>
    </item>
    <item>
      <title>Going async in python</title>
      <dc:creator>Shahar Polak</dc:creator>
      <pubDate>Wed, 07 Dec 2022 09:04:16 +0000</pubDate>
      <link>https://forem.com/polakshahar/going-async-in-python-2icn</link>
      <guid>https://forem.com/polakshahar/going-async-in-python-2icn</guid>
      <description>&lt;p&gt;&lt;strong&gt;Going async in python - the missing guide&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;TLDR;&lt;br&gt;
&lt;em&gt;Make your code run faster and scalable using async API calls in python. with a quick explanation and an example anyone can follow.&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;Many times we have to make API requests, and one of the most familiar repositories to do so is &lt;code&gt;requests&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Unfortunately, the requests library is not equipped for performing asynchronous requests. &lt;br&gt;
We can wrap async/await syntax around requests, but that will make the underlying requests no less synchronous. &lt;/p&gt;
&lt;h2&gt;
  
  
  Why should we make our code async?
&lt;/h2&gt;

&lt;p&gt;There are several reasons why we might want our code to be async.&lt;br&gt;
Async code is a programming paradigm that allows multiple tasks to be performed concurrently without blocking. &lt;br&gt;
This can improve the performance, responsiveness, and scalability of an application. &lt;/p&gt;

&lt;p&gt;Async code can be run on a single thread, allowing it to make better use of available resources and handle more concurrent requests without running out of resources. &lt;/p&gt;

&lt;p&gt;Async code can also make the code easier to read and maintain by allowing complex operations to be broken down into smaller tasks.&lt;/p&gt;
&lt;h2&gt;
  
  
  So how do we turn our code to async?
&lt;/h2&gt;

&lt;p&gt;In order to get async requests, we must address other tools that can provide it. &lt;/p&gt;

&lt;p&gt;One of my go-to tools is &lt;code&gt;asyncio&lt;/code&gt; and using &lt;code&gt;aiohttp&lt;/code&gt;. (they have amazing &lt;a href="https://docs.aiohttp.org/en/stable/"&gt;docs&lt;/a&gt; )&lt;/p&gt;
&lt;h2&gt;
  
  
  Can we see some examples of sync turn into async code?
&lt;/h2&gt;

&lt;p&gt;In the following section we will take a &lt;code&gt;requests&lt;/code&gt; code and convert it to &lt;code&gt;async&lt;/code&gt; code using &lt;code&gt;aiohttp&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;IP_GEOLOCATION_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"YOUR API KEY"&lt;/span&gt;

&lt;span class="c1"&gt;# requests example 
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GeolocationClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;IP_GEOLOCATION_KEY&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_geolocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://api.ipgeolocation.io/ipgeo"&lt;/span&gt;
        &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now lets see how we can convert to &lt;code&gt;async&lt;/code&gt; method. &lt;/p&gt;

&lt;p&gt;To use an async library to fetch data in the GeolocationClient class, you would need to first import the async library and then use its methods to make the HTTP request and retrieve the response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;aiohttp&lt;/span&gt;

&lt;span class="n"&gt;IP_GEOLOCATION_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"YOUR API KEY"&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GeolocationClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;IP_GEOLOCATION_KEY&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_geolocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://api.ipgeolocation.io/ipgeo"&lt;/span&gt;
        &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;aiohttp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the &lt;code&gt;aiohttp.ClientSession&lt;/code&gt; is used to create a session for making HTTP requests. Then, the &lt;code&gt;session.get()&lt;/code&gt; method is used to make a GET request to the specified URL with the given query parameters.&lt;br&gt;&lt;br&gt;
The &lt;code&gt;response.json()&lt;/code&gt; method is used to parse the response as JSON and return it.&lt;/p&gt;

&lt;p&gt;Note that this code uses &lt;code&gt;async/await&lt;/code&gt; syntax, which is only supported in &lt;code&gt;Python 3.7&lt;/code&gt; and above. If you are using an older version of Python, you would need to use a different syntax for async operations, so upgrade your version! :)&lt;/p&gt;

&lt;p&gt;Here is a full example running the code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;aiohttp&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;asyncio&lt;/span&gt;

&lt;span class="n"&gt;IP_GEOLOCATION_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"YOUR API KEY"&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GeolocationClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;IP_GEOLOCATION_KEY&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_geolocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://api.ipgeolocation.io/ipgeo"&lt;/span&gt;
        &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;aiohttp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;


&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GeolocationClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;ips&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"1.1.1.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"8.8.8.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"9.9.9.9"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Start all the tasks concurrently
&lt;/span&gt;    &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_geolocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ips&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Print the results as they come in
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# Run the main function
&lt;/span&gt;&lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this code, the main function is an async function that creates a &lt;code&gt;GeolocationClient&lt;/code&gt; and a list of IP addresses. &lt;br&gt;
It then uses a list comprehension to create a list of tasks that call the &lt;code&gt;get_geolocation&lt;/code&gt; method on each IP address.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;asyncio.gather&lt;/code&gt; function is used to start all of the tasks concurrently and wait for them to complete.&lt;br&gt;&lt;br&gt;
It returns a list of results, which are the JSON responses from the get_geolocation method. The results are then printed as they come in.&lt;/p&gt;

&lt;p&gt;Finally, the &lt;code&gt;asyncio.run&lt;/code&gt; function is used to run the main function. &lt;br&gt;
This will start the asynchronous tasks and wait for them to complete before exiting the program.&lt;/p&gt;

&lt;p&gt;Hope that helps, and as usual, I'd love to know what you think. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Keep calm and code on&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>performance</category>
    </item>
    <item>
      <title>Aim for inspiration; motivation will follow.</title>
      <dc:creator>Shahar Polak</dc:creator>
      <pubDate>Mon, 05 Apr 2021 17:26:20 +0000</pubDate>
      <link>https://forem.com/polakshahar/aim-for-inspiration-motivation-will-follow-2i9p</link>
      <guid>https://forem.com/polakshahar/aim-for-inspiration-motivation-will-follow-2i9p</guid>
      <description>&lt;p&gt;Every organization's role has its challenges, and as much as I wish to tackle them all, I'll go with the bite-sized chunks and share my two cents about motivation. &lt;/p&gt;

&lt;p&gt;Motivating your team and keeping high morale is one of the fundamental challenges every manager needs to master. &lt;br&gt;
Is it an easy task? No-no-no.&lt;/p&gt;

&lt;p&gt;There are so many things that can go wrong in the process without considering the manager's own motivation. &lt;br&gt;
I can not talk through the eyes of every manager in the world, but I can share some of the experience I had with managers over the years and my managerial style and its progress during the past two decades. &lt;/p&gt;

&lt;p&gt;So before I begin, I wish to share a tiny piece of one of my first f%^&amp;amp;kups as a manager. Let's go back almost 20 years ago to 2003, to a time, information flows were sooo much different from today, and I was a young operation sergeant in the Israeli Defense Force.&lt;br&gt;
With little knowledge of how to treat my platoon, I was sure that the sun was revolving around me with a sense of entitlement that was highly misplaced. &lt;br&gt;
One of my female soldiers was underperforming horribly; instead of understanding why I decided that the best approach is to go hard on her without even hearing her side of the story.&lt;br&gt;
Then the crying river burst, bawling her eyes out, and the 19 years old me didn't know what to do or say. Between the tears, she has managed to tell me that she just had an abortion. &lt;br&gt;
My inner world had crumbled.&lt;br&gt;
At that moment, I gave her a big hug and told her I'm here for whatever she needs, I know it can not undo the way I've behaved, but I was determined to ease her pain in whatever way possible. &lt;/p&gt;

&lt;p&gt;That day will stay with me for decades to come. &lt;/p&gt;

&lt;h2&gt;
  
  
  1) Inspire your team
&lt;/h2&gt;

&lt;p&gt;It's effortless to tell people what to do, but I highly doubt it if this is how people wanna live their life, always being told what to do. &lt;/p&gt;

&lt;p&gt;Instead of telling your employees what to do, tell them what you believe. Your job is to inspire and guide them, but let them pave the way, and I promise you, you are to be rewarded with happy, loyal and enthusiastic employees.&lt;/p&gt;

&lt;p&gt;You make decisions that impact the success or failure of your team. As a leader, you are not meant to be a martyr. With this in mind, think about how you want your team to feel about working with you. Are they thankful and energized? Do they respect you? Do they enjoy the process, and can they see themselves performing well in your environment? &lt;/p&gt;

&lt;p&gt;Trust is built through communication, and your members will feel comfortable sharing information with you. Try asking the members of your team, "What's going well?" "What can I do to help?" "How can we improve?" Also, listen to your team.&lt;/p&gt;

&lt;h2&gt;
  
  
  2) Lead your team
&lt;/h2&gt;

&lt;p&gt;In order to truly empower your employees, you have to be willing to create a common language within your teams, know what the mistakes are that you are ready to let them make, and trust them when they want to try something that challenges the status quo.&lt;/p&gt;

&lt;p&gt;It's not all about producing endless worksheets. There are several important aspects to making work fun and inspirational. Make it authentic, offer challenging work, give your employees permission to do their best, and let them know that you are committed to helping them grow and succeed. &lt;/p&gt;

&lt;p&gt;Building their confidence and the chance to rise to the challenge. People usually like to do the stuff that they are good at. True, each and every single one of us needs to do things that we unnecessarily love and have to eat some frogs every now and then. &lt;br&gt;
But if you see that an employee talks about a subject passionately, know if you could let them do it a bit more often, you'd be surprised how much they will go above and beyond.&lt;/p&gt;

&lt;p&gt;Besides, try to let the team know why you've made the choices you've made. You are not operating in a vacuum, and transparency works both ways. The last word can be yours, but let your team be part of the process of coming to those conclusions. &lt;/p&gt;

&lt;h2&gt;
  
  
  3) Reward your team
&lt;/h2&gt;

&lt;p&gt;Many see the financial reward as the best reward, I would like to suggest another approach. &lt;br&gt;
I do find acknowledgment as a reward that goes a more extended range than any financial compensation I've ever received. How come?&lt;/p&gt;

&lt;p&gt;Whenever any team member does outstanding work, being an exceptional team player, or had a great idea, you should let them know, not only to them but also to the rest of the team/group/company. &lt;/p&gt;

&lt;p&gt;At my previous company, we used to have happy-hour every Thursday, and we started this tradition from day one of the company when every week could have been our last. &lt;br&gt;
It started with thanking for everything we have managed to do this week and be thankful for each other. After some time, it has become the most iconic ritual in the company, where we gave authentic phrases to each other and a place where we could share our gratitude. &lt;/p&gt;

&lt;p&gt;Be aware that some people do not feel comfortable getting praise in public, so feel free to express this gratitude in private. &lt;/p&gt;

&lt;p&gt;You do not have to be a manager to lead.&lt;br&gt;
Many leaders do not have an official title, but people will follow them because they believe. they can communicate their visions clearly. &lt;/p&gt;

&lt;p&gt;One of my favorite authors of all times, Simon Sinek, said, "People don't buy WHAT you do: they buy WHY you do it". &lt;br&gt;
I find this sentence to be my own personal guideline. &lt;/p&gt;

&lt;p&gt;When I've joined BreezoMeter, a company that helps fight air pollution, I've done it because my wife has asthma. This is my story, and this is where it meets me. &lt;br&gt;
After six years, when I've decided that it is my time to move on, I've joined ActiveFence, a fantastic company that helps fight bad actors online and make the internet a safer place. As a father, this is where it met me - creating a safe environment for my kid and all the other kids out there in the world. &lt;/p&gt;

&lt;p&gt;Even before I had a managerial role, I have shared my personal WHY, that what connected me to the people I work with, the belief that we are paving the way together to the exact cause.  &lt;/p&gt;

&lt;h2&gt;
  
  
  4) Be present
&lt;/h2&gt;

&lt;p&gt;This last piece of advice is for everyone, not only leaders and managers. &lt;/p&gt;

&lt;p&gt;When my son was born almost 6 years ago, I found myself sitting with him, but I wasn't really there. While I was sitting with my son, I was on my phone, scrolling Facebook, looking at how other people spend time with their kids. I know what you are thinking, what a jerk, and yes, you are right. &lt;br&gt;
After doing some introspective, I've realized how much injustice I've created. I have deprived my son and me of having the time of our lives and making memories together.&lt;/p&gt;

&lt;p&gt;It seems that all we need to do to write everything off in our to-do lists is multitasking. Well... don't. In any conversation, just be there. It is not enough to place your phone in your pocket on silent; you need to turn off all those mental notifications. &lt;/p&gt;

&lt;p&gt;People can sense when you are not present, so please give them your undivided attention. &lt;/p&gt;

</description>
      <category>management</category>
      <category>motivation</category>
    </item>
    <item>
      <title>Project Loo: the best time-wasting, amazing, over-engineered project of my life</title>
      <dc:creator>Shahar Polak</dc:creator>
      <pubDate>Sun, 18 Oct 2020 17:51:05 +0000</pubDate>
      <link>https://forem.com/polakshahar/project-loo-the-best-time-wasting-amazing-over-engineered-project-of-my-life-37m7</link>
      <guid>https://forem.com/polakshahar/project-loo-the-best-time-wasting-amazing-over-engineered-project-of-my-life-37m7</guid>
      <description>&lt;p&gt;At my previous company, BreezoMeter, we had a problem, but it wasn’t a software architecture one, a code plight, or a cultural issue.&lt;/p&gt;

&lt;p&gt;The problem was... [cue Scooby-Doo sound effect - DAM DAM DAM] - the bathroom.&lt;/p&gt;

&lt;h2&gt;
  
  
  THE PROBLEM
&lt;/h2&gt;

&lt;p&gt;Our offices were great back when we were 8 people, but over time we grew to over 40 employees in the same offices having to share only TWO BATHROOM STALLS.&lt;/p&gt;

&lt;p&gt;Now, I know what you're thinking, those spoiled engineers probably expect a personal stall each; but I assure you, that was not the case, and to make matters worse, there was no direct line of sight from our desks to the bathroom doors, so we couldn’t tell when they were vacant.&lt;/p&gt;

&lt;p&gt;We decided that we have to find a remedy to this situation, so that people may relieve themselves when they please or at least be notified whenever the bathroom is vacant.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Not every invention needs to change the world; sometimes, it’s enough to change one person’s world.&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;-- Shahar Polak&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  THE SOLUTION
&lt;/h2&gt;

&lt;p&gt;Every couple of months we used to have "Lab Days" - mini hackathons in which we could try new technologies, fiddle with ideas, and do so in teams that we did not have too much interaction with in our day-to-day.&lt;/p&gt;

&lt;p&gt;My fellow teammate, Simon, and I played with the idea of connecting those stalls to the internal network, as every stall should be.&lt;/p&gt;

&lt;p&gt;We knew that time was of the essence; we had two days to come up with a plan, buy all the equipment we needed, install the hardware, write the software, and make sure everything was working as expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  THE ARCHITECTURE
&lt;/h2&gt;

&lt;p&gt;After thinking it over, we decided to go with Google Firestore’s real-time database for the bathroom doors’ state.&lt;/p&gt;

&lt;p&gt;We created a Vue.js web application to present users with the doors' status and connected to Firestore.&lt;/p&gt;

&lt;p&gt;We added Cloud Functions so we could integrate it with Slack as well.&lt;/p&gt;

&lt;p&gt;And lastly, we created workers so users would be able to subscribe and get a notification whenever the status changed.&lt;/p&gt;

&lt;p&gt;Did I mention that we had two days to pull this off? Ambitious, I know.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fshahar-data%2Fblog%2Fover-engineering-post%2Fover%2520engineering%2520architecture.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fshahar-data%2Fblog%2Fover-engineering-post%2Fover%2520engineering%2520architecture.png" title="over-engineered project" alt="alt text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  THE PROCESS
&lt;/h2&gt;

&lt;p&gt;On the day of the Lab Day, we both arrived at the office at 7:00 after a weekend of excitement and anticipation.&lt;/p&gt;

&lt;p&gt;We decided to go with Raspberry Pi 3 with a microswitch as we thought it would be our best bet.&lt;/p&gt;

&lt;p&gt;The microswitch is an on/off clip that connects to the Raspberry Pi 3 and can send a boolean signal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fshahar-data%2Fblog%2Fover-engineering-post%2Fmicroswitch.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fshahar-data%2Fblog%2Fover-engineering-post%2Fmicroswitch.jpg" title="Raspberry Pi 3 with a microswitch" alt="alt text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Simon took the drill and started making holes so we could run the wires to the restroom. Not the most elegant way, but hey, it worked.&lt;/p&gt;

&lt;p&gt;We installed &lt;a href="https://www.raspberrypi.org/downloads/raspberry-pi-os/" rel="noopener noreferrer"&gt;Raspbian OS&lt;/a&gt; and Python 3 on the Raspberry Pi and then the coding began.&lt;/p&gt;

&lt;p&gt;We had to build a small program that would sample the microswitch every X seconds and, once the status changed, update the database.&lt;/p&gt;

&lt;p&gt;The first problem we encountered was false positives. The microswitch that we installed on the door was extremely sensitive, so every couple of seconds, even if someone only touched the door, it would send through a signal. We decided to change strategy and update our server only after 5 times that the microswitch sent the same signal.&lt;/p&gt;

&lt;p&gt;The Python program would check every second what the microswitch's status is; once it detects the same signal 5 times and the status is different from the current one, it would update the database's state.&lt;/p&gt;

&lt;p&gt;A small code sample, just to explain the Raspberry Pi part without too many tedious details:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_items&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;should_remove_first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;should_remove_first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;all_same&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_empty&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elem&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;elem&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_items&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_door_status&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# reads the data from the Raspberry pi micro-switch
&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_door_server_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;door_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# update firebase with the new status
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;door_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; 
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;door_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_door_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;door_status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;all_same&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all_same&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;all_same&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;door_status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="nf"&gt;update_door_server_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;door_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;door_status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;door_status&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we had code that updates the database, we had to create a web client, so users could access it.&lt;/p&gt;

&lt;p&gt;We decided to go with a lean application using &lt;a href="https://vuejs.org/" rel="noopener noreferrer"&gt;Vue.js&lt;/a&gt; hosted on &lt;a href="https://firebase.google.com/products/hosting" rel="noopener noreferrer"&gt;Firebase Hosting&lt;/a&gt; - why you ask? Well, it's free.&lt;/p&gt;

&lt;p&gt;We hustled our way into our designer’s heart and convinced him that designing the restroom's web pages is probably the best use of his time. The designs came out great!&lt;/p&gt;

&lt;h2&gt;
  
  
  THE FINAL RESULTS
&lt;/h2&gt;

&lt;p&gt;This is the final result: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fshahar-data%2Fblog%2Fover-engineering-post%2Floo-cloud%2520website%2520-%2520side%2520by%2520side.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fshahar-data%2Fblog%2Fover-engineering-post%2Floo-cloud%2520website%2520-%2520side%2520by%2520side.png" title="over-engineered project final results" alt="alt text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We even had a favicon that changed according to the status, so people could collapse the browser tab and see by the icon if the restrooms are available or not.&lt;/p&gt;

&lt;p&gt;We added a button to subscribe and get a notification the second that the restrooms are vacant.&lt;/p&gt;

&lt;h2&gt;
  
  
  CONCLUSION
&lt;/h2&gt;

&lt;p&gt;Not every application is meant to change the world. This is one of those applications that did one thing and did it well, and it was a heck of a lot of fun building it.&lt;/p&gt;

&lt;p&gt;So after building &lt;a href="https://loo.cloud/" rel="noopener noreferrer"&gt;loo.cloud&lt;/a&gt; (yes, we actually bought this domain, but we do not use it anymore), we pondered these questions.&lt;br&gt;
Was it perfect? Far from it!&lt;br&gt;
Was it over-engineered? Like 10 levels too much for the task at hand.&lt;br&gt;
Was it awesome? Sure as hell was.&lt;/p&gt;

&lt;p&gt;Building fun stuff is part of what makes being a software engineer so much fun.&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>vue</category>
      <category>firebase</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Keep Calm and Cache On</title>
      <dc:creator>Shahar Polak</dc:creator>
      <pubDate>Sat, 10 Oct 2020 13:05:28 +0000</pubDate>
      <link>https://forem.com/polakshahar/keep-calm-and-cache-on-48d2</link>
      <guid>https://forem.com/polakshahar/keep-calm-and-cache-on-48d2</guid>
      <description>&lt;p&gt;I had a great conversation with a friend about premature optimizations.&lt;br&gt;
One thing led to another, and we have started talking about caching and Memoization. &lt;/p&gt;

&lt;p&gt;Each of us had a very different perspective on the matter, but the one thing we both agreed upon is the importance of performance.&lt;/p&gt;

&lt;p&gt;He asked me if I could explain my thoughts in a layman's terms, and as Barney Stinson used to say, challenge accepted!&lt;/p&gt;

&lt;p&gt;So before we begin, let's talk about what Memoization is and why we even need it.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is Memoization?
&lt;/h2&gt;

&lt;p&gt;Memoization is an optimization technique used primarily to prevent the re-calculation of the save results for the same output.&lt;br&gt;
Basically, it means that our software will run faster.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why should we use Memoization?
&lt;/h2&gt;

&lt;p&gt;We should use Memoization for better performance and faster results.&lt;br&gt;
For example, if we use any client-side JavaScript code, we are less likely to choke the main thread and have laggy UI, and nobody likes that ¯\&lt;em&gt;(ツ)&lt;/em&gt;/¯.&lt;/p&gt;
&lt;h2&gt;
  
  
  ENOUGH TALKING! LET ME SEE THE CODE!
&lt;/h2&gt;

&lt;p&gt;You are right; I know that I would like to see some action before I keep reading.&lt;/p&gt;

&lt;p&gt;Let's say that we have a simple function "add"; add takes two numbers and return the value of the bough of them;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this function, we re-evaluate &lt;strong&gt;a+b&lt;/strong&gt; every single time it is called.&lt;br&gt;
This is not an "expensive" calculation. Therefore, we would unlikely to use Memoization for something like that, but we could do something like that if we would.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cachedAdd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;memoizer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;cachedAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 5 Not Cached&lt;/span&gt;
&lt;span class="nx"&gt;cachedAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 5 Cached&lt;/span&gt;
&lt;span class="nx"&gt;cachedAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 5 Cached&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all nice and well, but how the heck does "memoizer" works?&lt;/p&gt;

&lt;p&gt;Let's see if we can create a simple generic "memoizer" &lt;a href="https://eloquentjavascript.net/05_higher_order.html#h_xxCc98lOBK"&gt;high-order&lt;/a&gt; function that we can reuse.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Cache function results for given params
 *
 * @param {function} func
 * @returns {function(): (*)}
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;memoizer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;func&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are many ways to go about writing this function, but let's go over this implementation step by step.&lt;br&gt;
The "memoizer" takes a function, it uses the arguments object, and stringify it to create the key.&lt;br&gt;
Once it has the key, the function checks to see if the key is available in the cache object; if it does, it returns the cached result, and we are done.&lt;br&gt;
In case it does not, it will calculate the value, save it in the cache, and then return it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Memoization exchanges time for space.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I know what you think, "I am not convinced that it worth the hassle."&lt;/p&gt;
&lt;h2&gt;
  
  
  Show me the money
&lt;/h2&gt;

&lt;p&gt;Let's see some runtime results.&lt;br&gt;
To see the following, I'll use the notorious Fibonacci Sequence function.&lt;/p&gt;

&lt;p&gt;The Fibonacci Sequence is the series of numbers:&lt;br&gt;
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...&lt;br&gt;
The next number is found by adding up the two numbers before it;&lt;/p&gt;

&lt;p&gt;We could implement such a function like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fibonacci&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fibonacci&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;fibonacci&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getFibonacci&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
   &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fibonacci&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can call the function like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;getFibonacci&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// will result [ 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 ...]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets will run a benchmark test when the limit is 30:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fibonacci&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;getCachedFibonacci&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeEnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fibonacci&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first time we run it, it will result in 193.097ms;&lt;br&gt;
The problem is that in case we will run this code 100 times, it will not get any better and might just get worst.&lt;br&gt;
For example, this code ran 100 times in a total of 18357.116ms, which is shit tones.&lt;/p&gt;

&lt;p&gt;Let's see if we could do better?&lt;br&gt;
We will use the Memoization function that we wrote earlier to create a new cached Fibonacci function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cachedFibonacci&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;memoizer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fibonacci&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getCachedFibonacci&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cachedFibonacci&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cachedFibonacci&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;getCachedFibonacci&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeEnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cachedFibonacci&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time around, we will get other results.&lt;br&gt;
The first time we run it, it will result like before, and take around 193.509ms  to resolve, but from the second time and beyond, the function resulted in an average of 0.027ms;&lt;br&gt;
To a total of  199.988ms for the 100 iterations.&lt;/p&gt;

&lt;h3&gt;
  
  
  👑 &lt;strong&gt;That results is 7,000~ times faster for each iteration.&lt;/strong&gt;
&lt;/h3&gt;




&lt;p&gt;Now, I know what you are thinking; not every problem is a Fibonacci one;&lt;br&gt;
I can not stress it enough, Memoization is not a silver bullet, and it is not suitable for every scenario.&lt;br&gt;
On the other hand, it is another powerful tool that can help your application performance when using correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I create my own Memoization function?
&lt;/h3&gt;

&lt;p&gt;Of course, you can do it, but in case you wish to use one of the open-source, well tested, well-documented Memoization function, here is a short list:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/memoizee"&gt;memoizee&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/fast-memoize"&gt;memoized&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/lodash.memoize"&gt;lodash.memoize&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you have any questions or thoughts on the matter, I would love to hear them, and in the meantime, Keep Calm 👑 Cache On.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>optimisation</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Full Stack - Where to Start</title>
      <dc:creator>Shahar Polak</dc:creator>
      <pubDate>Mon, 14 Sep 2020 16:49:57 +0000</pubDate>
      <link>https://forem.com/polakshahar/where-to-start-5amb</link>
      <guid>https://forem.com/polakshahar/where-to-start-5amb</guid>
      <description>&lt;p&gt;Last week I got a phone call from a friend of a friend. They told me that they’re in their final semester of CS, and want to become a full-stack engineer.&lt;br&gt;
I thought to myself, well, all you need now is years of experience in backend development, front-end, DBA, DevOps, logs, and so on (you get the point).&lt;/p&gt;

&lt;p&gt;I didn’t want to discourage him, and if there’s one thing that I value, it’s enthusiasm and willingness to learn. So I told him to start where I tell all my students to start - at the beginning.&lt;br&gt;
Fundamentals are the most valuable part at this stage - acquiring good habits, understanding principles, and using them. I also told him that some of my bad habits took me months and even years to unlearn (while some are still with me to this day).&lt;/p&gt;

&lt;p&gt;So, he asked me, "Where do I start?"&lt;/p&gt;

&lt;p&gt;You start with the fundamentals. If you want to become a web developer, you should learn HTML, CSS, and Javascript before diving deep into any framework.&lt;/p&gt;

&lt;p&gt;This list was meant for him, but it will be a shame not to share it here as well.&lt;/p&gt;

&lt;h1&gt;
  
  
  So, where to start?
&lt;/h1&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://javascript30.com/"&gt;Wes Bos, Javascript 30 (free)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;An incredible 30 exercise challenge, with explanations of how Wes Bos goes on about each of them.&lt;br&gt;
This one is also highly recommended for veteran developers who wish to sharpen their Vanilla JavaScript skills.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://teamtreehouse.com/tracks/front-end-web-development"&gt;Team Treehouse online school ($14.99/m)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;One of the best online video schools. Funny, with great teachers.&lt;/p&gt;

&lt;p&gt;What I love the most about Team Treehouse is the "tracks." They really help you hit the ground running by creating a track that lets you know what to study next.&lt;/p&gt;

&lt;p&gt;Personally, when I have too many options to choose from, I sometimes get lost trying to study everything.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://frontendmasters.com/"&gt;Frontend Masters online school ($39/m)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;An outstanding online school that, like Team Treehouse’s "tracks," has "learning paths."&lt;/p&gt;

&lt;p&gt;They have managed to assemble some of the most well-known names in the front-end industry to deliver their courses, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kyle Simpson, the author of “You Don't Know JavaScript.”&lt;/li&gt;
&lt;li&gt;Steve Kinney from Twillo.&lt;/li&gt;
&lt;li&gt;Sarah Drasner from Netlify/Microsoft, and many more.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.pluralsight.com/courses/front-end-web-development-get-started"&gt;Pluralsight online school ($29)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;One school that had to make it to this list.&lt;br&gt;
Pluralsight is an online video school with thousands of courses in many languages and technologies.&lt;br&gt;
With that said, I do not know if this will be the first place to learn the basics; I think that Pluralsight is better a little bit down the road. &lt;/p&gt;

&lt;h2&gt;
  
  
  📚 Books
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/getify/You-Dont-Know-JS"&gt;You don't know JavaScript by Kyle Simpson - online books(free)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;An online series that deep dives into JavaScript, and is highly recommended for learning concepts and truly understanding JavaScript fundamentals.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.learninpublic.org/"&gt;The coding carrier handbook - ebook(39$)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;AKA The Missing Manual for Early Career Devs: Guides, Principles, Strategies, and Tactics. &lt;br&gt;
This book is a must to any developer, and I often heard developers saying it is a book that they wish they read years ago. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.bookdepository.com/Pragmatic-Programmer-David-Thomas/9780135957059"&gt;The Pragmatic Programmer (2020 Edition) By David Thomas &amp;amp; Andrew Hunt - audible($15), paperback($44)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This book has literally changed my life as a programmer. &lt;br&gt;
It gave me insights and tough me not to work on autopilot. &lt;/p&gt;

&lt;h2&gt;
  
  
  📺 YouTube Channels
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.youtube.com/channel/UClb90NQQcskPUGDIXsQEz5Q"&gt;Dev Ed&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;A funny, easy-going, and colorful YouTuber that does live coding tutorials.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.youtube.com/kepowob"&gt;Kevin Powell&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Mostly talks about CSS and design but managed to explain it readily and helpt me to get some context in my mind.  &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.youtube.com/c/DesignCourse/about"&gt;Gary Simon&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;An excellent resource for front-end development and UX-UI. Gary is a fantastic teacher with paid courses on Udamy and Pluralsight. &lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Steve Jobs put it best - "Stay hungry. Stay foolish."&lt;/p&gt;

&lt;p&gt;Hope this helps, and please let me know what you think or what should be added to this list.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Full disclosure - I am not associated or affiliated with any of the schools above and do not get anything other than the satisfaction of knowing I’ve managed to help even a single person.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
